解決 kafka 客戶端緩衝機制造成的頻繁GC問題 寫的很透徹!

1、Kafka的客戶端緩衝機制
2、內存緩衝造成的頻繁GC問題
3、Kafka設計者實現的緩衝池機制
4、總結一下

“ 這篇文章,給大家聊一個硬核的技術知識,我們通過Kafka內核源碼中的一些設計思想,來看你設計Kafka架構的技術大牛,是怎麼優化JVM的GC問題的?

1、Kafka的客戶端緩衝機制
首先,先得給大家明確一個事情,那就是在客戶端發送消息給kafka服務器的時候,一定是有一個內存緩衝機制的。

也就是說,消息會先寫入一個內存緩衝中,然後直到多條消息組成了一個Batch,纔會一次網絡通信把Batch發送過去。

整個過程如下圖所示:
在這裏插入圖片描述

2、內存緩衝造成的頻繁GC問題
那麼這種內存緩衝機制的本意,其實就是把多條消息組成一個Batch,一次網絡請求就是一個Batch或者多個Batch。

這樣每次網絡請求都可以發送很多數據過去,避免了一條消息一次網絡請求。從而提升了吞吐量,即單位時間內發送的數據量。

但是問題來了,大家可以思考一下,一個Batch中的數據,會取出來然後封裝在底層的網絡包裏,通過網絡發送出去到達Kafka服務器。

那麼然後呢?

這個Batch裏的數據都發送過去了,現在Batch裏的數據應該怎麼處理?

你要知道,這些Batch裏的數據此時可還在客戶端的JVM的內存裏啊!那麼此時從代碼實現層面,一定會嘗試避免任何變量去引用這些Batch對應的數據,然後嘗試觸發JVM自動回收掉這些內存垃圾。

這樣不斷的讓JVM回收垃圾,就可以不斷的清理掉已經發送成功的Batch了,然後就可以不斷的騰出來新的內存空間讓後面新的數據來使用。

這種想法很好,但是實際線上運行的時候一定會有問題,最大的問題,就是JVM GC問題。

大家都知道一點,JVM GC在回收內存垃圾的時候,他會有一個“Stop the World”的過程,也就是垃圾回收線程運行的時候,會導致其他工作線程短暫的停頓,這樣可以便於他自己安安靜靜的回收內存垃圾。

這個也很容易想明白,畢竟你要是在回收內存垃圾的時候,你的工作線程還在不斷的往內存裏寫數據,製造更多的內存垃圾,那你讓人家JVM怎麼回收垃圾?

這就好比在大馬路上,如果地上有很多垃圾,現在要把垃圾都掃乾淨,最好的辦法是什麼?大家都讓開,把馬路空出來,然後清潔工就是把垃圾清理乾淨。

但是如果清潔工在清掃垃圾的時候,結果一幫人在旁邊不停的嗑瓜子扔瓜子殼,喫西瓜扔西瓜皮,不停的製造垃圾,你覺得清潔工內心啥感受?當然是很憤慨了,照這麼搞,地上的垃圾永遠的都搞不乾淨了!

通過了上面的語言描述,我們再來一張圖,大家看看就更加清楚了
在這裏插入圖片描述

現在JVM GC是越來越先進,從CMS垃圾回收器到G1垃圾回收器,核心的目標之一就是不斷的縮減垃圾回收的時候,導致其他工作線程停頓的時間。

所以現在越是新款的垃圾回收器導致工作線程停頓的時間越短,但是再怎麼短,他也還是存在啊!

所以說,如何儘可能在自己的設計上避免JVM頻繁的GC就是一個非常考驗水平的事兒了。

3、Kafka設計者實現的緩衝池機制
在Kafka客戶端內部,對這個問題實現了一個非常優秀的機制,就是緩衝池的機制

簡單來說,就是每個Batch底層都對應一塊內存空間,這個內存空間就是專門用來存放寫入進去的消息的。

然後呢,當一個Batch被髮送到了kafka服務器,這個Batch的數據不再需要了,就意味着這個Batch的內存空間不再使用了。

此時這個Batch底層的內存空間不要交給JVM去垃圾回收,而是把這塊內存空間給放入一個緩衝池裏。

這個緩衝池裏放了很多塊內存空間,下次如果你又有一個新的Batch了,那麼不就可以直接從這個緩衝池裏獲取一塊內存空間就ok了?

然後如果一個Batch發送出去了之後,再把內存空間給人家還回來不就好了?以此類推,循環往復。

同樣,聽完了上面的文字描述,再來一張圖,看完這張圖相信大夥兒就明白了:
在這裏插入圖片描述

一旦使用了這個緩衝池機制之後,就不涉及到頻繁的大量內存的GC問題了。

爲什麼呢?因爲他可以上來就佔用固定的內存,比如32MB。然後把32MB劃分爲N多個內存塊,比如說一個內存塊是16KB,這樣的話這個緩衝池裏就會有很多的內存塊。

然後你需要創建一個新的Batch,就從緩衝池裏取一個16KB的內存塊就可以了,然後這個Batch就不斷的寫入消息,但是最多就是寫16KB,因爲Batch底層的內存塊就16KB。

接着如果Batch被髮送到Kafka服務器了,此時Batch底層的內存塊就直接還回緩衝池就可以了。

下次別人再要構建一個Batch的時候,再次使用緩衝池裏的內存塊就好了。這樣就可以利用有限的內存,對他不停的反覆重複的利用。因爲如果你的Batch使用完了以後是把內存塊還回到緩衝池中去,那麼就不涉及到垃圾回收了。

如果沒有頻繁的垃圾回收,自然就避免了頻繁導致的工作線程的停頓了,JVM GC問題是不是就得到了大幅度的優化?

沒錯,正是這個設計思想讓Kafka客戶端的性能和吞吐量都非常的高,這裏蘊含了大量的優秀的機制。

那麼此時有人說了,如果我現在把一個緩衝池裏的內存資源都佔滿了,現在緩衝池裏暫時沒有內存塊了,怎麼辦呢?

很簡單,阻塞你的寫入操作,不讓你繼續寫入消息了。把你給阻塞住,不停的等待,直到有內存塊釋放出來,然後再繼續讓你寫入消息。

4、總結一下
這篇文章我們從Kafka內存緩衝機制的設計思路開始,一直分析到了JVM GC問題的產生原因以及惡劣的影響。

接着談到了Kafka優秀的緩衝池機制的設計思想以及他是如何解決這個問題的,分析了很多Kafka作者在設計的時候展現出的優秀的技術設計思想和能力。

希望大家多吸取這裏的精華,在以後面試或者工作的時候,可以把這些優秀的思想納爲己用。

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