Kafka 訊息遷移工具的壓測與調優

rihkddd發表於2020-10-10

壓測背景

臨近雙十一大家都免不了要對自己的業務系統進行壓測。公司一個核心業務預計雙十一會迎來數倍日常流量的業務高峰,該系統強依賴於 Kafka,Kafka 本身是分散式的系統,擴容比較方便。但是為了保證核心業務的穩定性和高可用,需要在機房故障的場景下核心業務快速恢復服務,因此 Kafka 需要跨機房熱備機制。

一般情況的 Kafka 叢集,都是在同一個 IDC 中的,跨 IDC 的熱備在 Kafka 的使用場景叫做 Mirror,Kafka 內建的支援為 MirrorMaker,現在已經進化到了第二版,下文簡稱為 MM2.

經過前期技術調研,基本確定了 MM2 的 Kafka 熱備機制。為了確定這套方案在雙十一的流量高峰情況下,能滿足訊息系統熱備的需求,需要提前做壓測。

壓測實施

MM2 的工作原理可以簡單理解為:在目的端機房消費源端機房需要複製的訊息。因此 MM2 的壓測,本質上還是 Kafka 的壓測。而幸運的是 Kafka 作為高效能、廣泛使用的訊息佇列,它本身就提供了 benchmark 工具,可以方便的使用內建的命令列進行壓測。

基本的用法如下:

./bin/kafka-producer-perf-test.sh --topic test_perf --num-records 1000000 --record-size 15000 --throughput -1 --producer-props bootstrap.servers=10.xx.xx.1:9094,10.xx.xx.2:9094 acks=1

幾個重要的引數解釋如下:

  • acks 訊息確認模式,-1 表示需要確認所有副本都寫入成功,1 表示只需確認一個副本寫入成功,0 表示無需確認。
  • --topic topic 名稱,本例為 test_perf
  • --num-records 總共需要傳送的訊息數,本例為 1000000
  • --record-size 每個記錄的位元組數,本例為 15000
  • --throughput 每秒鐘傳送的記錄數,本例為 -1,表示不做限制
  • --producer-props bootstrap.servers=localhost:9092 傳送端的配置資訊,本例只指定了 kafka 的連結資訊
  • --producer-num-retries 一個訊息失敗傳送重試次數
  • --request-timeouts-ms 一個訊息請求傳送超時時間

訊息體的大小,我們可以根據實際的業務場景構造合適的訊息大小,例如統計一批訊息的平均大小:

./bin/kafka-console-consumer.sh --bootstrap-server 10.xx.xx.1:9094,10.xx.xx.2:9094 --topic sdsoms  --max-messages=100 | wc

acks 模式需要和實際生產環境保持一致即可。

確定了訊息體大小,和 acks 之後,可以調整 num-records 和 throughput,保證有足夠的訊息數量和速率。

效能問題

在具體壓測實施過程中,遇到了不少問題。經過業務預估,雙十一業務高峰時 Kafka 需要承受的 QPS 約為 x 萬,但是初次壓測下來的資料僅能到幾千就出現了大量的跨機房複製訊息延遲。

問題分析

透過對機器負載分析發現:

部署訊息遷移服務的機器 CPU/磁碟都沒有到平靜,實際上從 MM2 訊息遷移的原理來看,它工作完全是在記憶體中完成,因此磁碟基本沒有使用。

那麼最有可能的問題就出在了網路上。

跨 IDC 的訊息同步會面臨幾個問題:

  1. IDC 之間的網路延遲(RTT)會遠高於 IDC 內的,典型的情況下機房內的延遲往往在 0.1ms 以內,而廣州到上海的 IDC 延遲一般為 30ms。
  2. 頻寬有限,且成本高。跨 IDC 的網路鋪設成本高,總頻寬有限,費用很高。機房內部雙千兆一般都是標配,萬兆也很普及,而且一般是免費使用。

經過一番分析和計算,在壓測的業務場景中,訊息體較大,達到了 15000Byte(典型的訊息應用場景中訊息體一般在幾十到幾百 Byte),這個大小在幾千的 QPS 下,頻寬就需要幾百兆大小。

為了確定是頻寬的原因,一方面同 OP 確定跨機房的頻寬上限。另一方面在兩個機房的機器上安裝 iperf3 工具測試速度。

使用 iperf3 工具測試網路速度:

# MM2目的端的機器(也就是MM2服務部署機器)操作
iperf3 -s
# MM2源端的機器操作
iperf3 -c MM2_host_ip

綜合測速結果和 OP 反饋的機房間頻寬,發現確實到了頻寬瓶頸。

所以向 OP 申請臨時調高頻寬,頻寬調高了 X 倍。

本以為,機房間頻寬增加之後,MM2 的效能應該也會有接近 X 倍的提升。但是實際情況卻是:
在效能有小幅提升後,在 CPU/磁碟/網路依然沒有到達瓶頸的情況下,再出出現了大量的跨機房訊息複製延遲。

經過一番深入分析發現,在高延遲的網路環境中,打滿頻寬不是一件顯而易見的事情:

我們知道 Kafka 使用的是 TCP 網路傳輸協議,TCP 在經過三次握手之後,開始進入的流量控制階段,在這個階段,會使用滑動視窗確定一個 RTT 時間範圍內傳送的資料量。在一個 TCP 連線中,如果網路延遲很大,那麼它實際上不是那麼容易能完全利用頻寬。

這裡引入一個概念:

頻寬時延乘積,在資料通訊中,頻寬時延乘積(英語:bandwidth-delay product;或稱頻寬延時乘積、頻寬延時積等)指的是一個資料鏈路的能力(每秒位元)與來回通訊延遲(單位秒)的乘積。其結果是以位元(或位元組)為單位的一個資料總量,等同在任何特定時間該網路線路上的最大資料量——已傳送但尚未確認的資料。

當然,在現代的高延遲、大頻寬網路中 TCP 提供了縮放因子來解決這個問題,可以避免 TCP 預設最大視窗 65535 帶來的頻寬利用不足問題。

沿著這個思路,我們對服務的網路相關引數做了一些調整,主要增加了 tcp 相關 buffer 大小,雖然帶來了一些提升,但是離我們的目標還很遠。這是因為,考慮一個線路的傳輸速率時,除了需要考慮 BDP 這個因素外,還需要考慮丟包率,滑動視窗增加,在增加傳送速率的同時,也增加了丟包重傳的成本。

效能最佳化

在頻寬上限提不上去的情況下,還想提升 QPS,那麼可行的方案就是減小訊息體大小。減小訊息體有兩個思路:

  1. 壓縮。
  2. 訊息裁剪。

壓縮

壓縮就是在訊息生產之後,進入 producer 時,進行壓縮,之後的同叢集落盤和跨叢集 MM2 複製,就都是壓縮後的訊息了。producer 和 consumer 一般都內建了訊息壓縮的支援,Kafka 支援 GZIP、Snappy、LZ4 三種壓縮演算法,從 2.1.0 開始正式支援 zstd 演算法。各種壓縮演算法之間的對比可以參考這個文章:http://zhongmingmao.me/2019/08/02/kafka-compression/

在我們的使用場景中,我們希望儘可能的提高壓縮比,降低網路傳輸量,雖然 zstd 具有最高的壓縮比,不幸的時我們使用的版本尚不支援,因此只能退而求其次選擇了 GZIP 演算法。但即使時 GZIP 也能獲得很高的壓縮比。

訊息剪裁

原來的訊息體很大主要的原因是,很多資料其實不需要,但是為了簡單方便,都放到了訊息中,透過仔細分析訊息欄位,並去除不需要的欄位,大幅減小了訊息體大小。

透過上述兩種最佳化的疊加,基本上達到了預期的壓測目標,希望訊息系統可以順利透過雙十一的考驗。


參考資料

[1] https://engineering.salesforce.com/mirrormaker-performance-tuning-63afaed12c21
[2] https://plantegg.github.io/2019/09/28/TCP--效能和傳送接收 Buffer 的關係/就是要你懂
[3] http://www.dengshenyu.com/kafka-data-mirror/
[4] https://www.slideshare.net/JiangjieQin/producer-performance-tuning-for-apache-kafka-63147600

相關文章