一次機房停電引發的思考
今天早上到公司的時候,接到開發反饋 DEV 環境所有接口都卡,耗時都在一分鐘以上,嚴重影響開發正常工作,然後通過網關的日誌定位到原因是因爲 kafka 集羣不可用(總共 3 個 broker,前一天晚上機房停電導致 leader 節點掛了),導致網關的反爬過濾器裏面發送 kafka 消息的代碼 kafkaTemplat.send 阻塞了 60s,當時在想這個 send 方法不是異步的嗎,爲什麼會阻塞 60s?於是查閱了一些資料,大致搞清楚了原因,這裏稍作整理,分享給可能踩坑或者以及踩過坑的同學。
版本信息
-
spring-boot:2.0.6.RELEASE
-
spring-kafka:2.1.2.RELEASE
-
kafka-clients:1.0.2
爲什麼阻塞了 60s?
首先我們知道 kafkaTemplat.send 底層是調用 KafkaProducer 的 send 方法
public Future<RecordMetadata> send(ProducerRecord<K, V> record, Callback callback) {}
根據文檔的說明[1]它是一個異步的發送方法,按道理不管如何它都不應該阻塞主線程,但實際中某些情況下會出現阻塞線程,比如 broker 未正確運行,topic 未創建等情況。具體得源碼分析參考https://www.cnblogs.com/felixzh/p/11849296.html[2]
查詢官方文檔http://kafka.apache.org/10/documentation.html[3]得知,具體阻塞多久是由 max.block.ms 參數決定的,由於我們的業務場景是高容忍消息丟失,低容忍阻塞請求,所以需要進行優化,下面簡單介紹一下 2 種優化方案。
!!!注意,以下方案只適用於高容忍消息丟失,低容忍阻塞請求業務場景
優化方案
方案 1:參數調優
-
max.block.ms 調整到 100ms,這個參數有以下 2 個作用
-
用於配置 send 數據或 partitionFor 函數得到對應的 leader 時,最大的等待時間,默認值爲 60 秒
-
控制生產者可用的緩存總量,如果消息發送速度比其傳輸到服務器的快,將會耗盡 buffer.memory 這個緩存空間。當緩存空間耗盡,其他發送調用將被阻塞,阻塞時間的閾值通過 max.block.ms 設定, 之後它將拋出一個 TimeoutException。
-
-
關閉自動重試 retries=0 默認就是 0
-
其他
-
acks=0,acks 有 4 個選項[all, -1, 0, 1] 。這裏不確定會不會阻塞 send 方法,但是高容忍消息丟失,低容忍阻塞請求的業務場景配置成 0 就好了
-
0:不保證消息的到達確認,只管發送,低延遲但是會出現消息的丟失,在某個 server 失敗的情況下,有點像 TCP
-
1:發送消息,並會等待 leader 收到確認後,一定的可靠性
-
-1 或 all:發送消息,等待 leader 收到確認,並進行復制操作後,才返回,最高的可靠性
-
-
其他參數參考 http://kafka.apache.org/10/documentation.html[4]
雖然調整一些參數,但是 kafka 集羣不可用或請求量過大時,還是對主流程有短暫的阻塞
方案 2:真異步
kafkaTemplat.send 方法其實是個假異步方法,所以需要自己實現真異步,這裏構造一個公用的線程池來處理就可以了,下面爲參考代碼
package com.qiaofang.tortoise.gateway.component;
import com.qiaofang.tortoise.gateway.config.KafkaAsyncProperties;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* kafka異步操作工具類
*
* @author chenhao
* @version 1.0
* @date 2020/7/2 3:47 下午
*/
public class KafkaAsyncUtil {
private final KafkaTemplate kafkaTemplate;
private final KafkaAsyncProperties kafkaAsyncProperties;
public KafkaAsyncUtil(KafkaTemplate kafkaTemplate, KafkaAsyncProperties kafkaAsyncProperties) {
this.kafkaTemplate = kafkaTemplate;
this.kafkaAsyncProperties = kafkaAsyncProperties;
init();
}
private ThreadPoolTaskExecutor executor;
private void init() {
executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(kafkaAsyncProperties.getThreadPoolCoreThreads());
executor.setMaxPoolSize(kafkaAsyncProperties.getThreadPoolMaxThreads());
executor.setQueueCapacity(kafkaAsyncProperties.getThreadPoolQueueSize());
executor.setThreadNamePrefix("kafka-async-util-pool-");
//高容忍消息丟失場景,工作隊列滿了之後直接丟棄
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.initialize();
}
/**
* 發送消息
*
* @param topic
* @param data
*/
public void send(String topic, Object data) {
executor.execute(() -> kafkaTemplate.send(topic, data));
}
}
/**
* kafka異步操作相關配置
* @author chenhao
* @version 1.0
* @date 2020/7/2 3:47 下午
*/
@Data
@ConfigurationProperties(prefix = "tortoise.kafka.async")
public class KafkaAsyncProperties {
/**
* core
*/
private Integer threadPoolCoreThreads = 3;
/**
* max
*/
private Integer threadPoolMaxThreads = 3;
/**
* queue大小
*/
private Integer threadPoolQueueSize = 10000;
}
有文章《關於高併發下 kafka producer send 異步發送耗時問題的分析[5]》說多線程高併發下 producer.send 的損耗比較嚴重,這個還要等到後續壓測之後再更新文章吧
參考文章
站在巨人的肩膀上
-
Kafka producer 異步發送在某些情況會阻塞主線程,使用時候慎重[6]
-
HAVENT 原創 Spring Boot + Spring-Kafka 異步配置[7]
-
關於高併發下 kafka producer send 異步發送耗時問題的分析[8]
-
http://kafka.apache.org/10/documentation.html[9]
參考資料
[1]
文檔的說明: https://github.com/apache/kafka/blob/1f2d230bfdaafb34c9be12a370ab2eb4d3016039/clients/src/main/java/org/apache/kafka/clients/producer/KafkaProducer.java#L853
[2]
https://www.cnblogs.com/felixzh/p/11849296.html
[3]
http://kafka.apache.org/10/documentation.html
[4]
http://kafka.apache.org/10/documentation.html
[5]
關於高併發下 kafka producer send 異步發送耗時問題的分析: https://www.cnblogs.com/dafanjoy/p/10292875.html
[6]
Kafka producer 異步發送在某些情況會阻塞主線程,使用時候慎重: https://www.cnblogs.com/felixzh/p/11849296.html
[7]
HAVENT 原創 Spring Boot + Spring-Kafka 異步配置: https://my.oschina.net/u/943746/blog/1928471
[8]
關於高併發下 kafka producer send 異步發送耗時問題的分析: https://www.cnblogs.com/dafanjoy/p/10292875.html
[9]
http://kafka.apache.org/10/documentation.html
文章來自:猿天地