引言
所謂的消費語義,指的就是如下三種情況
- 如何保證消息最多消費一次
- 如何保證消息至少消費一次
- 如何保證消息恰好消費一次
其實類似還有一個投遞語義
- 如何保證消息最多投遞一次
- 如何保證消息至少投遞一次
- 如何保證消息恰好投遞一次
說句實在話,其實還是老問題,只是換了一種問法!
OK,開始我們的正文
正文
我們先做如下約定
Producer
代表生產者Consumer
代表消費者Message Queue
代表消息隊列
投遞語義
我們先從投遞語義開始講起,因為要先把這個概念講明白了,才能講消費語義。恰巧,kafka
實現了這三種語義,我們以kafka
來說明。
如何保證消息最多投遞一次?
簡單,就是我已經投出去了,收沒收到不管了,會存在消息丟失。
我們在初始化Producer
時可以通過配置request.required.acks
不同的值,來實現不同的發送模式。
這里將request.required.acks
設為0,意思就是Producer
不等待Leader確認,只管發出即可;最可能丟失消息。如果丟了消息,就是投遞0次。如果沒丟,就是投遞1次。符合最多投遞一次的含義。
如何保證消息至少投遞一次?
這里將request.required.acks
設為-1。Producer
往kafka
的Leader(主)
節點發送消息后,會等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
會為每一個<topic,partition>維護一個單調遞增的seq。
類似的,Message Queue
也會為每個<pid,topic,partition>記錄下最新的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
滿足冪等性操作即可。
如何保證冪等性操作?
老問題了,比如有狀態的消息啊。比如唯一表啊。大家搜一搜,一大堆答案,不想重復說了。
總結
本文講的是消息隊列的消費語義和投遞語義的含義,希望大家有所收獲。