原文地址:https://kafka.apache.org/0101/documentation.html#semantics
現(xiàn)在我們對(duì)Producer和Consumer已經(jīng)有了一定的了解,接著我們來討論Kafka在Producer和Consumer上提供的語義。顯然的,在分發(fā)消息時(shí)是可以有多種語義的:
- At most once:消息可能丟失,但不會(huì)重復(fù)投遞
- At least once:消息不會(huì)丟失,但可能會(huì)重復(fù)投遞
- Exactly once:消息不丟失、不重復(fù),會(huì)且只會(huì)被分發(fā)一次(真正想要的)
值得注意的是這分為兩個(gè)問題:發(fā)布消息的可用性和消費(fèi)消息的可用性。
許多系統(tǒng)都聲稱提供“exactly once”語義,仔細(xì)閱讀會(huì)發(fā)現(xiàn),這些聲明是誤導(dǎo)的(他們沒有考慮Producer和Consumer可能Crash的場(chǎng)景,或是數(shù)據(jù)寫入磁盤后丟失的情況)。
Kafka提供的語義是直接了當(dāng)?shù)?。發(fā)送消息的時(shí)候我們有一個(gè)消息被Commit到Log的概念。一旦消息已經(jīng)被Commit,它將不會(huì)丟失,只要還有一個(gè)復(fù)制了消息所在Partition的Broker存活著?!按婊睢钡亩x以及我們覆蓋的失敗的情況將在下一節(jié)描述?,F(xiàn)在假設(shè)一個(gè)完美的Broker,并且不會(huì)丟失,來理解對(duì)Producer和Consumer提供的語義保證。如果Producer發(fā)送一條消息,并且發(fā)生了網(wǎng)絡(luò)錯(cuò)誤,我們是不能確認(rèn)錯(cuò)誤發(fā)生在消息Commit之前還是消息Commit之后的。類似于使用自增主鍵插入數(shù)據(jù)庫,是不能確認(rèn)寫入之后的主鍵值的。
Producer沒有使用的強(qiáng)制可能的語義。我們無法確認(rèn)網(wǎng)絡(luò)是否會(huì)發(fā)生異常,可以使Producer創(chuàng)建有序的主鍵使重試發(fā)送成為冪等的行為。這個(gè)特性對(duì)一個(gè)復(fù)制系統(tǒng)來說不是無價(jià)值的,因?yàn)榉?wù)器在發(fā)生故障的情況下依舊需要提供服務(wù)。使用這個(gè)功能,Producer可以重試,直到收到消息成功commit的響應(yīng),在這個(gè)點(diǎn)上保證消息發(fā)送的exactly once。我們希望把這個(gè)特性加到后續(xù)的Kafka版本中。
不是所有的場(chǎng)景都需要這樣的保證。對(duì)應(yīng)延遲敏感的場(chǎng)景,我們?cè)试SProducer指定其期望的可用性級(jí)別。如果Producer期望等待消息Commit,那么這可能消耗10ms。Producer也可以指定以異步的方式發(fā)送消息或只等Leader節(jié)點(diǎn)寫入消息(不能Follower)。
接著我們從消費(fèi)者的視角來描述語義。所有的副本都擁有偏移量相同的日志。Consumer控制它在日志中的偏移量。如果Consumer一直正常運(yùn)行,它可以只把偏移量存儲(chǔ)在內(nèi)存中,但是如果Consumer crash且我們期望另一個(gè)新的Consumer接管消費(fèi),那么需要選擇一個(gè)位置來開始消費(fèi)。假設(shè)Consumer讀取了一些消息——它有集中處理消息和位置的方式。
- 1.它可以讀取消息,然后保存位置信息,然后處理消息。在這個(gè)場(chǎng)景中,Consumer可能在保存位置信息后消費(fèi)消息失敗,那么下一次消費(fèi)可能從保存的位點(diǎn)開始,盡管之前部分消息被處理失敗。這是消費(fèi)處理過程中失敗的at-most-once(只被處理了一次,但是可能處理失敗)。
- 2.它可以讀取消息,之后處理消息,最后保存位置信息。這個(gè)場(chǎng)景中,Consumer可能在處理完消息,但是保存位點(diǎn)之前Crash,那么下一次會(huì)重新消費(fèi)這些消息,盡管已經(jīng)被消費(fèi)過。這是Consumer Crash引起的at-least-once(消息可能會(huì)被處理多次)。在很多場(chǎng)景沖,消息可以有一個(gè)逐漸,這樣可以保證處理的冪等性(多次處理不會(huì)有影響)。
- 3.那么什么是exactly once語義?這里的限制實(shí)際上不是消息系統(tǒng)的特性,而是消息處理和位置信息的保存。經(jīng)典的解決方案是采用兩階段提交的方式來處理。但是這也可以用一個(gè)更簡單的方式來處理:通過將消息處理結(jié)果和位置信息保存在同一位置上。這是更好的,因?yàn)楹芏郈onsumer期望寫入的系統(tǒng)并不支持兩階段提交。例如,我們的hadoop ETL工具從保存數(shù)據(jù)到dhfs上的同時(shí)也把位移位置也保存到hdfs中了,這樣可以保證數(shù)據(jù)和位移位置同時(shí)被更新或者都沒更新。我們?cè)诤芏嘞到y(tǒng)上使用類似的模式,用于解決那些需要這種強(qiáng)語義但是卻沒有主鍵用于區(qū)分重復(fù)的儲(chǔ)存系統(tǒng)中。
默認(rèn)Kafka提供at-least-once語義的消息分發(fā),允許用戶通過在處理消息之前保存位置信息的方式來提供at-most-once語義。exactly-once語義需要和輸出系統(tǒng)相結(jié)合,Kafka提供的offset可以使這個(gè)實(shí)現(xiàn)變的“直接了當(dāng)?shù)摹保ㄗ兊帽容^簡單)。