引言

今天呢,還是來回答一個群友的問題。如下圖所示

好吧,這個馬賽克可能打的不走心,應該沒人認出來吧~

ps:這張圖其實暴露了我上班在看微信。我要澄清一下,我那是帶薪上廁所,大家不要羨慕。

OK,回到我們的正題。 所謂消費語義,指的就是如下三種情況

  • 如何保證消息最多消費一次
  • 如何保證消息至少消費一次
  • 如何保證消息恰好消費一次

其實類似還有一個投遞語義

  • 如何保證消息最多投遞一次
  • 如何保證消息至少投遞一次
  • 如何保證消息恰好投遞一次

說句實在話,其實還是老問題,只是換了一種問法!

OK,開始我們的正文

正文

我們先做如下約定

  • Producer代表生產者
  • Consumer代表消費者
  • Message Queue代表消息隊列

投遞語義

我們先從投遞語義開始講起,因為要先把這個概念講明白了,才能講消費語義。恰巧,kafka實現了這三種語義,我們以kafka來說明。

如何保證消息最多投遞一次?

簡單,就是我已經投出去了,收沒收到不管了,會存在消息丟失。 我們在初始化Producer時可以通過配置request.required.acks不同的值,來實現不同的發送模式。

這裡將request.required.acks設為0,意思就是Producer不等待Leader確認,只管發出即可;最可能丟失消息。如果丟了消息,就是投遞0次。如果沒丟,就是投遞1次。符合最多投遞一次的含義。

如何保證消息至少投遞一次?

這裡將request.required.acks設為-1。ProducerkafkaLeader(主)節點發送消息後,會等follower(從)節點同步完數據以後,再給Producer返回ACK確認消息。 但是這裡是有幾率出現重複消費的問題的。

例如,kafka保存消息後,發送ACK前宕機,Producer認為消息未發送成功並重試,造成數據重複! 那麼,在這種情況下,就會出現大於1次的投遞情況,符合至少投遞一次的含義。

如何保證消息恰好投遞一次?

kafka在0.11.0.0版本之後支持恰好投遞一次的語義。 我們將enable.idempotence設置為ture,此時就會默認把request.required.acks設為-1,可以達到恰好投遞一次的語義。

如何做到的?

為了實現Producer的冪等語義,Kafka引入了Producer ID(即PID)和Sequence Number。

kafka為每個Producer分配一個pid,作為該Producer的唯一標識。

Producer會為每一個消息維護一個單調遞增的seq。 類似的,Message Queue也會為每個消息記錄下最新的seq。 當req_seq == message_seq+1時,Message Queue才會接受該消息。因為:

  • (1)消息的seq比Message Queue的seq大一以上,說明中間有數據還沒寫入,即亂序了
  • (2)消息的seq比Message Queue的seq小,那麼說明該消息已被保存。

消費語義

這裡我們還是做一個定義如下所示

  • consumer.poll()表示消費者獲取消息內容
  • processMsg(message)表示下游系統進行消費消息
  • consumer.commit()表示消費者往消息隊列提交確認信息,消息隊列接到確認消息,刪除該消息。

注意了,我是以processMsg函數,即處理消息的過程,定義為消費消息。

如何保證消息最多消費一次?

Producer:滿足最多投遞一次的語義即可,即只管發消息,不需要等待消息隊列返回確認消息。

Message Queue:接到消息後往內存中一放就行,不用持久化存儲。

Consumer:拉取到消息以後,直接給消息隊列返回確認消息即可。至於後續消費消息成功與否,無所謂的。即按照以下順序執行

consumer.poll();
consumer.commit();
processMsg(message);

如何保證消息至少消費一次?

Producer:滿足至少投遞一次語義即可,即發送消息後,需要等待消息隊列返回確認消息。如果超時沒收到確認消息,則重發。

Message Queue:接到消息後,進行持久化存儲,而後返回生產者確認消息。

Consumer:拉取到消息後,進行消費,消費成功後,再返回確認消息。即按照如下順序執行

consumer.poll();
processMsg(message);
consumer.commit();

由於這裡Producer滿足的是至少投遞一次語義,因此消息隊列中是有重複消息的。所以我們的Consumer會出現重複消費的情形!

如何保證消息恰好消費一次?

在保證至少消費一次的基礎上,processMsg滿足冪等性操作即可。

如何保證冪等性操作?

老問題了,比如有狀態的消息啊。比如唯一表啊。大家搜一搜,一大堆答案,不想重複說了。

總結

本文講的是消息隊列的消費語義和投遞語義的含義,希望大家有所收穫。 最後,我就是帶薪上廁所了,羨慕不!


推薦閱讀:
相关文章