微服务 消息中间件kafka消息丢失问题
微服務(wù) 消息中間件kafka消息丟失問題
- 1. kafka消息丟失概述
- 1.1 kafka概述
- 1.2 kafka架構(gòu)
- 1.3 kafka問題
- 2. kafka消息傳遞語義
- 3. kafka消息丟失問題分析
- 4. Producer端消息丟失分析
- 4.1 Producer消息發(fā)送流程
- 4.2 Producer 端消息丟失場景
- 4.3 Producer消息確認(rèn)機(jī)制
- 4.4 Producer端消息丟失解決方案
- 5. Broker端消息丟失分析
- 5.1 broker端持久化流程
- 5.2 Broker 端消息丟失場景
- 5.3 Broker 端消息丟失解決方案
- 6. Consumer端消息丟失分析
- 6.1 Consumer消費(fèi)流程
- 6.2 Consumer 端消息丟失場景
- 6.3 Consumer 端消息丟失解決方案
1. kafka消息丟失概述
1.1 kafka概述
kafka是一個(gè)分布式的基于發(fā)布訂閱模式的消息中間件,主要應(yīng)用于實(shí)時(shí)數(shù)據(jù)處理領(lǐng)域。
目前隨著分布式微服務(wù)架構(gòu)盛行,對(duì)系統(tǒng)高性能,高可用,高并發(fā)提出了更高的要求,消息中間件作為分布式微服務(wù)中異步,解耦,削峰的重要組件,變得越來越重要。
kafka官方文檔總共九章,分別從入門,API,配置,設(shè)計(jì),實(shí)施,運(yùn)維,安全,連接,流九個(gè)方面詳細(xì)闡述了卡夫卡應(yīng)用設(shè)計(jì)架構(gòu)。
kafka官方文檔說明:https://kafka.apache.org/documentation/#semantics
1.2 kafka架構(gòu)
Kafka 的整個(gè)架構(gòu)非常簡潔,是分布式的架構(gòu),主要由 Producer、Broker、Consumer 三部分組成,整個(gè)消息的流程也是根據(jù)此三個(gè)模塊進(jìn)行的。這里多說一句新版本kafka不依賴與zk了,但是目前大部分使用的仍然是老版本,zk主要就是配置信息以及選舉的作用,zk有個(gè)很不友好的設(shè)計(jì)就是通過原子廣播協(xié)議選舉的時(shí)候不對(duì)外提供服務(wù),這樣就不能提供高可用特性。
1.3 kafka問題
消息中間件通用問題,消息丟失,消息堆積,重復(fù)消費(fèi),順序消費(fèi)問題。
消息丟失一般圍繞生產(chǎn)者,消息組件,消費(fèi)者三個(gè)環(huán)節(jié)展開,本文也是根據(jù)這三個(gè)維度進(jìn)行分析探討,不足之處還敬請(qǐng)指正。
消費(fèi)堆積問題一般通過增加消費(fèi)者擴(kuò)容,或者異步消費(fèi)解決。
重復(fù)消費(fèi)需要消費(fèi)者做冪等性,根據(jù)全局流水或者業(yè)務(wù)主鍵進(jìn)行判斷。
順序消費(fèi)問題這個(gè)kafka暫不支持,kafka同一個(gè)partation才是順序的,但是實(shí)際生產(chǎn)環(huán)境中同時(shí)存在多個(gè)partation導(dǎo)致無法順序消費(fèi),同步消費(fèi)性能又很差,所以后續(xù)在研究。
2. kafka消息傳遞語義
所謂的消息傳遞語義是 Kafka 提供的 Producer 和 Consumer 之間的消息傳遞過程中消息傳遞的保證性。主要分為三種。詳見kafka官方文檔4.6章節(jié)詳細(xì)說明。
at most once:最多一次。消息可能會(huì)丟失,但是不會(huì)重復(fù)。
at least once:最少一次。消息不會(huì)丟失,但是可能會(huì)重復(fù),
exactly once:精確一次。消息只會(huì)被精確處理一次,不會(huì)丟失重復(fù)。
分析總結(jié):默認(rèn) Kafka 提供 「at least once」語義的消息傳遞,允許用戶通過在處理消息之前保存 Offset 的方式提供 「at most once」 語義。如果我們可以自己實(shí)現(xiàn)消費(fèi)冪等,理想情況下這個(gè)系統(tǒng)的消息傳遞就是嚴(yán)格的「exactly once」, 也就是保證不丟失、且只會(huì)被精確的處理一次,但是這樣是很難做到的。
3. kafka消息丟失問題分析
從 Kafka 整體架構(gòu)圖我們可以得出消息傳遞過程中涉及到三個(gè)環(huán)節(jié)可能存在消息丟失的情況。
1.Producer 端發(fā)送消息給 Kafka Broker 端。
2.Kafka Broker 將消息進(jìn)行同步并持久化數(shù)據(jù)。
3.Consumer 端從 Kafka Broker 將消息拉取并進(jìn)行消費(fèi)。
以上三個(gè)環(huán)節(jié)每一個(gè)步驟都可能出現(xiàn)丟失數(shù)據(jù)的情況,那么kafka怎么才能保證消息不丟失呢。
Kafka 只對(duì)已提交的消息做最大限度的持久化保證不丟失。
首先Producer 將消息提交到kafka中的Broker,只有當(dāng)ISR中所有flower都確認(rèn)收到消息后反饋給Leader,此時(shí)Leader才會(huì)給Producer確認(rèn)收到消息,避免了Leader收到后,在同步到副本之前掛掉,消息丟失的場景。這也就是kafka對(duì)已經(jīng)提交的消息做最大限度的持久化保證不丟失。
這里還有個(gè)誤區(qū)需要注意,Leader收到消息會(huì)同步ISR中的flower并不是所有flower,這樣既保證了時(shí)效性又保證了數(shù)據(jù)的可靠性,也算是一個(gè)這種的處理設(shè)計(jì)方式吧。
4. Producer端消息丟失分析
4.1 Producer消息發(fā)送流程
1)首先我們要知道一點(diǎn)就是 Producer 端是直接與 Broker 中的 Leader Partition 交互的,所以在 Producer 端初始化中就需要通過 Partitioner 分區(qū)器從 Kafka 集群中獲取到相關(guān) Topic 對(duì)應(yīng)的 Leader Partition 的元數(shù)據(jù) 。2)待獲取到 Leader Partition 的元數(shù)據(jù)后直接將消息發(fā)送過去。3)Kafka Broker 對(duì)應(yīng)的 Leader Partition 收到消息會(huì)先寫入 Page Cache,定時(shí)刷盤進(jìn)行持久化(順序?qū)懭氪疟P)。4) Follower Partition 拉取 Leader Partition 的消息并保持同 Leader Partition 數(shù)據(jù)一致,待消息拉取完畢后需要給 Leader Partition 回復(fù) ACK 確認(rèn)消息。5)待 Kafka Leader 與 Follower Partition 同步完數(shù)據(jù)并收到所有 ISR 中的 Replica 副本的 ACK 后,Leader Partition 會(huì)給 Producer 回復(fù) ACK 確認(rèn)消息。根據(jù)以及消息發(fā)送流程可以得出:Producer 端為了提升發(fā)送效率,減少IO操作,發(fā)送數(shù)據(jù)的時(shí)候是將多個(gè)請(qǐng)求合并成一個(gè)個(gè) RecordBatch,并將其封裝轉(zhuǎn)換成 Request 請(qǐng)求「異步」將數(shù)據(jù)發(fā)送出去(也可以按時(shí)間間隔方式,達(dá)到時(shí)間間隔自動(dòng)發(fā)送),所以 Producer 端消息丟失更多是因?yàn)橄⒏揪蜎]有發(fā)送到 Kafka Broker 端。
4.2 Producer 端消息丟失場景
網(wǎng)絡(luò)原因:由于網(wǎng)絡(luò)抖動(dòng)導(dǎo)致數(shù)據(jù)根本就沒發(fā)送到 Broker 端。 數(shù)據(jù)原因:消息體太大超出 Broker 承受范圍而導(dǎo)致 Broker 拒收消息。4.3 Producer消息確認(rèn)機(jī)制
Kafka Producer 端也可以通過配置來確認(rèn)消息是否生產(chǎn)成功,配置參數(shù):request.required.acks。
request.required.acks=0
他表示只要發(fā)送就認(rèn)為成功,并不進(jìn)行消息結(jié)合搜是否成功的ACK確認(rèn),不能保證消息是否發(fā)送成功,實(shí)際生產(chǎn)環(huán)境無法使用,不能保證數(shù)據(jù)可靠性。
request.required.acks=1
他表示當(dāng)Leader接收成功時(shí)就進(jìn)行ack確認(rèn),只要leader存活就可以保證消息不丟失,也能保證吞吐量,但是可用性不高,一旦leader收到消息,確認(rèn)ack接收成功,同步flower之前掛掉就會(huì)導(dǎo)致數(shù)據(jù)丟失,實(shí)際生產(chǎn)環(huán)境根據(jù)業(yè)務(wù)場景慎用。
request.required.acks=-1或者all
他表示leader和ISR列表中的所有flower接收完成后,進(jìn)行ack確認(rèn),可以最大限度的保證消息不丟失,同時(shí)也保證了系統(tǒng)的高可用性,因?yàn)橛型絝lower的操作所以性能稍遜于等于1的情況,但是提高了可用性,比較推薦的配置方式。
4.4 Producer端消息丟失解決方案
1.更換調(diào)用方式
棄用調(diào)用發(fā)后即焚的方式,使用帶回調(diào)通知函數(shù)的方法進(jìn)行發(fā)送消息,即 Producer.send(msg, callback), 這樣一旦發(fā)現(xiàn)發(fā)送失敗, 就可以做針對(duì)性處理。
(1)網(wǎng)絡(luò)抖動(dòng)導(dǎo)致消息丟失,Producer 端可以進(jìn)行重試。
(2)消息大小不合格,可以進(jìn)行適當(dāng)調(diào)整,符合 Broker 承受范圍再發(fā)送。
通過以上方式可以保證最大限度消息可以發(fā)送成功。
2 ACK 確認(rèn)機(jī)制
需要將 request.required.acks 設(shè)置為 -1/ all,-1/all 表示有多少個(gè)副本 Broker 全部收到消息,才認(rèn)為是消息提交成功的標(biāo)識(shí)。
針對(duì) acks = -1/ all , 這里有兩種非常典型的情況:
(1)數(shù)據(jù)發(fā)送到 Leader Partition, 且所有的 ISR 成員全部同步完數(shù)據(jù), 此時(shí),Leader Partition 異常 Crash 掉,那么會(huì)選舉新的 Leader Partition,數(shù)據(jù)不會(huì)丟失。
(2)數(shù)據(jù)發(fā)送到 Leader Partition,部分 ISR 成員同步完成,此時(shí) Leader Partition 異常 Crash, 剩下的 Follower Partition 都可能被選舉成新的 Leader Partition,會(huì)給 Producer 端發(fā)送失敗標(biāo)識(shí), 后續(xù)會(huì)重新發(fā)送數(shù)據(jù),數(shù)據(jù)可能會(huì)重復(fù)。
因此通過上面分析,我們還需要通過其他參數(shù)配置來進(jìn)行保證:
replication.factor >= 2
min.insync.replicas > 1
3 重試次數(shù) retries
該參數(shù)表示 Producer 端發(fā)送消息的重試次數(shù)。
需要將 retries 設(shè)置為大于0的數(shù), 在 Kafka 2.4 版本中默認(rèn)設(shè)置為Integer.MAX_VALUE。另外如果需要保證發(fā)送消息的順序性,配置如下:
retries = Integer.MAX_VALUE
max.in.flight.requests.per.connection = 1
4 重試時(shí)間 retry.backoff.ms
該參數(shù)表示消息發(fā)送超時(shí)后兩次重試之間的間隔時(shí)間,避免無效的頻繁重試,默認(rèn)值為100ms, 推薦設(shè)置為300ms。
5. Broker端消息丟失分析
5.1 broker端持久化流程
生產(chǎn)者發(fā)送消息到kafka broker端持久化流程。
#mermaid-svg-KpyPr3nKfGWTXQeX .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .label text{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .node rect,#mermaid-svg-KpyPr3nKfGWTXQeX .node circle,#mermaid-svg-KpyPr3nKfGWTXQeX .node ellipse,#mermaid-svg-KpyPr3nKfGWTXQeX .node polygon,#mermaid-svg-KpyPr3nKfGWTXQeX .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-KpyPr3nKfGWTXQeX .node .label{text-align:center;fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .node.clickable{cursor:pointer}#mermaid-svg-KpyPr3nKfGWTXQeX .arrowheadPath{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-KpyPr3nKfGWTXQeX .flowchart-link{stroke:#333;fill:none}#mermaid-svg-KpyPr3nKfGWTXQeX .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-KpyPr3nKfGWTXQeX .edgeLabel rect{opacity:0.9}#mermaid-svg-KpyPr3nKfGWTXQeX .edgeLabel span{color:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-KpyPr3nKfGWTXQeX .cluster text{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-KpyPr3nKfGWTXQeX .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-KpyPr3nKfGWTXQeX text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-KpyPr3nKfGWTXQeX .actor-line{stroke:grey}#mermaid-svg-KpyPr3nKfGWTXQeX .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-KpyPr3nKfGWTXQeX #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .sequenceNumber{fill:#fff}#mermaid-svg-KpyPr3nKfGWTXQeX #sequencenumber{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX #crosshead path{fill:#333;stroke:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .messageText{fill:#333;stroke:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-KpyPr3nKfGWTXQeX .labelText,#mermaid-svg-KpyPr3nKfGWTXQeX .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-KpyPr3nKfGWTXQeX .loopText,#mermaid-svg-KpyPr3nKfGWTXQeX .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-KpyPr3nKfGWTXQeX .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-KpyPr3nKfGWTXQeX .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-KpyPr3nKfGWTXQeX .noteText,#mermaid-svg-KpyPr3nKfGWTXQeX .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-KpyPr3nKfGWTXQeX .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-KpyPr3nKfGWTXQeX .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-KpyPr3nKfGWTXQeX .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-KpyPr3nKfGWTXQeX .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX .section{stroke:none;opacity:0.2}#mermaid-svg-KpyPr3nKfGWTXQeX .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-KpyPr3nKfGWTXQeX .section2{fill:#fff400}#mermaid-svg-KpyPr3nKfGWTXQeX .section1,#mermaid-svg-KpyPr3nKfGWTXQeX .section3{fill:#fff;opacity:0.2}#mermaid-svg-KpyPr3nKfGWTXQeX .sectionTitle0{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .sectionTitle1{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .sectionTitle2{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .sectionTitle3{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-KpyPr3nKfGWTXQeX .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX .grid path{stroke-width:0}#mermaid-svg-KpyPr3nKfGWTXQeX .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-KpyPr3nKfGWTXQeX .task{stroke-width:2}#mermaid-svg-KpyPr3nKfGWTXQeX .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX .taskText:not([font-size]){font-size:11px}#mermaid-svg-KpyPr3nKfGWTXQeX .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-KpyPr3nKfGWTXQeX .task.clickable{cursor:pointer}#mermaid-svg-KpyPr3nKfGWTXQeX .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-KpyPr3nKfGWTXQeX .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-KpyPr3nKfGWTXQeX .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-KpyPr3nKfGWTXQeX .taskText0,#mermaid-svg-KpyPr3nKfGWTXQeX .taskText1,#mermaid-svg-KpyPr3nKfGWTXQeX .taskText2,#mermaid-svg-KpyPr3nKfGWTXQeX .taskText3{fill:#fff}#mermaid-svg-KpyPr3nKfGWTXQeX .task0,#mermaid-svg-KpyPr3nKfGWTXQeX .task1,#mermaid-svg-KpyPr3nKfGWTXQeX .task2,#mermaid-svg-KpyPr3nKfGWTXQeX .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-KpyPr3nKfGWTXQeX .taskTextOutside0,#mermaid-svg-KpyPr3nKfGWTXQeX .taskTextOutside2{fill:#000}#mermaid-svg-KpyPr3nKfGWTXQeX .taskTextOutside1,#mermaid-svg-KpyPr3nKfGWTXQeX .taskTextOutside3{fill:#000}#mermaid-svg-KpyPr3nKfGWTXQeX .active0,#mermaid-svg-KpyPr3nKfGWTXQeX .active1,#mermaid-svg-KpyPr3nKfGWTXQeX .active2,#mermaid-svg-KpyPr3nKfGWTXQeX .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-KpyPr3nKfGWTXQeX .activeText0,#mermaid-svg-KpyPr3nKfGWTXQeX .activeText1,#mermaid-svg-KpyPr3nKfGWTXQeX .activeText2,#mermaid-svg-KpyPr3nKfGWTXQeX .activeText3{fill:#000 !important}#mermaid-svg-KpyPr3nKfGWTXQeX .done0,#mermaid-svg-KpyPr3nKfGWTXQeX .done1,#mermaid-svg-KpyPr3nKfGWTXQeX .done2,#mermaid-svg-KpyPr3nKfGWTXQeX .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-KpyPr3nKfGWTXQeX .doneText0,#mermaid-svg-KpyPr3nKfGWTXQeX .doneText1,#mermaid-svg-KpyPr3nKfGWTXQeX .doneText2,#mermaid-svg-KpyPr3nKfGWTXQeX .doneText3{fill:#000 !important}#mermaid-svg-KpyPr3nKfGWTXQeX .crit0,#mermaid-svg-KpyPr3nKfGWTXQeX .crit1,#mermaid-svg-KpyPr3nKfGWTXQeX .crit2,#mermaid-svg-KpyPr3nKfGWTXQeX .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-KpyPr3nKfGWTXQeX .activeCrit0,#mermaid-svg-KpyPr3nKfGWTXQeX .activeCrit1,#mermaid-svg-KpyPr3nKfGWTXQeX .activeCrit2,#mermaid-svg-KpyPr3nKfGWTXQeX .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-KpyPr3nKfGWTXQeX .doneCrit0,#mermaid-svg-KpyPr3nKfGWTXQeX .doneCrit1,#mermaid-svg-KpyPr3nKfGWTXQeX .doneCrit2,#mermaid-svg-KpyPr3nKfGWTXQeX .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-KpyPr3nKfGWTXQeX .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-KpyPr3nKfGWTXQeX .milestoneText{font-style:italic}#mermaid-svg-KpyPr3nKfGWTXQeX .doneCritText0,#mermaid-svg-KpyPr3nKfGWTXQeX .doneCritText1,#mermaid-svg-KpyPr3nKfGWTXQeX .doneCritText2,#mermaid-svg-KpyPr3nKfGWTXQeX .doneCritText3{fill:#000 !important}#mermaid-svg-KpyPr3nKfGWTXQeX .activeCritText0,#mermaid-svg-KpyPr3nKfGWTXQeX .activeCritText1,#mermaid-svg-KpyPr3nKfGWTXQeX .activeCritText2,#mermaid-svg-KpyPr3nKfGWTXQeX .activeCritText3{fill:#000 !important}#mermaid-svg-KpyPr3nKfGWTXQeX .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-KpyPr3nKfGWTXQeX g.classGroup text .title{font-weight:bolder}#mermaid-svg-KpyPr3nKfGWTXQeX g.clickable{cursor:pointer}#mermaid-svg-KpyPr3nKfGWTXQeX g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-KpyPr3nKfGWTXQeX g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-KpyPr3nKfGWTXQeX .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-KpyPr3nKfGWTXQeX .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-KpyPr3nKfGWTXQeX .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-KpyPr3nKfGWTXQeX .dashed-line{stroke-dasharray:3}#mermaid-svg-KpyPr3nKfGWTXQeX #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-KpyPr3nKfGWTXQeX #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-KpyPr3nKfGWTXQeX #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-KpyPr3nKfGWTXQeX #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-KpyPr3nKfGWTXQeX #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-KpyPr3nKfGWTXQeX #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-KpyPr3nKfGWTXQeX #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-KpyPr3nKfGWTXQeX #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-KpyPr3nKfGWTXQeX .commit-id,#mermaid-svg-KpyPr3nKfGWTXQeX .commit-msg,#mermaid-svg-KpyPr3nKfGWTXQeX .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-KpyPr3nKfGWTXQeX g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-KpyPr3nKfGWTXQeX g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-KpyPr3nKfGWTXQeX g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-KpyPr3nKfGWTXQeX .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-KpyPr3nKfGWTXQeX .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-KpyPr3nKfGWTXQeX .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-KpyPr3nKfGWTXQeX .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-KpyPr3nKfGWTXQeX .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-KpyPr3nKfGWTXQeX .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-KpyPr3nKfGWTXQeX .edgeLabel text{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-KpyPr3nKfGWTXQeX .node circle.state-start{fill:black;stroke:black}#mermaid-svg-KpyPr3nKfGWTXQeX .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-KpyPr3nKfGWTXQeX #statediagram-barbEnd{fill:#9370db}#mermaid-svg-KpyPr3nKfGWTXQeX .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-KpyPr3nKfGWTXQeX .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-KpyPr3nKfGWTXQeX .statediagram-state .divider{stroke:#9370db}#mermaid-svg-KpyPr3nKfGWTXQeX .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-KpyPr3nKfGWTXQeX .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-KpyPr3nKfGWTXQeX .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-KpyPr3nKfGWTXQeX .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-KpyPr3nKfGWTXQeX .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-KpyPr3nKfGWTXQeX .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-KpyPr3nKfGWTXQeX .note-edge{stroke-dasharray:5}#mermaid-svg-KpyPr3nKfGWTXQeX .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-KpyPr3nKfGWTXQeX .error-icon{fill:#522}#mermaid-svg-KpyPr3nKfGWTXQeX .error-text{fill:#522;stroke:#522}#mermaid-svg-KpyPr3nKfGWTXQeX .edge-thickness-normal{stroke-width:2px}#mermaid-svg-KpyPr3nKfGWTXQeX .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-KpyPr3nKfGWTXQeX .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-KpyPr3nKfGWTXQeX .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-KpyPr3nKfGWTXQeX .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-KpyPr3nKfGWTXQeX .marker{fill:#333}#mermaid-svg-KpyPr3nKfGWTXQeX .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}#mermaid-svg-KpyPr3nKfGWTXQeX {color: rgba(0, 0, 0, 0.75);font: ;}Producerkafka BrokerOS Cache磁盤Kafka Broker 集群接收到數(shù)據(jù)后會(huì)將數(shù)據(jù)進(jìn)行持久化存儲(chǔ)到磁盤,為了提高吞吐量和性能,采用的是「異步批量刷盤的策略」,也就是說按照一定的消息量和間隔時(shí)間進(jìn)行刷盤。首先會(huì)將數(shù)據(jù)存儲(chǔ)到 「PageCache」 中,至于什么時(shí)候?qū)?Cache 中的數(shù)據(jù)刷盤是由「操作系統(tǒng)」根據(jù)自己的策略決定或者調(diào)用 fsync 命令進(jìn)行強(qiáng)制刷盤,如果此時(shí) Broker 宕機(jī) Crash 掉,且選舉了一個(gè)落后 Leader Partition 很多的 Follower Partition 成為新的 Leader Partition,那么落后的消息數(shù)據(jù)就會(huì)丟失。
既然 Broker 端消息存儲(chǔ)是通過異步批量刷盤的,那么這里就可能會(huì)丟數(shù)據(jù)的!!!
5.2 Broker 端消息丟失場景
1.由于Kafka中并沒有提供同步刷盤的方式,所以說從單個(gè) Broker 來看還是很有可能丟失數(shù)據(jù)的。
2.kafka 通過「多 Partition (分區(qū))多 Replica(副本)機(jī)制」已經(jīng)可以最大限度的保證數(shù)據(jù)不丟失,如果數(shù)據(jù)已經(jīng)寫入 PageCache 中但是還沒來得及刷寫到磁盤,此時(shí)如果所在 Broker 突然宕機(jī)掛掉或者停電,極端情況還是會(huì)造成數(shù)據(jù)丟失。
5.3 Broker 端消息丟失解決方案
1.unclean.leader.election.enable
該參數(shù)表示有哪些 Follower 可以有資格被選舉為 Leader , 如果一個(gè) Follower 的數(shù)據(jù)落后 Leader 太多,那么一旦它被選舉為新的 Leader, 數(shù)據(jù)就會(huì)丟失,因此我們要將其設(shè)置為false,防止此類情況發(fā)生。
2.replication.factor
該參數(shù)表示分區(qū)副本的個(gè)數(shù)。建議設(shè)置 replication.factor >=3, 這樣如果 Leader 副本異常 Crash 掉,Follower 副本會(huì)被選舉為新的 Leader 副本繼續(xù)提供服務(wù)。
3 min.insync.replicas
該參數(shù)表示消息至少要被寫入成功到 ISR 多少個(gè)副本才算"已提交",建議設(shè)置min.insync.replicas > 1, 這樣才可以提升消息持久性,保證數(shù)據(jù)不丟失。
另外我們還需要確保一下 replication.factor > min.insync.replicas, 如果相等,只要有一個(gè)副本異常 Crash 掉,整個(gè)分區(qū)就無法正常工作了,因此推薦設(shè)置成: replication.factor = min.insync.replicas +1, 最大限度保證系統(tǒng)可用性。
6. Consumer端消息丟失分析
6.1 Consumer消費(fèi)流程
1)Consumer 拉取數(shù)據(jù)之前跟 Producer 發(fā)送數(shù)據(jù)一樣, 需要通過訂閱關(guān)系獲取到集群元數(shù)據(jù), 找到相關(guān) Topic 對(duì)應(yīng)的 Leader Partition 的元數(shù)據(jù)。2)然后 Consumer 通過 Pull 模式主動(dòng)的去 Kafka 集群中拉取消息。3)在這個(gè)過程中,有個(gè)消費(fèi)者組的概念(不了解的可以看上面鏈接文章),多個(gè) Consumer 可以組成一個(gè)消費(fèi)者組即 Consumer Group,每個(gè)消費(fèi)者組都有一個(gè)Group-Id。同一個(gè) Consumer Group 中的 Consumer 可以消費(fèi)同一個(gè) Topic 下不同分區(qū)的數(shù)據(jù),但是不會(huì)出現(xiàn)多個(gè) Consumer 去消費(fèi)同一個(gè)分區(qū)的數(shù)據(jù)。4)拉取到消息后進(jìn)行業(yè)務(wù)邏輯處理,待處理完成后,會(huì)進(jìn)行 ACK 確認(rèn),即提交 Offset 消費(fèi)位移進(jìn)度記錄。5)最后 Offset 會(huì)被保存到 Kafka Broker 集群中的 __consumer_offsets 這個(gè) Topic 中,且每個(gè) Consumer 保存自己的 Offset 進(jìn)度。根據(jù)上圖以及消息消費(fèi)流程可以得出消費(fèi)主要分為兩個(gè)階段:
獲取元數(shù)據(jù)并從 Kafka Broker 集群拉取數(shù)據(jù)。 處理消息,并標(biāo)記消息已經(jīng)被消費(fèi),提交 Offset 記錄。既然 Consumer 拉取后消息最終是要提交 Offset, 那么這里就可能會(huì)丟數(shù)據(jù)的!!!
6.2 Consumer 端消息丟失場景
1.可能使用的「自動(dòng)提交 Offset 方式」。
2.拉取消息后「先提交 Offset,后處理消息」,如果此時(shí)處理消息的時(shí)候異常宕機(jī),由于 Offset 已經(jīng)提交了, 待 Consumer 重啟后,會(huì)從之前已提交的 Offset 下一個(gè)位置重新開始消費(fèi), 之前未處理完成的消息不會(huì)被再次處理,對(duì)于該 Consumer 來說消息就丟失了。
3.拉取消息后「先處理消息,在進(jìn)行提交 Offset」, 如果此時(shí)在提交之前發(fā)生異常宕機(jī),由于沒有提交成功 Offset, 待下次 Consumer 重啟后還會(huì)從上次的 Offset 重新拉取消息,不會(huì)出現(xiàn)消息丟失的情況, 但是會(huì)出現(xiàn)重復(fù)消費(fèi)的情況,這里只能業(yè)務(wù)自己保證冪等性。
6.3 Consumer 端消息丟失解決方案
在剖析 Consumer 端丟失場景的時(shí)候,我們得出其拉取完消息后是需要提交 Offset 位移信息的,因此為了不丟數(shù)據(jù),正確的做法是:拉取數(shù)據(jù)、業(yè)務(wù)邏輯處理、提交消費(fèi) Offset 位移信息。
我們還需要設(shè)置參數(shù) enable.auto.commit = false, 采用手動(dòng)提交位移的方式。
另外對(duì)于消費(fèi)消息重復(fù)的情況,業(yè)務(wù)自己保證冪等性, 保證只成功消費(fèi)一次即可。
參考文檔
微服務(wù) 消息中間件Kafka詳解
kafka官網(wǎng)文檔
總結(jié)
以上是生活随笔為你收集整理的微服务 消息中间件kafka消息丢失问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sqlmap下载安装
- 下一篇: RPC failed; curl 56