前提
生產者給kakfa投遞的消(xiao)息(xi),在同(tong)一個topic下(xia)不(bu)同(tong)的Partition中,不(bu)同(tong)的Partition之間的消(xiao)息(xi)是無法(fa)保(bao)證(zheng)順(shun)(shun)序(xu)(xu)的。但有些(xie)場景,業務需(xu)要處理順(shun)(shun)序(xu)(xu)消(xiao)息(xi)。怎(zen)么保(bao)證(zheng)kakfa消(xiao)息(xi)的順(shun)(shun)序(xu)(xu)性,以致最終消(xiao)費(fei)者能順(shun)(shun)序(xu)(xu)消(xiao)費(fei)消(xiao)息(xi)呢
消息有序性分類
- 全局有序:一個Topic下的所有消息都需要按照生產順序消費。
- 局部有序:一個Topic下的消息,只需要滿足同一業務字段的要按照生產順序消費。例如:Topic消息是訂單的流水表,包含訂單orderId,業務要求同一個orderId的消息需要按照生產順序進行消費。
全局有序
由于Kafka的一個(ge)(ge)Topic可以分為了(le)多個(ge)(ge)Partition,Producer發(fa)送消(xiao)息(xi)的時候,是(shi)分散在不同 Partition的。當Producer按順(shun)序發(fa)消(xiao)息(xi)給Broker,但進入Kafka之后,這(zhe)些消(xiao)息(xi)就不一定(ding)進到哪個(ge)(ge)Partition,會導致順(shun)序是(shi)亂的。因此要滿足全局有序,需要1個(ge)(ge)Topic只能對應1個(ge)(ge)Partition。而且對應的consumer也要使用單線(xian)程(cheng)或者保證消(xiao)費順(shun)序的線(xian)程(cheng)模型.
局部有序
要滿足局部有序,只需(xu)要在(zai)發消(xiao)息(xi)的時候指定Partition Key,Kafka對其進行Hash計算,根據計算結果決定放(fang)入哪個Partition。這樣Partition Key相(xiang)(xiang)同(tong)的消(xiao)息(xi)會(hui)放(fang)在(zai)同(tong)一(yi)(yi)個Partition。確保(bao)將(jiang)相(xiang)(xiang)關(guan)的消(xiao)息(xi)發送(song)(song)到(dao)同(tong)一(yi)(yi)個分(fen)區。Kafka 保(bao)證在(zai)單(dan)個分(fen)區內消(xiao)息(xi)的順(shun)(shun)序。因此(ci),如果消(xiao)息(xi)的順(shun)(shun)序性對業(ye)務非(fei)常重要,可(ke)(ke)以將(jiang)相(xiang)(xiang)關(guan)的消(xiao)息(xi)通過(guo)相(xiang)(xiang)同(tong)的分(fen)區鍵(如用(yong)戶 ID 或訂單(dan) ID)發送(song)(song)至同(tong)一(yi)(yi)分(fen)區。此(ci)時,Partition的數量(liang)(liang)仍然可(ke)(ke)以設置多個,提升Topic的整體吞吐量(liang)(liang)。
提升消費效率
在不增加(jia)partition數量的情況下(xia)想提高消(xiao)費速(su)度,可以(yi)考慮以(yi)上局部(bu)有(you)序的場景下(xia)再(zai)次hash唯一標識(例(li)如(ru)訂單orderId)到不同的線程上,多(duo)個消(xiao)費者線程并發(fa)處理消(xiao)息(依(yi)舊(jiu)可以(yi)保(bao)證局部(bu)有(you)序)。說到底還是一個實(shi)例(li)線程消(xiao)費一個partition消(xiao)息
消息重試對順序消息的影響
對于一個有著(zhu)先后(hou)順序的(de)消息A、B,正常情況(kuang)下(xia)應該是(shi)A先發(fa)(fa)(fa)送完成后(hou)再(zai)發(fa)(fa)(fa)送B,但是(shi)在異常情況(kuang)下(xia),在A發(fa)(fa)(fa)送失敗(bai)的(de)情況(kuang)下(xia),B發(fa)(fa)(fa)送成功,而A由于重試機制(zhi)在B發(fa)(fa)(fa)送完成之后(hou)重試發(fa)(fa)(fa)送成功了。這時(shi)對于本身順序為(wei)AB的(de)消息順序變(bian)成了BA。
針對這種問題,嚴格的順序消費還需要max.in.flight.requests.per.connection參數的支持。
該參數指定了(le)生(sheng)產者在收到服務(wu)器(qi)響應(ying)之前可以(yi)發送多(duo)少個消(xiao)息。它的(de)(de)值(zhi)越(yue)高(gao),就(jiu)會(hui)占用越(yue)多(duo)的(de)(de)內存,同時也(ye)會(hui)提(ti)升吞吐量。把(ba)它設為1就(jiu)可以(yi)保證消(xiao)息是按照(zhao)發送的(de)(de)順序寫入服務(wu)器(qi)的(de)(de)。
此外,對于某些業務場景,設置max.in.flight.requests.per.connection=1會嚴重(zhong)(zhong)降低吞(tun)吐量,如果(guo)放棄使(shi)用這種同步(bu)重(zhong)(zhong)試機制,則可以考慮在(zai)消費端增加失敗(bai)標記(ji)的記(ji)錄(lu),然(ran)后用定時任務(wu)輪詢(xun)去(qu)重(zhong)(zhong)試這些失敗(bai)的消息并做好監控報警。