天天看點

Kafka 消息 Deliver guarantee

       Kafka 消息有以下三種delivery guarantee:

  • At most once 消息可能會丢,但絕不會重複傳輸
  • At least one 消息絕不會丢,但可能會重複傳輸
  • Exactly once 每條消息肯定會被傳輸一次且僅傳輸一次,很多時候這是使用者所想要的。

      Producer到Broker的delivery guarantee semantic非常直接。當producer向broker發送消息時,一旦這條消息被commit,由于replication的存在,它就不會丢。但是如果producer發送資料給broker後,遇到的網絡問題而造成通信中斷,那producer就無法判斷該條消息是否已經commit。這一點有點像向一個自動生成primary key的資料庫表中插入資料。雖然Kafka無法确定網絡故障期間發生了什麼,但是producer可以生成一種類似于primary key的東西,發生故障時幂等性的retry多次,這樣就做到了Exactly one。是以目前預設情況下一條消息從producer和broker是確定了At least once,但可通過設定producer異步發送實作At most once。

      那麼消息從broker到consumer的delivery guarantee semantic。(僅針對Kafka consumer high level API)。consumer在從broker讀取消息後,可以選擇commit,該操作會在Zookeeper中存下該consumer在該partition下讀取的消息的offset。該consumer下一次再讀該partition時會從下一條開始讀取。如未commit,下一次讀取的開始位置會跟上一次commit之後的開始位置相同。當然可以将consumer設定為autocommit,即consumer一旦讀到資料立即自動commit。如果隻讨論這一讀取消息的過程,那Kafka是確定了Exactly once。但實際上實際使用中consumer并非讀取完資料就結束了,而是要進行進一步處理,而資料處理與commit的順序在很大程度上決定了消息從broker和consumer的delivery guarantee semantic。

      讀完消息先commit再處理消息。這種模式下,如果consumer在commit後還沒來得及處理消息就crash了,下次重新開始工作後就無法讀到剛剛已送出而未處理的消息,這就對應于At most once。

      讀完消息先處理再commit。這種模式下,如果處理完了消息在commit之前consumer crash了,下次重新開始工作時還會處理剛剛未commit的消息,實際上該消息已經被處理過了。這就對應于At least once。

      在很多使用場景下,消息都有一個primary key,是以消息的處理往往具有幂等性,即多次處理這一條消息跟隻處理一次是等效的,那就可以認為是Exactly once。

      如果一定要做到Exactly once,就需要協調offset和實際操作的輸出。精典的做法是引入兩階段送出。如果能讓offset和操作輸入存在同一個地方,會更簡潔和通用。這種方式可能更好,因為許多輸出系統可能不支援兩階段送出。比如,consumer拿到資料後可能把資料放到HDFS,如果把最新的offset和資料本身一起寫到HDFS,那就可以保證資料的輸出和offset的更新要麼都完成,要麼都不完成,間接實作Exactly once。(目前就high level API而言,offset是存于Zookeeper中的,無法存于HDFS,而low level API的offset是由自己去維護的,可以将之存于HDFS中)

  總之,Kafka預設保證At least once,并且允許通過設定producer異步送出來實作At most once。而Exactly once要求與目标存儲系統協作,幸運的是Kafka提供的offset可以使用這種方式非常直接非常容易。