Spark Streaming kafka實現數據零丟失的幾種方式

 在使用Spark streaming消費kafka數據時,程序異常中斷的情況下發現會有數據丟失的風險,本文簡單介紹如何解決這些問題。

  在問題開始之前先解釋下流處理中的幾種可靠性語義:

  1、At most once - 每條數據最多被處理一次(0次或1次),這種語義下會出現數據丟失的問題;
  2、At least once - 每條數據最少被處理一次 (1次或更多),這個不會出現數據丟失,但是會出現數據重複;

  3、Exactly once - 每條數據只會被處理一次,沒有數據會丟失,並且沒有數據會被多次處理,這種語義是大家最想要的,但是也是最難實現的。


Kafka高級API

  如果不做容錯,將會帶來數據丟失,因爲Receiver一直在接收數據,在其沒有處理的時候(已通知zk數據接收到),Executor突然掛掉(或是driver掛掉通知executor關閉),緩存在內存中的數據就會丟失。因爲這個問題,Spark1.2開始加入了WAL(Write ahead log)開啓 WAL,將receiver獲取數據的存儲級別修改爲StorageLevel.MEMORY_AND_DISK_SER,使用代碼片段如下:

val conf = new SparkConf()  
conf.set("spark.streaming.receiver.writeAheadLog.enable","true")  
val sc= new SparkContext(conf)  
val ssc = new StreamingContext(sc,Seconds(5))  
ssc.checkpoint("checkpoint")  
val lines = <span class="wp_keywordlink_affiliate"><a data-original-title="View all posts in Kafka" href="https://www.iteblog.com/archives/tag/kafka" title="" target="_blank">Kafka</a></span>Utils.createStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topicMap, StorageLevel.MEMORY_AND_DISK_SER).map(_._2)


但是開啓WAL後,依舊存在數據丟失問題,即使按官方說的設置了WAL,依舊會有數據丟失,這是爲什麼?因爲在任務中斷時receiver也被強行終止了,將會造成數據丟失,提示如下:



 

ERROR ReceiverTracker: Deregistered receiver for stream 0: Stopped by driver  
WARN BlockGenerator: Cannot stop BlockGenerator as its not in the Active state [state = StoppedAll]  
WARN BatchedWriteAheadLog: BatchedWriteAheadLog Writer queue interrupted.


在Streaming程序的最後添加代碼,只有在確認所有receiver都關閉的情況下才終止程序。我們可以調用StreamingContext的stop方法,其原型如下:


  1. def stop(stopSparkContext: Boolean, stopGracefully: Boolean): Unit



可以如下使用:

  1. sys.addShutdownHook({  
      ssc.stop(true,true)  
    )})


WAL帶來的問題
WAL實現的是At-least-once語義。如果在寫入到外部存儲的數據還沒有將offset更新到zookeeper就掛掉,這些數據將會被反覆消費。同時,因爲需要把數據寫入到可靠的外部系統,這會犧牲系統的整個吞吐量。


Kafka Direct API

  Kafka direct API 的運行方式,將不再使用receiver來讀取數據,也不用使用WAL機制。同時保證了exactly-once語義,不會在WAL中消費重複數據。不過需要自己完成將offset寫入zk的過程。調用方式可以參見下面:


  1. messages.foreachRDD(rdd=>{  
       val message = rdd.map(_._2)    
       //對數據進行一些操作  
       message.map(method)  
       //更新zk上的offset (自己實現)  
       updateZKOffsets(rdd)  
    })
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章