生產問題系列一次機房停電引發的思考

一次機房停電引發的思考

今天早上到公司的時候,接到開發反饋 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 個作用

    1. 用於配置 send 數據或 partitionFor 函數得到對應的 leader 時,最大的等待時間,默認值爲 60 秒

    2. 控制生產者可用的緩存總量,如果消息發送速度比其傳輸到服務器的快,將會耗盡 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

 

文章來自:猿天地

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