深入淺出系列之 -- kafka調優

背景引入:很多同學看不懂kafka參數

    今天給大家聊一個很有意思的話題,大家知道很多公司都會基於Kafka作爲MQ來開發一些複雜的大型系統。而在使用Kafka的客戶端編寫代碼與服務器交互的時候,是需要對客戶端設置很多的參數的。所以我就見過很多同學,可能剛剛加入團隊,對Kafka這個技術其實並不是很瞭解。此時就會導致他們看團隊裏的一些資深同事寫的一些代碼,會看不懂是怎麼回事,不瞭解背後的含義,這裏面尤其是一些Kafka參數的設置

    所以這篇文章,我們還是採用老規矩畫圖的形式,來聊聊Kafka生產端一些常見參數的設置,讓大家下次看到一些Kafka客戶端設置的參數時,不會再感到發怵。

下面根據一段java代碼進行調優:

一段Kafka生產端的示例代碼

Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("buffer.memory", 67108864);
props.put("batch.size", 131072);
props.put("linger.ms", 100);
props.put("max.request.size", 10485760);
props.put("acks", "1");
props.put("retries", 10);
props.put("retry.backoff.ms", 500);

KafkaProducer<String, String> producer = new KafkaProducer<String, String>(props);

內存緩衝的大小

首先我們看看“buffer.memory”這個參數是什麼意思?

    Kafka的客戶端發送數據到服務器,一般都是要經過緩衝的,也就是說,你通過KafkaProducer發送出去的消息都是先進入到客戶端本地的內存緩衝裏,然後把很多消息收集成一個一個的Batch,再發送到Broker上去的

所以這個“buffer.memory”的本質就是用來約束KafkaProducer能夠使用的內存緩衝的大小的,他的默認值是32MB。那麼既然瞭解了這個含義,大家想一下,在生產項目裏,這個參數應該怎麼來設置呢?你可以先想一下,如果這個內存緩衝設置的過小的話,可能會導致一個什麼問題?

首先要明確一點,那就是在內存緩衝裏大量的消息會緩衝在裏面,形成一個一個的Batch,每個Batch裏包含多條消息。

然後KafkaProducer有一個Sender線程會把多個Batch打包成一個Request發送到Kafka服務器上去。

那麼如果要是內存設置的太小,可能導致一個問題消息快速的寫入內存緩衝裏面,但是Sender線程來不及把Request發送到Kafka服務器

這樣是不是會造成內存緩衝很快就被寫滿?一旦被寫滿,就會阻塞用戶線程,不讓繼續往Kafka寫消息了。

所以對於“buffer.memory”這個參數應該結合自己的實際情況來進行壓測,你需要測算一下在生產環境,你的用戶線程會以每秒多少消息的頻率來寫入內存緩衝。

比如說每秒300條消息,那麼你就需要壓測一下,假設內存緩衝就32MB,每秒寫300條消息到內存緩衝,是否會經常把內存緩衝寫滿?經過這樣的壓測,你可以調試出來一個合理的內存大小。

多少數據打包爲一個Batch合適?

接着你需要思考第二個問題,就是你的“batch.size”應該如何設置?這個東西是決定了你的每個Batch要存放多少數據就可以發送出去了。

比如說你要是給一個Batch設置成是16KB的大小,那麼裏面湊夠16KB的數據就可以發送了。這個參數的默認值是16KB,一般可以嘗試把這個參數調節大一些,然後利用自己的生產環境發消息的負載來測試一下。

比如說發送消息的頻率就是每秒300條,那麼如果比如“batch.size”調節到了32KB,或者64KB,是否可以提升發送消息的整體吞吐量。

因爲理論上來說,提升batch的大小,可以允許更多的數據緩衝在裏面,那麼一次Request發送出去的數據量就更多了,這樣吞吐量可能會有所提升。

但是這個東西也不能無限的大,過於大了之後,要是數據老是緩衝在Batch裏遲遲不發送出去,那麼豈不是你發送消息的延遲就會很高。

比如說,一條消息進入了Batch,但是要等待5秒鐘Batch才湊滿了64KB,才能發送出去。那這條消息的延遲就是5秒鐘。

所以需要在這裏按照生產環境的發消息的速率,調節不同的Batch大小自己測試一下最終出去的吞吐量以及消息的 延遲,設置一個最合理的參數。

要是一個Batch遲遲無法湊滿怎麼辦?

要是一個Batch遲遲無法湊滿,此時就需要引入另外一個參數了,“linger.ms

他的含義就是說一個Batch被創建之後,最多過多久,不管這個Batch有沒有寫滿,都必須發送出去了

     給大家舉個例子,比如說batch.size是16kb,但是現在某個低峯時間段,發送消息很慢。這就導致可能Batch被創建之後,陸陸續續有消息進來,但是遲遲無法湊夠16KB,難道此時就一直等着嗎?當然不是,假設你現在設置“linger.ms”是50ms,那麼只要這個Batch從創建開始到現在已經過了50ms了,哪怕他還沒滿16KB,也要發送他出去了。所以“linger.ms”決定了你的消息一旦寫入一個Batch,最多等待這麼多時間,他一定會跟着Batch一起發送出去。避免一個Batch遲遲湊不滿,導致消息一直積壓在內存裏發送不出去的情況。這是一個很關鍵的參數。  

這個參數一般要非常慎重的來設置,要配合batch.size一起來設置。

舉個例子,首先假設你的Batch是32KB,那麼你得估算一下,正常情況下,一般多久會湊夠一個Batch,比如正常來說可能20ms就會湊夠一個Batch。

那麼你的linger.ms就可以設置爲25ms,也就是說,正常來說,大部分的Batch在20ms內都會湊滿,但是你的linger.ms可以保證,哪怕遇到低峯時期,20ms湊不滿一個Batch,還是會在25ms之後強制Batch發送出去。

如果要是你把linger.ms設置的太小了,比如說默認就是0ms,或者你設置個5ms,那可能導致你的Batch雖然設置了32KB,但是經常是還沒湊夠32KB的數據,5ms之後就直接強制Batch發送出去,這樣也不太好其實,會導致你的Batch形同虛設,一直湊不滿數據。

最大請求大小

“max.request.size”這個參數決定了每次發送給Kafka服務器請求的最大大小,同時也會限制你一條消息的最大大小也不能超過這個參數設置的值,這個其實可以根據你自己的消息的大小來靈活的調整。

給大家舉個例子,你們公司發送的消息都是那種大的報文消息,每條消息都是很多的數據,一條消息可能都要20KB。

此時你的batch.size是不是就需要調節大一些?比如設置個512KB?然後你的buffer.memory是不是要給的大一些?比如設置個128MB?

只有這樣,才能讓你在大消息的場景下,還能使用Batch打包多條消息的機制。但是此時“max.request.size”是不是也得同步增加?

因爲可能你的一個請求是很大的,默認他是1MB,你是不是可以適當調大一些,比如調節到5MB?

重試機制

“retries”和“retries.backoff.ms”決定了重試機制,也就是如果一個請求失敗了可以重試幾次,每次重試的間隔是多少毫秒。

這個大家適當設置幾次重試的機會,給一定的重試間隔即可,比如給100ms的重試間隔。

持久化機制

“acks”參數決定了發送出去的消息要採用什麼樣的持久化策略,這個涉及到了很多其他的概念,這裏就不再提了。

 

個人總結:

    Kafka調優通常可以從4個維度展開,分別是吞吐量延遲持久性可用性。在具體展開這些方面之前,我想先建議用戶保證客戶端與服務器端版本一致。如果版本不一致,就會出現向下轉化的問題。舉個例子,服務器端保存高版本的消息,當低版本消費者請求數據時,服務器端就要做轉化,先把高版本消息轉成低版本再發送給消費者。這件事情本身就非常非常低效。很多文章都討論過Kafka速度快的原因,其中就談到了零拷貝技術——即數據不需要在頁緩存和堆緩存中來回拷貝。

    簡單來說producer把生產的消息放到頁緩存上,如果兩邊版本一致,可以直接把此消息推給Consumer,或者Consumer直接拉取,這個過程是不需要把消息再放到堆緩存。但是你要做向下轉化或者版本不一致的話,就要額外把數據再堆上,然後再放回到Consumer上,速度特別慢。

 

1.Kafka調優 – 吞吐量

    調優吞吐量就是我們想用更短的時間做更多的事情。這裏列出了客戶端需要調整的參數。producer是把消息放在buffer緩存區,後端Sender線程從緩存區拿出來發到broker。這裏面涉及到一個打包的過程,它是批處理的操作,不是一條一條發送的。因此這個包的大小就和TPS(系統吞吐量)息息相關。通常情況下調大這個值都會讓TPS(系統吞吐量)提升,但是也不會無限制的增加。不過調高此值的劣處在於消息延遲的增加。除了1)調整batch.size2)設置壓縮也可以提升TPS(系統吞吐量),它能夠減少網絡傳輸IO。當前Lz4的壓縮效果是最好的,如果客戶端機器CPU資源很充足那麼建議開啓壓縮。

    對於消費者端而言,調優TPS(系統吞吐量)並沒有太好的辦法,能夠想到的就是調整fetch.min.bytes。適當地增加該參數的值能夠提升consumer端的TPS。對於Broker端而言,通常的瓶頸在於副本拉取消息時間過長,因此可以適當地增加num.replica.fetcher值,利用多個線程同時拉取數據,可以加快這一進程。

 

2.Kafka調優 – 延時

    所謂的延時就是指消息被處理的時間。某些情況下我們自然是希望越快越好。針對這方面的調優,consumer端能做的不多,簡單保持fetch.min.bytes默認值即可,這樣可以保證consumer能夠立即返回讀取到的數據。說到這裏,可能有人會有這樣的疑問:TPS和延時不是一回事嗎?假設發一條消息延時是2ms,TPS自然就是500了,因爲一秒只能發500消息,其實這兩者關係並不是簡單的。因爲我發一條消息2毫秒,但是如果把消息緩存起來統一發,TPS會提升很多。假設發一條消息依然是2ms,但是我先等8毫秒,在這8毫秒之內可能能收集到一萬條消息,然後我再發。相當於你在10毫秒內發了一萬條消息,大家可以算一下TPS是多少。事實上,Kafka producer在設計上就是這樣的實現原理。

 

3.Kafka調優 –消息持久性

    消息持久化本質上就是消息不丟失。Kafka對消息不丟失的承諾是有條件的。以前碰到很多人說我給Kafka發消息,發送失敗,消息丟失了,怎麼辦?嚴格來說Kafka不認爲這種情況屬於消息丟失,因爲此時消息沒有放到Kafka裏面。Kafka只對已經提交的消息做有條件的不丟失保障。

    如果要調優持久性,對於producer而言,首先要設置重試以防止因爲網絡出現瞬時抖動造成消息發送失敗。一旦開啓了重試,還需要防止亂序的問題。比如說我發送消息1與2,消息2發送成功,消息1發送失敗重試,這樣消息1就在消息2之後進入Kafka,也就是造成亂序了。如果用戶不允許出現這樣的情況,那麼還需要顯式地設置max.in.flight.requests.per.connection爲1。

官網解釋:

max.in.flight.requests.per.connection: 客戶端在阻止之前將在單個連接上發送的最大未確認請求數。請注意,如果此設置設置爲大於1並且發送失敗,則存在由於重試而導致消息重新排序的風險(即,如果啓用了重試)。

上圖列出的其他參數都是很常規的參數,比如unclean.leader.election.enable參數,最好還是將其設置成false,即不允許“髒”副本被選舉爲leader。

 

4.Kafka調優 –可用性

最後是可用性,與剛纔的持久性是相反的,我允許消息丟失,只要保證系統高可用性即可。因此我需要把consumer心跳超時設置爲一個比較小的值,如果給定時間內消費者沒有處理完消息,該實例可能就被踢出消費者組。我想要其他消費者更快地知道這個決定,因此調小這個參數的值。

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

      考慮一千次,不如去做一次;猶豫一萬次,不如實踐一次;華麗的跌倒,勝過無謂的彷徨,將來的你,一定會感謝現在奮鬥的你。歡迎大家加入大數據交流羣:725967421     一起交流,一起進步!!

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

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