2日前に、csdnは私にもう1人のファンがいることを思い出させました。それが私の執筆の動機を刺激しました。私のファンの数を見るためにクリックしないでください!
通常の状況では、Kafkaのコンシューマースレッドデータは1対1にパーティション化(パーティション化)され、単一のパターンはKafka並列操作の最小単位です。Kafkaでは、単一のパーティションのデータのみがコンシューマースレッドによって消費されることを許可します。たとえば、20のパーティションを作成します。実際には、 20のコンシューマースレッドに対応して、いくつかのアクティビティを実行すると、メッセージの量が急激に増加し、コンシューマースレッドが制限され、メッセージを処理する機能が追いつかず、処理できない大量のメッセージが発生する場合があります。 。
現時点では、処理能力を最適化して増やす必要があるかもしれません。ほとんどの人はパーティションの追加を考えています。パーティションは増やすことができますが、無限に増やすことはできません。ここではマルチスレッドソリューションを使用しています。
package com.imcbb;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
import java.util.concurrent.*;
/**
* @author kevin
* Date 2020-09-24
* Time 09:43
*/
@Service
public class KafkaConsumer {
private static Logger logger = LoggerFactory.getLogger(KafkaConsumer.class);
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 10, TimeUnit.SECONDS,
new SynchronousQueue<>(),
new ThreadFactoryBuilder().setNameFormat("KThread-%d").build(),
(r, executor) -> {
logger.warn("Ops,Rejected!");
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// new ThreadPoolExecutor.CallerRunsPolicy()
);
@KafkaListener(topics = "myTopic")
public void listen(ConsumerRecord<?, ?> cr) {
executor.execute(() -> {
logger.info("---------" + cr.toString());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
上記のコードでは、パーティションからのメッセージを消費するスレッドプールを作成しましたが、特に注意が必要な点が2つあります。
1.スレッドプールにタスクを保存するためのキュー。ここでは、SynchronousQueueブロッキングキューを使用します。このキューは非常に興味深いものです。これは非容量キューです。興味がある場合は、インターネットを確認して、あまり説明しないでください。SynchronousQueueは、システムが異常停止したりアプリケーションが再起動したりしたときにキューに蓄積されたメッセージが失われるのを防ぐには(もちろん、スレッドの作業中にシステムの例外が原因でスレッドがハングするのを完全に防ぐことはできません。より高い可用性が必要な場合は、まずデータベースに保存することを検討してください。) redisでは、補正処理メカニズムを実行します)。
2.メッセージが多すぎてスレッドプールがビジー状態の場合、2つの解決策があります。
2.1リスニングコンシューマスレッドをブロックし、新しいThreadPoolExecutor.CallerRunsPolicy()を使用します。この時点で、メッセージは消費されなくなります。
2.1拒否戦略をカスタマイズしてタスクを元に戻す
(r, executor) -> {
logger.warn("Ops,Rejected!");
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
上記の2つの解決策はどちらも問題を解決できます。使用する1つを選択するだけです。どちらの解決策もメッセージの処理順序を保証しません。
2番目のタイプを使用する利点の1つは、ログを介してコンシューマーの消費電力を確認し、それが拒否に入ったかどうかを確認して、スレッド数を適切に調整できることです。
上記はマルチスレッド消費の方法であり、本番環境でテストされています。
記事は単にコードを投稿し、すべての人に完全なデモを書いたので、参照用にダウンロードできます。https://github.com/kevinmails/kafka-consumer-demo
このマシンにkafkaをインストールすることが前提条件です。公式のインストールマニュアル(入手可能な最小バージョン、ローカルテストで十分です)、公式のメッセージがわからない場合はメッセージを残してください。別のインストール記事を書きます。公式のインストールマニュアルがあります:https : //kafka.apache.org/quickstart
皆様のご協力をお願いします!
リファレンス:https : //www.confluent.io/blog/how-choose-number-topics-partitions-kafka-cluster/