作者:vivo 網際網路伺服器團隊-Luo Mingbo
一、Kafka 叢集部署架構
為了讓讀者能與小編在後續的問題分析中有更好的共鳴,小編先與各位讀者朋友對齊一下我們 Kafka 叢集的部署架構及服務接入 Kafka 叢集的流程。
為了避免超大叢集我們按照業務維度將整個每天負責十萬億級訊息的 Kafka 叢集拆分成了多個 Kafka 叢集。拆分粒度太粗會導致單一叢集過大,容易由於流量突變、資源隔離、限速等原因導致叢集穩定性和可用性受到影響,拆分粒度太細又會因為叢集太多不易維護,叢集內資源較少應對突發情況的抗風險能力較弱。
由於 Kafka 資料儲存和服務在同一節點上導致叢集擴縮容週期較長,遇到突發流量時不能快速實現叢集擴容扛住業務壓力,因此我們按照業務維度和資料的重要程度及是否影響商業化等維度進行 Kafka 叢集的拆分,同時在 Kafka 叢集內新增一層邏輯概念“資源組”,資源組內的 Node 節點共享,資源組與資源組之間的節點資源相互隔離,確保故障發生時不會帶來雪崩效應。
二、業務接入 Kafka 叢集流程
-
在 Kafka 平臺註冊業務專案。
-
若專案的業務資料較為重要或直接影響商業化,使用者需申請建立專案獨立的資源組,若專案資料量較小且對資料的完整性要求不那麼高可以直接使用叢集提供的公共資源組無需申請資源組。
-
專案與邏輯概念資源組繫結。
-
建立 topic,建立 topic 時使用 Kafka 平臺提供的介面進行建立,嚴格遵守 topic 的分割槽分佈只能在專案繫結的資源組管理的 broker 節點上。
-
授權對 topic 的讀寫操作。
通過上述的架構部署介紹及接入流程接入介紹相信大家有很多相關知識點都與小編對齊了。
從部署架構圖我們可以清晰的瞭解到我們這套叢集部署在服務端最小的資源隔離單元為“資源組”即在同一個資源組下的多個broker節點之間會有影響,不同的資源組下的broker節點做了邏輯隔離。
上述的相關知識點對齊後我們將開啟我們的故障排查之旅。
三、故障情況介紹
故障發生時,故障節點所在資源組的多個 topic 流量幾乎全部掉零,生產環境我們對 Kafka 叢集的磁碟指標READ、WRITE、IO.UTIL、AVG.WAIT、READ.REQ、WRITE.REQ做了告警監控,由於故障發生在凌晨,整個故障的處理過程持續實踐較長,導致了業務方長時間的topic流量整體掉零對業務造成不小的影響。
四、監控指標介紹
4.1 流量監控情況
1、故障節點在故障發生時網路空閒率出現短暫的掉零情況,且與生產流量監控指標一致。一旦生產流量上升故障節點的網路空閒率就同步掉零。
2、Grafana 監控指標中topic生產流量幾乎全部掉零。
3、Kafka 平臺專案監控中也體現了當前專案的多個topic生產流量指標掉零。
4.2 磁碟指標監控
SDF 盤的IO.UTIL指標達到100%, 80%左右我們認為是服務可穩定執行的指標閾值。
SDF 盤的AVG.WAIT指標達到分鐘級等待,一般400ms左右的延遲我們認為是服務可穩定執行的閾值。
4.3 Kafka 服務端日誌及系統日誌情況
Kafka叢集controller節點的日誌中出現Input/Output error的錯誤日誌。
Linux 系統日誌中出現Buffer I/O error 的錯誤日誌
五、故障猜想及分析
從上述的指標監控中很明顯的可以得出結論,故障原因是由於 Kafka broker節點的sdf盤磁碟故障導致的,只需在對應的 Kafka broker 節點上將sdf盤踢掉重啟即可恢復。那這樣就結束了嗎 ?of course not。
對 Kafka 有一定認識的小夥伴應該都知道,建立topic時topic的分割槽是均勻分佈到叢集內的不同broker節點上的,即使內部某一臺broker節點故障,其他分割槽應該能正常進行生產消費,如果其他分割槽能進行正常的生產和消費就不應該出現整個topic的流量幾乎全掉零的情況。
如上圖所示,topicA 的三個分割槽分別分佈在 brokerA、brokerB、brokerC三個物理主機節點上。
生產者producer向TopicA傳送訊息時會分別與brokerA、brokerB、brokerC三個物理主機節點建立長連結進行訊息的傳送,此時若 brokerB 節點發生故障無法向外部提供服務時按照我們的猜想應該不會影響到brokerA和brokerC兩個節點繼續向producer提供接收訊息的服務。
但從監控指標的資料展示來分析當brokerB節點出現故障後topic整體流量掉零與我們的猜想大相徑庭。
既然是出現類似了服務雪崩的效應導致了部分topic的整體流量幾乎掉零那麼我們在猜想問題發生的原因時就可以往資源隔離的方向去思考,看看在整個過程中還有哪些地方涉及到資源隔離的環節進行猜想。
Kafka 服務端我們按照資源組的方式做了 Kafka broker的邏輯隔離且從Grafana監控上可以看出有一些topic的流量並沒有嚴重掉零的情況,那麼我們暫時將分析問題的目光轉移到 Kafka client端,去分析 Kafka producer的傳送訊息的過程是否存在有資源隔離地方沒有做隔離導致了整體的雪崩效應。
六、Kafka 預設分割槽器的分割槽規則
對 Kafka 生產流程流程有一定了解的同學肯定知道,Kafka 作為了大資料生態中海量資料的訊息中介軟體,為了解決海量資料的併發問題 Kafka 在設計之初就採用了客戶端緩衝訊息,當訊息達到一定批量時再進行批量訊息的傳送。
通過一次網路IO將批量的資料傳送到 Kafka 服務端。關於Kafka producer客戶端緩衝區的設計小編後續會單獨一個篇幅進行深入的探索,鑑於篇幅問題不再此處進行詳細分析。
基於此處的分析我們對一批訊息傳送到一個故障節點時的容錯方案可以有以下猜想:
快速失敗,記錄故障節點資訊。下次進行訊息路由時只路由到健康的節點上。快速釋放訊息緩衝記憶體。
快速失敗,記錄故障節點資訊,下次進行訊息路由時當訊息路由到故障節點上時直接報錯,快速釋放緩衝區記憶體。
等待超時,當次訊息等待超時後,下次進行訊息路由時依然會出現路由到故障節點上的情況,且每次等待超時時間後才釋放佔用的資源。
上述猜想中,如果是第一種情況,那麼每次訊息路由只路由到健康的節點上不會出現雪崩效應耗盡客戶端緩衝區資源的情況;
第二種情況,當訊息路由到故障節點上時,直接拒絕分配緩衝區資源也不會造成雪崩效應;
第三種情況,每次需要在一個或多個超時時間後才能將故障節點所佔用的客戶端緩衝區資源釋放,在海量訊息傳送的場景下一個超時時間週期內故障節點上的訊息足以將客戶端緩衝區資源耗盡,導致其他可用分割槽無法分配客戶端緩衝區資源導致出現雪崩效應。
帶著上述的猜想開啟kafka client producer的原始碼分析下defaultPartitioner的分割槽規則得到如下的分配邏輯:
傳送訊息時是否指定了分割槽,若指定了分割槽那訊息就直接發往該分割槽無需重新路由分割槽。
訊息是否指定了key,若訊息指定了key,使用key的hash值與topic的分割槽數進行模運算,得出訊息路由的分割槽號(對應第三種猜想)。
訊息未指定分割槽也未指定key,使用自增變數與topic的可用分割槽進行模運算,得出訊息路由的分割槽號(對應第一種猜想)。
七、總結
-
從原始碼中分析出若傳送訊息的時候指定了key,並使用的是 Kafka producer預設的分割槽分配器請款下會出現 Kafka producer 客戶端緩衝區資源被耗盡而出現topic所有分割槽雪崩效應。
-
跟業務系統同學瞭解了他們的傳送邏輯確實在訊息傳送指定了key並使用的是 Kafka producer的預設分割槽分配器。
-
問題得到論證。
八、建議
-
若非必要傳送訊息時不要指定key,否則可能會出現topic所有分割槽雪崩效應。
-
若確實需要傳送訊息指定key,建議不要使用Kafka producer預設的分割槽分配器,因為指定key的情況下使用 Kafka producer的預設分割槽分配器會出現雪崩效應。
九、擴充套件問題思考
-
為什麼 Kafka producer提供的預設分割槽分配器要單獨將指定key的情況採用topic所有分割槽進行模運算而在未指定key的採用是自增變數和可用分割槽進行模運算?
-
文章中分析的問題均為客戶端緩衝區的粒度是producer例項級別的即一個producer共用一塊記憶體緩衝區是否可以將緩衝區的粒度調整到分割槽級?
關於這系列的問題思考與分析,我們將在後續的文章中講述,敬請關注。