生產(chǎn)環(huán)境Kafka集群400W/Tps為啥就扛不住了?
最近公司日志Kafka集群出現(xiàn)了性能瓶頸,單節(jié)點(diǎn)還沒達(dá)到60W/tps時(shí)消息發(fā)送就出現(xiàn)了很大延遲,甚至最高超過了10s,截圖說明如下:
雖說使用的機(jī)械磁盤,但這點(diǎn)壓力對(duì)Kafka來說應(yīng)該是小菜一碟,這引起了我的警覺,需要對(duì)其進(jìn)行一番診斷了。
通過監(jiān)控平臺(tái)觀察Kafka集群中相關(guān)的監(jiān)控節(jié)點(diǎn),發(fā)現(xiàn)cpu使用率才接近20%左右,磁盤IO等待等指標(biāo)都并未出現(xiàn)任何異常,那會(huì)是什么問題呢?
通常CPU耗時(shí)不大,但性能已經(jīng)明顯下降了,我們優(yōu)先會(huì)去排查kafka節(jié)點(diǎn)的線程棧,獲取線程棧的方法比較簡(jiǎn)單,命令為:
ps -ef | grep kafka // 獲取pid
jstack pid > j1.log
通過上述命令我們就可以獲取到kafka進(jìn)程的堆棧信息,通過查看線程名稱中包含kafka-request-handler字眼的線程(Kafka中處理請(qǐng)求),發(fā)現(xiàn)了大量的鎖等待,具體截圖如下所示:
并且在jstack文件中發(fā)現(xiàn)很多線程都在等待這把鎖,截圖如下:
我們先根據(jù)線程堆棧查看代碼,找到對(duì)應(yīng)的源代碼如下圖所示:
通過閱讀源碼,這段代碼是分區(qū)Leader在追加數(shù)據(jù)時(shí)為了保證寫入分區(qū)時(shí)數(shù)據(jù)的完整性,對(duì)分區(qū)進(jìn)行的加鎖,即如果對(duì)同一個(gè)分區(qū)收到多個(gè)寫入請(qǐng)求,則這些請(qǐng)求將串行執(zhí)行,這個(gè)鎖時(shí)必須的,無法進(jìn)行優(yōu)化,但仔細(xì)觀察線程的調(diào)用棧,發(fā)現(xiàn)在鎖的代碼塊出現(xiàn)了GZIPInputstream,進(jìn)行了zip壓縮,一個(gè)壓縮處在鎖中,其執(zhí)行性能注定低下,那在什么時(shí)候需要在服務(wù)端進(jìn)行壓縮呢?
故我們繼續(xù)看一下LogValidator的validateMessagesAndAssignOffsets方法,最終調(diào)用validateMessagesAndAssignOffsetsCompressed方法,部分代碼截圖如下所示:
這段代碼的注釋部分詳細(xì)介紹了kafka在服務(wù)端需要進(jìn)行壓縮的4種情況,對(duì)其進(jìn)行翻譯,其實(shí)就是兩種情況:
- 客戶端與服務(wù)端端壓縮算法不一致
- 客戶端與服務(wù)端端的消息版本格式不一樣,包括offset的表示方法、壓縮處理方法
關(guān)于客戶端與服務(wù)端壓縮算法不一致,這個(gè)基本不會(huì)出現(xiàn),因?yàn)榉?wù)端通??梢灾С侄喾N壓縮算法,會(huì)根據(jù)客戶端的壓縮算法進(jìn)行自動(dòng)匹配。
最有可能的就是服務(wù)端與客戶端端消息協(xié)議版本不一致,如果版本不一致,則需要在服務(wù)端重新偏移量,如果使用了壓縮機(jī)制,則需要重新進(jìn)行解壓縮,然后計(jì)算位點(diǎn),再進(jìn)行壓縮存儲(chǔ),性能消耗極大。
后面排查日志使用端,確實(shí)是客戶端版本與服務(wù)端版本不一致導(dǎo)致,最終需要對(duì)客戶端進(jìn)行統(tǒng)一升級(jí)。