KAFKA事件:【十八】我們可以調整哪些參數來提高生產者的吞吐量?

大家好,這是一個爲了夢想而保持學習的博客。這個專題會記錄我對於KAFKA的學習和實戰經驗,希望對大家有所幫助,目錄形式依舊爲問答的方式,相當於是模擬面試。

前言

上一篇中,我們講了kafka-java-producer的架構原理,整體圖如下:

在瞭解了架構原理以及相關參數的作用之後,我們來思考一下我們生產環境下,如何提高生產者的生產速率,也就是提高生產者的吞吐量。
上面這一張圖很重要,我們後續會按照這張圖進行分析和整理。


線程安全的生產者客戶端

首先,我們之前提到,kafka-java-producer客戶端是線程安全的,也就是上面的主線程那一塊,是做了併發控制的。 因此,我們業務在使用的時候,就可以只創建一個producer對象,然後在全局進行使用。

常見的producer的使用問題:

  • 每次業務使用的時候都重新new一個producer去進行使用。這種使用是非常不可取的,創建producer是非常耗時也耗資源的一個操作,如果每發一個消息就new一個出來,吞吐量會極低,而且會有頻繁的GC問題,因爲之前我們提到的每創建一個produer都會申請32M的緩衝區。除此之外還可能產生其他問題,但凡瞭解一點kafka,都不應該這麼使用。
  • 爲每個業務線程都創建一個生產者。這種使用方式正常來說是可以的,但是很不優雅,也沒必要。爲什麼呢?因爲本身kafka-java-producer客戶端是線程安全的,而且性能很高,我們完全可以使用一個producer對象即可。

那正確的打開方式是如何的呢?
通常情況下,我們都會建議大家封裝一個Utils類,在其中創建一個KafkaProducer對象;業務在需要發送消息的時候呢,直接根據Utils去進行發送即可,不用在意多線程問題,因爲kafka-java-producer客戶端是線程安全。
至於是同步發送,還是異步發送,需要根據自己的業務去進行選擇。
我這邊建議的話,沒有一些嚴格消息丟失/消息順序的要求的話直接異步發送,只需要設置好回調函數即可。


緩衝區

接着,我們順着上面的思路,消息通過 interceptors、序列化器、分區器,最終會被寫到RecordAccumulator這個緩衝區中。
我們知道這個緩衝區默認是有大小限制的,32M。 如果你的業務消息非常多,那麼是有可能把緩衝區寫滿的,尤其是在你的單條消息非常大的時候(上M級別)。 所以如果存在上面這兩種可能,大家可以根據當前機器的資源情況,把這個參數設置得大一點,64M,128M都是可以的。
對應的控制參數(單位是b):buffer.memory


批處理 & linger.ms

然後,消息存在緩衝區中,會被封裝成一個個的Batch,這個Batch默認大小是16Kb。
這個參數非常關鍵,因爲它不僅僅代表kafka的批處理,還影響着上面緩衝區的內存複用單位大小。
我們通常建議大家可以適當調大這個值,尤其是在業務單條數據可能超過16kb的時候,因爲這會導致緩衝區無法複用內存,直接走內存分配,然後加重gc的負擔,從而影響吞吐量。

我們在上一節也提到過,設置了batch.size的時候,一定要設置linger.ms,否則是不生效的。
具體緣由在上一節講過了就不再重複了,但是還需要討論下linger.ms這個參數該怎麼設置,設置多大?
這個沒辦法給出一個具體的值,最好是進行壓測看一下,你設置的batch.size大概多長時間可能被寫滿,然後你就可以把linger.ms設置成那麼長的時間。
當然,如果你的業務對時延非常敏感,那麼這一小節的內容將不適用。

這一小節涉及到的參數:

  • batch.size 根據業務消息大小進行設置
  • linger.ms 最好進行壓測後再設置,一般設置個5ms、10ms也都ok,不宜太長。

max.in.flight.requests.per.connection

接着,消息被從緩衝區中取出,封裝成Request,最後在發送時,暫存在InFlightRequests中。
這裏有什麼可優化的點嗎?這其實就是這一小節的title的那個參數,這個參數是控制InFlightRequests中節點隊列的請求個數的,默認是5,也就是運行發出去5個請求沒收到響應,如果是第六個就無法發送,得等前面的超時了或者收到響應了才能被處理。

這個參數,如果不是業務要實現消息不丟失/順序的特殊要求的話,一般不建議修改它;這裏提到呢,因爲是按照消息流轉的順序進行梳理的。


回調處理

最後,消息被髮送出去了,寫入InFlightRequests。最終呢,這個發出去的請求會得到一個具體的結果:成功響應/超時。這時候呢,Sender線程就會去執行我們業務的回調。
這個回調函數的邏輯,是我們進行優化提升最明顯的地方。
我強調一下:一定不要在回調函數裏面去執行非常重的一些操作,例如更新db、rpc等。
至於原因在上一篇中有講。

那我們實在是要做這些操作怎麼辦呢?
建議進行異步操作,丟到一個線程池中去處理這些回調操作,不要影響到kafkaSender線程。


JVM參數

在講完整個消息發送流程之後,如果是java應用,不可避免的受到JVM的影響。
我之前遇到過一個case,是客戶的JVM參數設置的極不合理導致頻繁FullGC。
有多嚴重呢總共跑了3000s,GC的時間佔了2500s... 這是一個真是的按理,所以建議大家從上面的參數都根據情況設置了依舊無法有明顯的改善,那麼可以用jstat看下JVM的GC情況,以及看下JVM參數信息。


其他

最後,我們上面只是從流程方面,梳理了相關可以提升吞吐量的參數。但是實際使用中可能遇到各式各樣的問題,什麼網絡啦、帶寬啦,負載啦之類的。
如果大家要排查生產吞吐上不去的問題的話,建議順序是 :
回調函數邏輯 -> 消息大小及上面提到的參數 -> JVM -> 網絡及其他 -> 服務端

這裏最後一環是服務端,是想讓大家明白,有時候吞吐量上不去,不一定是客戶端問題,也有可能是服務端處理的問題
服務端的優化,這裏就不講了,後續有時間再整理一下。如果僅僅是排查爲什麼服務端生產請求處理慢的話,我們只需要看下服務端的PRODUCE請求處理時長即可,看下到底是哪個階段耗時比較長即可定位。

如果說,看到的耗時階段是Send之類的話,我們可以查下提到的兩個網絡參數,興許有所幫助。
send.buffer.bytes
receive.buffer.bytes

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章