10 張圖,5 個問題帶你徹底理解 Kafka 架構調優

大家好,我是君哥, 今天給大家分享 Kafka  架構調優的技術。

這篇文章乾貨很多,希望你可以耐心讀完。

1 瞭解 Kafka 超高併發網絡架構是如何設計嗎?

我們知道 Kafka 網絡通信架構使用到了 Java NIO 以及 Reactor 設計模式。我們先從整體上看一下完整的網絡通信層架構,如下圖所示:

1)從上圖中我們可以看出,Kafka 網絡通信架構中用到的組件主要由兩大部分構成:SocketServer 和 RequestHandlerPool

2)SocketServer 組件是 Kafka 超高併發網絡通信層中最重要的子模塊。它包含 Acceptor 線程、Processor 線程和 RequestChannel 等對象,都是網絡通信的重要組成部分。

3)RequestHandlerPool 組件就是我們常說的 I/O 工作線程池,裏面定義了若干個 I/O 線程,主要用來執行真實的請求處理邏輯

01 Accept 線程

在經典的 Reactor 設計模式有個「Dispatcher」的角色,主要用來接收外部請求並分發給下面的實際處理線程。在 Kafka 網絡架構設計中,這個 Dispatcher 就是「Acceptor 線程」, 用來接收和創建外部 TCP 連接的線程。在 Broker 端每個 SocketServer 實例只會創建一個 Acceptor 線程。它的主要功能就是創建連接,並將接收到的 Request 請求傳遞給下游的 Processor 線程處理

1)我們可以看出 Acceptor 線程主要使用了 Java NIO 的 Selector 以及 SocketChannel 的方式循環的輪詢準備就緒的 I/O 事件。

2)將 ServerSocketChannel 通道註冊到 nioSelector 上,並關注網絡連接創事件:SelectionKey.OP_ACCEPT。

3)事件註冊好後,一旦後續接收到連接請求後,Acceptor 線程就會指定一個 Processor 線程,並將該請求交給它並創建網絡連接用於後續處理。

02 Processor 線程

Acceptor 只是做了請求入口連接處理的,那麼,真正創建網絡連接以及分發網絡請求是由 Processor 線程來完成的。而每個 Processor 線程在創建時都會創建 3 個隊列。

1)**newConnections 隊列: **它主要是用來保存要創建的新連接信息,也就是 SocketChannel 對象,目前是硬編碼隊列長度大小爲 20。每當 Processor 線程接收到新的連接請求時,都會將對應的 SocketChannel 對象放入隊列,等到後面創建連接時,從該隊列中獲取 SocketChannel,然後註冊新的連接。

2)**inflightResponse 隊列:**它是一個臨時的 Response 隊列,當 Processor 線程將 Repsonse 返回給 Client 之後,要將 Response 放入該隊列。它存在的意義:由於有些 Response 回調邏輯要在 Response 被髮送回 Request 發送方後,才能執行,因此需要暫存到臨時隊列

3)**ResponseQueue 隊列:**它主要是存放需要返回給 Request 發送方的所有 Response 對象。每個 Processor 線程都會維護自己的 Response 隊列。

03 RequestHandlerPool 線程池

Acceptor 線程和 Processor 線程只是請求和響應的「搬運工」,而「真正處理 Kafka 請求」是 KafkaRequestHandlerPool 線程池,在上面網絡超高併發通信架構圖,有兩個參數跟整個流程有關係,分別是「num.network.threads」、「num.io.threads」。其中 num.io.threads 就是 I/O 工作線程池的大小配置。

               

下面我們結合 Kafka 超高併發網絡架構圖來講解下一個完整請求處理核心流程:

1)Clients 發送請求給 Acceptor 線程。

2)Acceptor 線程會創建 NIO Selector 對象,並創建 ServerSocketChannel 通道實例,然後將 Channel 和 OP_ACCEPT 事件綁定到 Selector 多路複用器上。

3)Acceptor 線程默認創建 3 個 Processor 線程參數:num.network.threads, 並輪詢的將請求對象 SocketChannel 放入到連接隊列中。

4)這時候連接隊列就源源不斷有請求數據了,然後不停地執行 NIO Poll, 獲取對應 SocketChannel 上已經準備就緒的 I/O 事件。

5)Processor 線程向 SocketChannel 註冊了 OP_READ/OP_WRITE 事件,這樣 客戶端發過來的請求就會被該 SocketChannel 對象獲取到,具體就是 processCompleteReceives 方法。

6)這個時候客戶端就可以源源不斷進行請求發送了,服務端通過 Selector NIO Poll 不停的獲取準備就緒的 I/O 事件。

7)然後根據 Channel 中獲取已經完成的 Receive 對象,構建 Request 對象,並將其存入到 Requestchannel 的 RequestQueue 請求隊列中 。

8)這個時候就該 I/O 線程池上場了,KafkaRequestHandler 線程循環地從請求隊列 RequestQueue 中獲取 Request 實例,然後交由 KafkaApis 的 handle 方法,執行真正的請求處理邏輯,並最終將數據存儲到磁盤中。

9)待處理完請求後,KafkaRequestHandler 線程會將 Response 對象放入 Processor 線程的 Response 隊列。

10)然後 Processor 線程通過 Request 中的 ProcessorID 不停地從 Response 隊列中來定位並取出 Response 對象,返還給 Request 發送方。

2 瞭解 Kafka 高吞吐日誌存儲架構是如何設計嗎?

對於 Kafka 來說, 它主要用來處理海量數據流,這個場景的特點主要包括:

  1. 寫操作: 寫併發要求非常高,基本得達到百萬級 TPS,順序追加寫日誌即可,無需考慮更新操作。

2)讀操作: 相對寫操作來說,比較簡單,只要能按照一定規則高效查詢即可, 支持(offset 或者時間戳)讀取。

根據上面兩點分析,對於寫操作來說,直接採用「順序追加寫日誌」的方式就可以滿足 Kafka 對於百萬 TPS 寫入效率要求。

如何解決高效查詢這些日誌呢?我們可以設想把消息的 Offset 設計成一個有序的字段,這樣消息在日誌文件中也就有序存放了,也不需要額外引入哈希表結構,可以直接將消息劃分成若干個塊,對於每個塊我們只需要索引當前塊的第一條消息的 Offset ,這個是不是有點二分查找算法的意思。即先根據 Offset 大小找到對應的塊, 然後再從塊中順序查找。如下圖所示:

這樣就可以快速定位到要查找的消息的位置了,在 Kafka 中,我們將這種索引結構叫做「稀疏哈希索引」。

上面得出了 Kafka 最終的存儲實現方案, 即基於順序追加寫日誌 + 稀疏哈希索引。

接下來我們來看看 Kafka 日誌存儲結構:

從上圖看出來,Kafka 是基於「主題 + 分區 + 副本 + 分段 + 索引」的結構進行日誌存儲的。

瞭解了整體的日誌存儲架構,我們來看下 Kafka 日誌格式,Kafka 日誌格式也經歷了多個版本迭代,這裏我們主要看下 V2 版本的日誌格式:

通過上圖可以得出:V2 版本日誌格式主要是通過可變長度提高了消息格式的空間使用率,並將某些字段抽取到消息批次(RecordBatch)中,同時消息批次可以存放多條消息,從而在批量發送消息時,可以大幅度地節省了磁盤空間。

接下來我們來看看日誌消息寫入磁盤的整體過程如下圖所示:

3 針對 Kafka 線上集羣部署方案, 你是怎麼做的?

這裏我們從架構師必備能力出發, 以電商平臺爲例講述了 Kafka 生產級容量評估方案該如何做?如何讓公司領導以及運維部門得到認可, 獲准你的方案。

詳細可以深讀:八大步驟帶你深度剖析 Kafka 生產級容量評估方案

4 針對 Kafka 線上系統, 你是如何進行監控的?

Kafka 作爲大型系統架構中重要的一環,有着舉足輕重的作用,因此 Kafka 集羣的穩定性尤爲重要,我們要對生產的 Kafka 集羣進行全方位的監控, 一般線上系統可以從以下五個維度進行監控:

01 主機節點監控

所謂主機節點監控就是監控 Kafka 集羣 Broker 所在節點機器的性能。主機節點監控對於 Kafka 來說是最重要的,因爲很多線上環境問題首先就是由於主機的某些性能出現了問題。

因此對於 Kafka 來說,主機監控通常是發現問題的第一步,主要性能指標如下:

機器負載(Load)」、「CPU 使用率」、「內存使用率」、「磁盤 I/O 使用率」、「網絡 I/O 使用率」、「TCP 連接數」、「打開文件數」、「inode 使用情況」。

如果想要更好的監控主機性能的話,有以下兩個教程可以學習和參考:

02 JVM 監控

另一個重要的監控維度就是 JVM 監控。監控 JVM 進程主要是爲了讓你全面地瞭解 Kafka Broker 進程

要監控 JVM 進程,需要關注 3 個指標:

監控 Full GC 發生頻率和時長」、「監控堆上活躍對象大小」、「監控****應用線程總數

03 Kafka 集羣監控

另外一個重要監控維度就是 Kafka Broker 集羣和各類客戶端的監控,主要有 3 個方法:

1)**查看 Broker 端重要日誌:**主要包括 Broker 端服務器日誌 server.log,控制器日誌 controller.log 以及主題分區狀態變更日誌 state-change.log。其中,server.log 是最重要的,如果你的 Kafka 集羣出現了故障,你要第一時間查看 server.log,定位故障原因。

2)查看 Broker 端關鍵線程運行狀態,例如**: **

Log Compaction 線程:日誌壓縮清理。一旦它掛掉了,所有 Compaction 操作都會中斷,但用戶對此通常是無感知的。

副本拉取消息的線程:主要執行 Follower 副本向 Leader 副本拉取消息的邏輯。如果它們掛掉了,系統會表現爲 Follower 副本延遲 Leader 副本越來越大 。

3)**‍‍‍‍‍‍‍‍‍‍‍‍查看 Broker 端關鍵的 JMX 性能指標: **主要有 BytesIn/BytesOut、NetworkProcessorAvgIdlePercent、RequestHandlerAvgIdlePercent、UnderReplicatedPartitions、ISRShrink/ISRExpand、ActiveControllerCount 這幾個指標 。

04 Kafka 客戶端監控

客戶端監控主要是生產者和消費者的監控,生產者往 Kafka 發送消息,此時我們要了解客戶端機器與 Broker 機器之間的往返時延 RTT 是多少,對於跨數據中心或者異地集羣來說,RTT 會更大,很難支撐很大的 TPS。

**Producer 角度: **request-latency 是需要重點關注的 JMX 指標,即消息生產請求的延時;另外 Sender 線程的運行狀態也是非常重要的, 如果 Sender 線程掛了,對於用戶是無感知的,表象只是 Producer 端消息發送失敗。

Consumer 角度: 對於 Consumer Group,需要重點關注 join rate 和 sync rate 指標,它表示 Rebalance 的頻繁程度。另外還包括消息消費偏移量、消息堆積數量等。

05 Broker 之間的監控

最後一個監控維度就是 Broker 之間的監控,主要指副本拉取的性能。Follower 副本實時拉取 Leader 副本的數據,此時我們希望拉取過程越快越好。Kafka 提供了一個特別重要的 JMX 指標,叫做「under replicated partitions」,意思就是比如我們規定這條消息,應該在兩個 Broker 上面保存,假設只有一個 Broker 上保存該消息,那麼這條消息所在的分區就叫 under replicated partitions,這種情況是特別關注的,因爲有可能造成數據的丟失。

另外還有一個比較重要的指標是「active controllor count」。在整個 Kafka 集羣中應該確保只能有一臺機器的指標是 1,其他全應該是 0,如果發現有一臺機器大於 1,一定是出現腦裂了,此時應該去檢查下是否出現了網絡分區。Kafka 本身是不能對抗腦裂的,完全依靠 Zookeeper 來做,但是如果真正出現網絡分區的話,也是沒有辦法處理的,應該讓其快速失敗重啓。

5 針對 Kafka 線上系統, 你是如何進行調優的?

對 Kafka 來說,「吞吐量」和「延時」是非常重要的優化指標。

吞吐量 TPS: 是指 Broker 端或 Client 端每秒能處理的消息數,越大越好。

延時: 表示從 Producer 端發送消息到 Broker 端持久化完成到 Consumer 端成功消費之間的時間間隔。與吞吐量 TPS 相反,延時越短越好。

總之,高吞吐量、低延時是我們調優 Kafka 集羣的主要目標

01 提升吞吐量

首先是提升吞吐量參數和措施:

XKyeza

02 降低延時

降低延時的目的就是儘量減少端到端的延時。

對比上面提升吞吐量的參數,我們只能調整 Producer 端和 Consumer 端的參數配置。

對於 Producer 端,此時我們希望可以快速的將消息發送出去,必須設置 linger.ms=0,同時關閉壓縮,另外設置 acks = 1,減少副本同步時間。

而對於 Consumer 端我們只保持 fetch.min.bytes=1 ,即 Broker 端只要有能返回的數據,就立即返回給 Consumer,減少延時。

03 合理設置分區數

分區數不是越多越好,也不是越少越好,需要搭建完集羣,進行壓測,再靈活調整分區個數。

這裏可以用 Kafka 官方自帶的腳本,對 Kafka 進行壓測。

1)生產者壓測: kafka-producer-perf-test.sh

2)消費者壓測: kafka-consumer-perf-test.sh 

華仔聊技術 聊聊後端技術架構以及中間件源碼

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/I9psJJvVOLH1hO-7zKSNbQ