實戰:實時數倉雙流join

業務場景

我的實時訂單流想要關聯另一個流的維度數據,我們該如何設計。

•A流:訂單流,從binlog中獲取下單數據,缺點是,表內維度數據缺少經緯度信息(舉例)。•B流:用戶流量信息,保存用戶的經緯度信息(舉例)

以上場景,是我們在實時數據倉庫開發中,會經常遇到實時關聯維度數據的情況,我們該如何優雅的開發?

通過唯一key做實時流關聯,然後通過union把兩個流做合併,通過flink的state做維度補全。

注意事項

•state保存多久,類型設計,上游異常如何回溯歷史數據?•什麼條件下滿足數據輸出?,如何解決數據先到和後到問題?•爲什麼用state做,而不是把維度數據落到redis內存kv中?優點是什麼?缺點是什麼?•這種設計適合的業務場景是什麼?搞清楚。注意⚠️咱們設計的輸出觸發條件是:“當設備id補全維度數據後再輸出結果”。

簡化代碼

    第一步:兩個流處理完之後,並針對每個流添加tag("order_stream","lat_lon_stream")標籤,方便兩個流進行  union之後去識別哪個是訂單流,其中key是deviceid,value是對應的json

val text =order_stream.union(lat_lon_stream).keyBy(0).process(new MyKeyedProcessFunction())

    第二步:在MyKeyedProcessFunction實現自己的業務邏輯

class MyKeyedProcessFunction extends KeyedProcessFunction[Tuple, (String, JSONObject), JSONObject]{
  //我設計的state類型是mapstate
  private  var state: MapState[String,String] = _
  private  var sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
  override def open(parameters: Configuration): Unit ={
    super.open(parameters)
    //我設置的保存時間是1天,大家可以根據業務場景自己設定。
    val config = StateTtlConfig.newBuilder(org.apache.flink.api.common.time.Time.days(1))
      .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
      .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
      .build()
    val mapStateDescriptor =new MapStateDescriptor[String,String]("OrderState", classOf[String], classOf[String])
    mapStateDescriptor.enableTimeToLive(config)
    state = getRuntimeContext.getMapState(mapStateDescriptor)
  }
  override def processElement(message: (String, JSONObject),
                              ctx: KeyedProcessFunction[Tuple, (String, JSONObject), JSONObject]#Context,
                              out: Collector[JSONObject]) = {
      if(message._2.get("tag").toString =="lat_lon_stream"){
       //標識我實時經緯度接收的日期(我設計的比較簡單,你們可以細化到小時)
        state.put("dau",message._2.get("dt").toString)
        state.put("lat_lon",message._2.get("lat_lon").toString)
        //如何日期相同,就輸出
        if(message._2.get("dt").toString ==state.get("order")){
          val jsonObject = JSON.parseObject(state.get("orderInfo"))
          jsonObject.put("lat_lon",message._2.get("lat_lon").toString)
          out.collect(jsonObject)
        }
      }else{
      //如果tag不是經緯度,那就說明是訂單流,判斷訂單流的日期,和flink中state
      //存儲的經緯度日期是否相同,如果相同,說明經緯度流已經到達了
      //滿足輸出條件了。(我設計的比較簡單,你們可以細化到小時)
        if (state.get("lat_lon") == message._2.get("dt").toString){
          val jsonObject = message._2
          jsonObject.put("lat_lon",state.get("lat_lon"))
          out.collect(jsonObject)
        }else{
        //標識我實時訂單接收的日期(我設計的比較簡單,你們可以細化到小時)
          state.put("order",message._2.get("dt").toString)
          state.put("orderInfo",message._2.toString)
        }
      }
  }
}

關注小晨說數據,獲取更多大廠技術乾貨分享

回覆“spark”,“flink”,“機器學習”,“前端”,“中臺”,“架構”獲取海量學習資料~~~

你也「在看」嗎

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