所謂的消費語義,指的就是以下三種狀況ide
其實相似還有一個投遞語義函數
說句實在話,其實仍是老問題,只是換了一種問法!
OK,開始咱們的正文ui
咱們先作以下約定code
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
如何保證消息剛好投遞一次?
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
纔會接受該消息。由於:同步
Message Queue
的seq大一以上,說明中間有數據還沒寫入,即亂序了。Message Queue
的seq小,那麼說明該消息已被保存。這裏咱們仍是作一個定義以下所示消息隊列
consumer.poll()
表示消費者獲取消息內容processMsg(message)
表示下游系統進行消費消息consumer.commit()
表示消費者往消息隊列提交確認信息,消息隊列接到確認消息,刪除該消息。注意了,我是以processMsg
函數,即處理消息的過程,定義爲消費消息。
如何保證消息最多消費一次?
Producer
:知足最多投遞一次的語義便可,即只管發消息,不須要等待消息隊列返回確認消息。
Message Queue
:接到消息後往內存中一放就行,不用持久化存儲。
Consumer
:拉取到消息之後,直接給消息隊列返回確認消息便可。至於後續消費消息成功與否,無所謂的。即按照如下順序執行it
consumer.poll(); consumer.commit(); processMsg(message);
如何保證消息至少消費一次?
Producer
:知足至少投遞一次語義便可,即發送消息後,須要等待消息隊列返回確認消息。若是超時沒收到確認消息,則重發。
Message Queue
:接到消息後,進行持久化存儲,然後返回生產者確認消息。
Consumer
:拉取到消息後,進行消費,消費成功後,再返回確認消息。即按照以下順序執行
consumer.poll(); processMsg(message); consumer.commit();
因爲這裏Producer
知足的是至少投遞一次語義,所以消息隊列中是有重複消息的。因此咱們的Consumer
會出現重複消費的情形!
如何保證消息剛好消費一次?
在保證至少消費一次的基礎上,processMsg
知足冪等性操做便可。
如何保證冪等性操做?
老問題了,好比有狀態的消息啊。好比惟一表啊。你們搜一搜,一大堆答案,不想重複說了。
本文講的是消息隊列的消費語義和投遞語義的含義,但願你們有所收穫。