1 提高Producer吞吐量的實踐
在實際環境中,用戶似乎總是願意用較小的延時增加的代價,去換取 TPS 的顯著提升。畢竟,從 2ms 到 10ms 的延時增加通常是可以忍受的。
事實上,Kafka Producer 就是採取了這樣的設計思想。每當 producer 發佈一個立即就發送 到 producer聚集一堆發佈後批量發送,如下圖所示:
我們可以在客戶端做一些配置,來實現producer的高吞吐量,涉及到的一些重要配置如下:
-
批次大小,它和等待時間只要有一個滿足就會發送,默認16K,可以修改爲32K~512K。
-
等待時間,它和批次大小隻要有一個滿足就會發送,建議設置爲5~100ms(根據你的場景來修改)。
-
壓縮算法,使用壓縮算法網絡傳遞效率高,但也會相應耗費CPU,建議設置爲LZ4或zstd。
-
緩衝區大小,默認1G,基本無需修改,最大可改爲2GB。
下面的示例展示了基於Confluent.Kafka客戶端組件如何對上面的配置項進行設置(均需要在Publish操作之前設置好),請注意查看帶有註釋的區域:
public async Task PublishAsync<T>(string topicName, T message) where T : class { var config = new ProducerConfig { BootstrapServers = KAFKA_SERVERS, QueueBufferingMaxKbytes = 2097151, // 修改緩衝區最大爲2GB,默認爲1GB CompressionType = CompressionType.Lz4, // 配置使用壓縮算法LZ4,其他:gzip/snappy/zstd BatchSize = 32768, // 修改批次大小爲32K LingerMs = 20 // 修改等待時間爲20ms }; using (var producer = new ProducerBuilder<string, string>(config).Build()) { producer.Produce(topicName, new Message<string, string> { Key = Guid.NewGuid().ToString(), Value = JsonConvert.SerializeObject(message) }); ; } }
2 高可靠性消息的實踐
在MQ中,一般存在兩種情況的消息丟失:
-
producer端消息丟失
-
consuer端消息丟失
對於producer端消息丟失,一般會採用帶回調函數的produce方法,且設置acks=all和設計一個較大的retry次數來避免消息丟失。
對於consumer端消息丟失,一般會採用關閉自動提交位移來避免消息丟失。
此外,要避免消息丟失,broker端也需要進行一些優化配置。
下面,我們就一起來看看。
Producer端
基於Confluent.Kafka的示例配置設置示例:重點關注註釋部分
public async Task PublishAsync<T>(string topicName, T message) where T : class { var config = new ProducerConfig { BootstrapServers = KAFKA_SERVERS, Acks = Acks.All, // 表明只有所有副本Broker都收到消息纔算提交成功 MessageSendMaxRetries = 50, // 消息發送失敗最大重試50次 ...... }; using (var producer = new ProducerBuilder<string, string>(config).Build()) { var numProduced = 0; var key = Guid.NewGuid().ToString(); var value = JsonConvert.SerializeObject(message); // 使用帶回調函數的Produce方法 producer.Produce(topicName, new Message<string, string> { Key = key, Value = value }, (deliveryReport) => { if (deliveryReport.Error.Code != ErrorCode.NoError) { // 發送失敗 Console.WriteLine($"[Error] Failed to deliver message: {deliveryReport.Error.Reason}"); } else { // 發送成功 Console.WriteLine($"[Info] Produced event to topic {topicName}: key = {key} value = {value}"); numProduced += 1; } }); // 等待所有回調函數執行完成,參數是超時時間,也就是最大的等待時間 var queueSize = producer.Flush(TimeSpan.FromSeconds(5)); if (queueSize > 0) Console.WriteLine($"[Warn] Producer event queue has {queueSize} pending events on exit."); Console.WriteLine($"[Info] {numProduced} messages were produced to topic {topicName}"); } await Task.CompletedTask; }
Consumer端
基於Confluent.Kafka的示例配置設置示例:重點關注註釋部分
public async Task SubscribeAsync<T>(IEnumerable<string> topics, Action<T> messageFunc, CancellationToken cancellationToken = default) where T : class { var config = new ConsumerConfig { BootstrapServers = KAFKA_SERVERS, ..... EnableAutoCommit = false, // 禁止AutoCommit Acks = Acks.All, // 需要所有副本響應纔算消費完成 ...... }; using (var consumer = new ConsumerBuilder<Ignore, string>(config).Build()) { consumer.Subscribe(topics); try { while (true) { try { var cr = consumer.Consume(cancellationToken); var message = JsonConvert.DeserializeObject<T>(cr.Message.Value); if (message != null) messageFunc(message); consumer.Commit(cr); // 手動提交位移,會產生阻塞,影響吞吐量 Console.WriteLine($"[Info] Consumed record successfully! Received message: {message}"); } catch (ConsumeException e) { Console.WriteLine($"[Error] Error occured in consuming: {e.Error.Reason}"); } } } catch (OperationCanceledException) { // Ctrl+C Pressed Console.WriteLine("[Info] Ctr+C pressed, now closing consumer."); consumer.Close(); } } await Task.CompletedTask; }
Broker端
對於Broker端,可以修改以下三個配置以適應高可靠性的要求:
-
unclean.leader.election.enable = false
-
replication.factor >= 3
-
min.insync.replicas > 1
-
確保 replication.factor > min.insync.replicas
(1)設置 unclean.leader.election.enable = false
這是 Broker 端的參數,它控制的是哪些 Broker 有資格競選分區的 Leader。如果一個 Broker 落後原先的 Leader 太多,那麼它一旦成爲新的 Leader,必然會造成消息的丟失。故一般都要將該參數設置成 false,即不允許這種情況的發生。
從Kafka 0.11版本開始,這個選項的默認值就變成了false。
(2)設置 replication.factor >= 3
這也是 Broker 端的參數(Topic參數)。其實這裏想表述的是,最好將消息多保存幾份,畢竟目前防止消息丟失的主要機制就是冗餘。
(3)設置 min.insync.replicas > 1
這依然是 Broker 端參數(Topic參數),控制的是消息至少要被寫入到多少個副本纔算是“已提交”。設置成大於 1 可以提升消息持久性。在實際環境中千萬不要使用默認值 1。
(4)確保 replication.factor > min.insync.replicas
如果兩者相等,那麼只要有一個副本掛機,整個分區就無法正常工作了。我們不僅要改善消息的持久性,防止數據丟失,還要在不降低可用性的基礎上完成。推薦設置成 replication.factor = min.insync.replicas + 1。
示例:設置replication.factor=3, min.insync.replicas=2
kafka-topics.sh --create --zookeeper zk-server-master:2181/kafka --replication-factor 3 --partitions 3 --topic testtopic--config min.insync.replicas=2
上述配置其實就是實現一個類似MongoDB副本集的WriteConcern=Major的效果。
3 總結
本文介紹了提高producer吞吐量 與 提高消息可靠性 的實踐,重點介紹了在Confluent.Kafka組件下如何進行配置的代碼實踐,相信會對你有所幫助。
參考資料
極客時間,胡夕《Kafka核心技術與實戰》
B站,尚硅谷《Kafka 3.x入門到精通教程》