spark 讀取 Oracle 以及 kafka 數據 ( GeoPoint 座標數據類型 ) 做Join 並插入ES 【原創】

一、kafka 模擬數據:

【1】模擬數據實體類:

public class CarDataTest {
    private String lat;
    private String lon;
    private String location;
    private String status;
    private String terminaltype;

    -------有很多字段 此處省略不寫了 下面 get set 也省略不寫了------ 

    public String getRecordvelocity() {
        return recordvelocity;
    }

    public void setRecordvelocity(String recordvelocity) {
        this.recordvelocity = recordvelocity;
    }

    public String getDepid() {
        return depid;
    }

    public void setDepid(String depid) {
        this.depid = depid;
    }

}

【2】kafka 生成模擬數據

public class kafkaTest {
    private static String carrDatas(){
        CarDataTest carDataTest = new CarDataTest();
        String randomNum_10 = String.valueOf(new Random().nextInt(9));
        String randomNum_100 = String.valueOf(new Random().nextInt(100));
        String randomNum_1 = String.valueOf(new Random().nextInt(2));
        long end = System.currentTimeMillis();
        Date date = new Date(end);
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = df.format(date);
        carDataTest.setLat("23.111111");
        carDataTest.setLon("113.112233");
        carDataTest.setLocation("23.111111,113.22222");
         
        --------------省略部分-----------

        carDataTest.setSvrtime(dateStr);
        carDataTest.setTerminaltype(randomNum_1);
        JSONObject object = JSONObject.fromObject(carDataTest);
        String json = object.toString();
        System.out.println(json);
        return json;
    }

    public static void main (String args[]) throws Exception{
/**
kafka 序列化 相關類指定
*/
        String serializerClassName = "org.apache.kafka.common.serialization.StringSerializer";
/**
kafka 的 bootsServers 沒啥好說的
*/
        String kafkaBootsrapServers ="machen1:9092,machen2:9092,machen3:9092";
/**
構建 kafka 相關參數
*/
        Properties properties = new Properties();
        properties.put("bootstrap.servers",kafkaBootsrapServers);
        properties.put("acks","all");
        properties.put("retries",0);
        properties.put("batch.size",16384);
        properties.put("linger.ms",1);
        properties.put("buffer.memory",33554432);
        properties.put("key.serializer" ,serializerClassName);
        properties.put("value.serializer",serializerClassName);
        KafkaProducer<String,String> kafkaProducer = new KafkaProducer<String, String>(properties);
/**
指定topic
*/
        String topic = "police_data_test";
        boolean flag = true;
        while( flag){
            for(int i=0;i<5;i++){
                kafkaProducer.send(
                        new ProducerRecord(
                                topic,
                                carrDatas()
                        ));
            }
            kafkaProducer.flush();
            System.out.println(" machen kafka 生成數據完畢");
            Thread.sleep(10000);
        }
        kafkaProducer.close();
    }
}

二、spark 讀取【oracle  和  kafka】數據 並插入 ES

【1】ES 索引創建

首先解釋一下 type 的類型,date ,keyword,folat 沒什麼好說的 

geo_point 是 Geopoint 的類型  是ES 用於空間查詢的一種類型

一定要注意、一定要注意、一定要注意、

geopoint 的 格式 爲    “lat,lon”  分別代表  維度和經度   比如說  “23.33445,113.223214”   第一個數字的小數點前面是 兩位數,第二個數字的小數點前面是3位數 ,否則是插不進去ES的 ,而且會一直報  非常詭異的錯誤  :

(我之前一直插不進去就是不小心 寫成  “113.22222,23.22222” 這種錯誤形式了)

反正日誌中你看不到任何關於   geopoint 的格式錯誤的提示 

他的值是 有 "lat" 和 "lon" 組合而成,也就是經緯度的表達

PUT test_police_car
{    
    "mapping":{
        "doc":{
            "properties":{

                "lat":{
                    "type":"float"
                },
                "lon":{
                    "type":"float"
                },
                "location":{
                    "type":"geo_point"
                },
                "rectime":{
                    "type":"date"
                },
                "address":{
                    "type":"keyword"
                },
                "name":{
                    "type":"machen"
                }
            }
        }
    }
}

【2】索引表結構展示: 

關閉 表限制:

 

 

【3】插入數據

object FromOrcaleToEs {
/**
* spark 的配置 包括鏈接ES 的一些參數設定 
* 主要注意點: 設置序列化的類 spark.serializer 
*/
  val sparkConf = new SparkConf().setAppName("machen_work_test").setMaster("local[2]")
  sparkConf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
  sparkConf.set("cluster.name","es")
//  原創鏈接---
  sparkConf.set("es.index.auto.create","true")
  sparkConf.set("es.nodes","machen1,machen2,machen3")
  sparkConf.set("es.port","9200")
  sparkConf.set("es.index.read.missing.as.empty","true")
  sparkConf.set("es.nodes.wan.only","true")


/**
*  Oracle 參數設定
*/
  val property  = new Properties()
  property.put("user","XXXX")
  property.put("password", "yyyy")
  property.put("driver", "oracle.jdbc.OracleDriver")
  val oracle_url = "jdbc:oracle:thin:@172.0.0.0:1521/datat_machen"

  val schema = StructType(Seq(
    StructField("lat",StringType,true),
    StructField("lon",StringType,true),
    StructField("location",StringType,true),

    -------- 省略部分 ---------

    StructField("recordvelocity",StringType,true),
    StructField("depid",StringType,true)
  ))

  //kafka
  def getKafkaData(topic: String, broker: String, scc: StreamingContext) = {
    val topicMap: Set[String] = List(topic).toSet
    val kafkaParam: Map[String, Object] = getKafkaParam(broker)
    val data = KafkaUtils.createDirectStream[String, String](scc,
      PreferConsistent,
      Subscribe[String, String](topicMap, kafkaParam)
    )
    data
  }
  def getKafkaParam(broker: String) = {
    val kafkaParam = Map[String, Object](
      "bootstrap.servers" -> broker,
      "group.id" -> "console-consumer-oracl-001",
      "key.deserializer" -> classOf[StringDeserializer],
      "value.deserializer" -> classOf[StringDeserializer],
      "enable.auto.commit" -> (false: java.lang.Boolean),
      "auto.offset.reset" -> "latest",// latest , earliest
      //      "security.protocol" -> "SASL_PLAINTEXT",
      //      "sasl.mechanism" -> "PLAIN",
      "max.poll.records"->"5"
    )
    kafkaParam
  }
   
/**
* ES 中的時間格式 都知道 是時區時間  也就是說表面上要比真實時間少 8個小時 
* 因此要把 準備插入的時間數據(String 類型)(比如說 2019-10-11 18:09:09)先變成ES 時間再插入
* 即 減去8小時 ,再轉換成時區格式 (2019-10-11T10:09:09.000Z)。
* 注意點:這是對於數據是 String 類型的,如果時間是Date 類型 直接插入(比如果用ES 自己的API 
* 或是kibana 方式的話 是沒有必要轉化的 直接插入就行)  
*/
  def dateForES (initdate:String) ={
    val initdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    val time = initdf.parse(initdate).getTime-8*60*60*1000
    val estimedf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
    val newtime = estimedf.format(new Date(time))
    newtime
  }

  def main(args: Array[String]): Unit = {

    val conf = sparkConf
    val sc = new SparkContext(conf)
    val spark = SparkSession
      .builder
      .master("local[2]")
      .appName("From_oracle")
      .config(sparkConf)
      .getOrCreate()
    val ssc = new StreamingContext(sc,Seconds(10))


    //oracle 數據  轉成 DF
    val jdbcdata = spark.read.jdbc(oracle_url,"my_oracle.test_data",property)
    jdbcdata.registerTempTable("orc_data")
    val oracle_data:DataFrame = spark.sql("select simno,plateno from orc_data limit 2")
    println("測試 oracle 數據:"+oracle_data.show())

    //kafka  數據  轉成 DF
    val topic = "police_data_test"
    val broker = "machen1:9092,machen2:9092,machen3:9092"
    val lines = getKafkaData(topic,broker,ssc)
    lines.foreachRDD(rdd => {
      val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
      lines.asInstanceOf[CanCommitOffsets].commitAsync(offsetRanges)
    })


    lines.foreachRDD{ rdd =>
      val data:RDD[Row] = rdd.map { x =>
        val buffer = ArrayBuffer.empty[Any]
        val jsons = JSONObject.fromObject(x.value())
        println(jsons)
        buffer.append(jsons.getString("lat"))
        buffer.append(jsons.getString("lon"))
        buffer.append(jsons.getString("location"))
    
        --------省略部分---------------

        buffer.append(dateForES(jsons.getString("rectime")))
        buffer.append(jsons.getString("depid"))
        println("轉換完畢---------")
        Row.fromSeq(buffer)
      }
      val df :DataFrame = spark.createDataFrame(data,schema)
      df.registerTempTable("kafka_data")
      val joindata = spark.sql("select k.*,o.plateno from kafka_data as k join orc_data as o on k.simid=o.simid ")
      val jsonArray = new JSONArray()
      val rddData = joindata.toJSON.rdd
      println("準備插入--------------------------------------------------")
      EsSpark.saveJsonToEs(rddData,"test_police_car/doc")
      println("插入成功--------------------------------------------------")
      joindata.show()
    }

/**  第二種方式:
 *   上面是先行 forechRDD 然後再map 操作 ,在map操作時候,已經是rdd 的類型了
 *   所以只能用 EsSpark.saveJsonToEs
 *   如果 先用 map 那麼返回的還是 Dstream[] 再用下面即可 x.value() 是json 格式
 *  val json = lines.map(x=>{
 *     val jsons = JSONObject.fromObject(x.value())
 *     jsons
 *   })
 *  EsSparkStreaming.saveJsonToEs(json,"index")
 */
    ssc.start()
    ssc.awaitTermination()
    ssc.stop()
  }
}

【4】運行 相關的日誌輸出:

kafka  數據

 

測試類日誌:【相關真實數據不方便暴漏,請理解】

 

join 之後的 df  【太長了,只展示後面字段】

ES 數據再次查詢:

能看到join 之後,並且插入的數據  (數據不方便暴漏 請理解) 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章