Kafka部分Partition中無數據導致Window不觸發問題

    業務上需要將同一個時間段的兩種數據收集到一起做一些計算,這兩種數據分別存在於Kafka的兩個Topic中。計算邏輯是這樣的:

    使用兩個DataStream分別消費兩個Topic中的數據,對兩條流先分別設置WaterMark,然後union,接着進行keyBy操作,最後使用Window將同一個時間窗口中的兩種數據匯聚在一起進行計算。但是發現程序無論是在本地運行還是在yarn-cluster模式下運行,只要並行度不爲1,程序都不能正常執行。明明顯示已經收到了數據,但是Window就是不觸發:

 

Window何時觸發:

    Window是通過Trigger來觸發的,時間使用EventTime時默認使用EventTimeTrigger:

    每條數據進來的時候都會通過WindowOperator中的processElement()方法走到onElement()方法中。如果當前WaterMark大於窗口結束時間,那麼就會立即觸發窗口計算,否則會使用窗口結束時間註冊一個觸發器(Timer),用於觸發Window的計算(底層是Set結構,所以觸發器會覆蓋從而不會內存溢出):

 

WaterMark何時更新:

    在對流使用assignTimestampsAndWatermarks之後,會對流中的元素調用用戶定義的方法,然後生成WaterMark:

再將WaterMark broadcast出去:

broadcast出去的目的是爲了下游再檢查WaterMark的時候,以最小的那個WaterMark作爲總體的WaterMark:

這裏可以看到,最小的watermark居然是個負值(在調試了好一會之後),也就是說至少有一個任務WaterMark一直沒有更新,也就是說並行度爲3的task裏面有task沒有接受到數據

 

Flink是如何消費Kafka數據的:

   

    Flink source用到了FlinkKafkaConsumer010,沒有指定KafkaPartitioner的話,會通過FixedPartitioner來給出默認的partitioner方法,而默認的Flink partition的規則,就是Flink的並行度ID除以kafka partition length取餘。程序的並行度爲3,Kafka的Partition數量也爲3,即每個Task消費一個Partition中的數據:

public int partition(T record, byte[] key, byte[] value, String targetTopic, int[] partitions) {
        Preconditions.checkArgument(partitions != null && partitions.length > 0, "Partitions of the target topic is empty.");
        return partitions[this.parallelInstanceId % partitions.length];
    }

然後使用命令檢查發現,有兩個Partition是沒有數據的,所以導致兩個Task的WaterMark一直更新不了:

 

解決方法:

    將WaterMark的並行度與Source的並行度設置不一致,使得數據進行Shuffle,從而使得Task都可以更新WaterMark,最終結果如下:

 

 

參考:

    https://www.jianshu.com/p/753e8cf803bb(問題定位:Flink水位線不觸發問題)

    https://www.cnblogs.com/ljygz/p/11392952.html

    https://www.jianshu.com/p/c8c789ff5570(EventTimeTrigger解析)

 

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