目次
1.デフォルトのPartitionAssignor-> RangeAssignorを使用します
2. PartitionAssignor-> RoundRobinAssignorを使用します
I.はじめに
公式サイトでspring-kafkaを学ぶ過程で、MessageListenerContainerは理解しにくく、実践から真の知識を得ることができるので、コードを入力してテストすることで理解を深めることができます。
公式ウェブサイトのリンク:https://docs.spring.io/spring-kafka/docs/2.2.13.RELEASE/reference/html/#message-listener-container
MessageListenerContainerを構成し、メッセージリスナーを提供するか、@ KafkaListenerアノテーションを使用して、メッセージを受信します。
MessageListenerContainerは、2つの実装を提供します:
KafkaMessageListenerContainer
ConcurrentMessageListenerContainerを
KafkaMessageListenerContainerは、単一のスレッド上のすべてのトピックまたはパーティションからすべてのメッセージを受信します。1つ以上のConcurrentMessageListenerContainerは、マルチスレッド消費を提供するKafkaMessageListenerContainerインスタンスを表します。
ConcurrentMessageListenerContainer构造函数
public ConcurrentMessageListenerContainer(ConsumerFactory<K, V> consumerFactory,
ContainerProperties containerProperties)
このコンストラクターの場合、Kafkaはグループ管理機能を使用して、ユーザー間でパーティションを分散します。
@KafkaListenerは、明確なトピックとパーティションを構成できます(パーティション割り当て戦略をテストする必要があるため、この記事の例ではパーティションを構成しないでください)
@KafkaListener(id = "thing2", topicPartitions =
{ @TopicPartition(topic = "topic1", partitions = { "0", "1" }),
@TopicPartition(topic = "topic2", partitions = "0",
partitionOffsets = @PartitionOffset(partition = "1", initialOffset = "100"))
})
public void listen(ConsumerRecord<?, ?> record) {
...
}
公式チュートリアルの内容は次のとおりです。
複数のトピックを聞いている場合、デフォルトのパーティション分散は期待したものではない可能性があります。たとえば、それぞれ5つのパーティションを持つ3つのトピックがあり、使用したい場合 concurrency=15
、アクティブなコンシューマーは5つだけで、それぞれが各トピックから1つのパーティションを割り当て、他の10のコンシューマーはアイドル状態です。これは、デフォルトのKafka PartitionAssignor
が RangeAssignor
(Javadocを参照)であるためです。このシナリオでは、RoundRobinAssignor
代わりに、すべてのコンシューマーにパーティションを分散する使用を検討することをお勧めします 。次に、各コンシューマーに1つのトピックまたはパーティションが割り当てられます。を変更するには PartitionAssignor
、に提供されているプロパティでpartition.assignment.strategy
コンシューマープロパティ(ConsumerConfigs.PARTITION_ASSIGNMENT_STRATEGY_CONFIG
)を 設定できます DefaultKafkaConsumerFactory
。
注:この記事はこのコンテンツをテストします
2.テストの準備
1.Kafkaクライアントの構成
kafka構成ファイルserver.properties(ディレクトリD:\ ProgramFiles \ kafka_2.12 \ config)を変更し、5つのパーティションを構成します
num.partitions = 5
kafkaを再起動します
注:エラーの報告を開始した場合は、これら2つのディレクトリをクリアできます
2.SpringBoot構成
この記事のコードは、以前のブログ「Spring-kafkaの概要(3):SpringBootを使用してメッセージを送受信する」に基づいて変更されています。
構成スレッドの数は15です
@Bean
ConcurrentKafkaListenerContainerFactory<Integer, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<Integer, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
factory.setConcurrency(15);//例如,container.setConcurrency(3)创建三个KafkaMessageListenerContainer实例。
return factory;
}
リスナーを構成する
@KafkaListener(id = "Listener", topics = {"topic_1", "topic_2", "topic_3"}, groupId = "consumer_group_1")
public void listen1(String foo) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
logger.info(sdf.format(new Date()) + " - Listener-接收消息:" + foo);
Thread.sleep(1000 * 2);
}
メッセージ送信インターフェース
@RequestMapping(value = "test2")
public String test2() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//每个topic发送5条数据,每条数据在一个分区
for (int i = 0; i < 5; i++) {
//send方法的参数(String topic, Integer partition, K key, @Nullable V data)
kafkaTemplate.send("topic_1", i, 0, "topic_1_" + i);
kafkaTemplate.send("topic_2", i, 0, "topic_2_" + i);
kafkaTemplate.send("topic_3", i, 0, "topic_3_" + i);
kafkaTemplate.flush();
}
return "test";
}
3.消費者流通テスト
1.デフォルトを使用しますPartitionAssignor -> RangeAssignor
プロジェクトを開始し、次のログを整理します。
5つのアクティブなコンシューマーのみが表示され、各コンシューマーにはトピックごとにパーティションが割り当てられ、他の10のコンシューマーはアイドル状態です。
2020-05-12 14:30:24.917 INFO 13152 --- [Listener-10-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-12, groupId=consumer_group_1] Setting newly assigned partitions [topic_1-2, topic_2-2, topic_3-2]
2020-05-12 14:30:24.917 INFO 13152 --- [ Listener-7-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-9, groupId=consumer_group_1] Setting newly assigned partitions []
2020-05-12 14:30:24.917 INFO 13152 --- [ Listener-4-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-6, groupId=consumer_group_1] Setting newly assigned partitions []
2020-05-12 14:30:24.917 INFO 13152 --- [Listener-13-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-15, groupId=consumer_group_1] Setting newly assigned partitions []
2020-05-12 14:30:24.918 INFO 13152 --- [ Listener-3-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-5, groupId=consumer_group_1] Setting newly assigned partitions []
2020-05-12 14:30:24.918 INFO 13152 --- [ Listener-5-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-7, groupId=consumer_group_1] Setting newly assigned partitions []
2020-05-12 14:30:24.918 INFO 13152 --- [ Listener-9-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-11, groupId=consumer_group_1] Setting newly assigned partitions [topic_1-1, topic_2-1, topic_3-1]
2020-05-12 14:30:24.918 INFO 13152 --- [ Listener-1-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-3, groupId=consumer_group_1] Setting newly assigned partitions []
2020-05-12 14:30:24.918 INFO 13152 --- [ Listener-8-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-10, groupId=consumer_group_1] Setting newly assigned partitions [topic_1-0, topic_2-0, topic_3-0]
2020-05-12 14:30:24.918 INFO 13152 --- [Listener-12-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-14, groupId=consumer_group_1] Setting newly assigned partitions [topic_1-4, topic_2-4, topic_3-4]
2020-05-12 14:30:24.918 INFO 13152 --- [ Listener-6-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-8, groupId=consumer_group_1] Setting newly assigned partitions []
2020-05-12 14:30:24.919 INFO 13152 --- [ Listener-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-2, groupId=consumer_group_1] Setting newly assigned partitions []
2020-05-12 14:30:24.919 INFO 13152 --- [Listener-11-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-13, groupId=consumer_group_1] Setting newly assigned partitions [topic_1-3, topic_2-3, topic_3-3]
2020-05-12 14:30:24.920 INFO 13152 --- [Listener-14-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-16, groupId=consumer_group_1] Setting newly assigned partitions []
2020-05-12 14:30:24.920 INFO 13152 --- [ Listener-2-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-4, groupId=consumer_group_1] Setting newly assigned partitions []
PartitionAssignor ->
2.RoundRobinAssignorを使用します
各コンシューマーにトピックまたはセクションを割り当てます。を変更するにはPartitionAssignor
、提供された属性にpartition.assignment.strategy
ユーザー属性(ConsumerConfigs.PARTITION_ASSIGNMENT_STRATEGY_CONFIG
)を設定できますDefaultKafkaConsumerFactory
。
Kafkaのデフォルト PartitionAssignor
はRangeAssignor,需要设置成
RoundRobinAssignorです。
1つの場所を変更するだけで済みます
プロジェクトを再起動し、次のログを整理します。
アイドル状態の消費者がいないことがわかります
2020-05-12 15:02:57.306 INFO 13720 --- [ Listener-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-2, groupId=consumer_group_1] Setting newly assigned partitions [topic_2-2]
2020-05-12 15:02:57.306 INFO 13720 --- [ Listener-2-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-4, groupId=consumer_group_1] Setting newly assigned partitions [topic_2-4]
2020-05-12 15:02:57.306 INFO 13720 --- [ Listener-8-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-10, groupId=consumer_group_1] Setting newly assigned partitions [topic_1-0]
2020-05-12 15:02:57.306 INFO 13720 --- [ Listener-1-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-3, groupId=consumer_group_1] Setting newly assigned partitions [topic_2-3]
2020-05-12 15:02:57.306 INFO 13720 --- [ Listener-9-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-11, groupId=consumer_group_1] Setting newly assigned partitions [topic_1-1]
2020-05-12 15:02:57.306 INFO 13720 --- [Listener-11-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-13, groupId=consumer_group_1] Setting newly assigned partitions [topic_1-3]
2020-05-12 15:02:57.306 INFO 13720 --- [ Listener-6-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-8, groupId=consumer_group_1] Setting newly assigned partitions [topic_3-3]
2020-05-12 15:02:57.306 INFO 13720 --- [Listener-14-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-16, groupId=consumer_group_1] Setting newly assigned partitions [topic_2-1]
2020-05-12 15:02:57.307 INFO 13720 --- [ Listener-4-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-6, groupId=consumer_group_1] Setting newly assigned partitions [topic_3-1]
2020-05-12 15:02:57.308 INFO 13720 --- [Listener-12-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-14, groupId=consumer_group_1] Setting newly assigned partitions [topic_1-4]
2020-05-12 15:02:57.308 INFO 13720 --- [ Listener-3-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-5, groupId=consumer_group_1] Setting newly assigned partitions [topic_3-0]
2020-05-12 15:02:57.310 INFO 13720 --- [Listener-10-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-12, groupId=consumer_group_1] Setting newly assigned partitions [topic_1-2]
2020-05-12 15:02:57.312 INFO 13720 --- [ Listener-7-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-9, groupId=consumer_group_1] Setting newly assigned partitions [topic_3-4]
2020-05-12 15:02:57.313 INFO 13720 --- [Listener-13-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-15, groupId=consumer_group_1] Setting newly assigned partitions [topic_2-0]
2020-05-12 15:02:57.315 INFO 13720 --- [ Listener-5-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-7, groupId=consumer_group_1] Setting newly assigned partitions [topic_3-2]
4、パフォーマンステスト
SpringBootを起動し、インターフェイスにアクセスします:http://127.0.0.1:8080 / test2
リスナーは2秒間スリープして、時間のかかるビジネスをシミュレートします。これはログ分析に便利です。
@KafkaListener(id = "Listener", topics = {"topic_1", "topic_2", "topic_3"}, groupId = "consumer_group_1")
public void listen1(String foo) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
logger.info(sdf.format(new Date()) + " - Listener-接收消息:" + foo);
Thread.sleep(1000 * 2);
}
1.シナリオ1のシングルスレッド消費
factory.setConcurrency(1);
消費結果:消費パフォーマンスの低下(2秒ごとのメッセージの消費)
2020-05-12 15:15:15.223 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:15 - Listener-接收消息:topic_1_0
2020-05-12 15:15:17.224 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:17 - Listener-接收消息:topic_1_4
2020-05-12 15:15:19.224 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:19 - Listener-接收消息:topic_1_3
2020-05-12 15:15:21.224 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:21 - Listener-接收消息:topic_2_4
2020-05-12 15:15:23.225 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:23 - Listener-接收消息:topic_1_2
2020-05-12 15:15:25.225 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:25 - Listener-接收消息:topic_2_3
2020-05-12 15:15:27.226 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:27 - Listener-接收消息:topic_3_4
2020-05-12 15:15:29.227 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:29 - Listener-接收消息:topic_1_1
2020-05-12 15:15:31.227 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:31 - Listener-接收消息:topic_2_2
2020-05-12 15:15:33.228 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:33 - Listener-接收消息:topic_3_3
2020-05-12 15:15:35.229 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:35 - Listener-接收消息:topic_2_1
2020-05-12 15:15:37.230 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:37 - Listener-接收消息:topic_3_2
2020-05-12 15:15:39.230 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:39 - Listener-接收消息:topic_2_0
2020-05-12 15:15:41.231 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:41 - Listener-接收消息:topic_3_1
2020-05-12 15:15:43.232 INFO 17188 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:15:43 - Listener-接收消息:topic_3_0
2.シナリオ2の同時消費
factory.setConcurrency(15);
消費結果:非常に良好な消費性能
2020-05-12 15:12:56.225 INFO 13720 --- [ Listener-0-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_2_2
2020-05-12 15:12:56.225 INFO 13720 --- [Listener-10-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_1_2
2020-05-12 15:12:56.225 INFO 13720 --- [Listener-12-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_1_4
2020-05-12 15:12:56.225 INFO 13720 --- [ Listener-2-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_2_4
2020-05-12 15:12:56.225 INFO 13720 --- [ Listener-8-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_1_0
2020-05-12 15:12:56.225 INFO 13720 --- [ Listener-5-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_3_2
2020-05-12 15:12:56.225 INFO 13720 --- [ Listener-6-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_3_3
2020-05-12 15:12:56.225 INFO 13720 --- [ Listener-4-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_3_1
2020-05-12 15:12:56.225 INFO 13720 --- [ Listener-7-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_3_4
2020-05-12 15:12:56.225 INFO 13720 --- [ Listener-1-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_2_3
2020-05-12 15:12:56.225 INFO 13720 --- [ Listener-9-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_1_1
2020-05-12 15:12:56.225 INFO 13720 --- [ Listener-3-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_3_0
2020-05-12 15:12:56.225 INFO 13720 --- [Listener-11-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_1_3
2020-05-12 15:12:56.225 INFO 13720 --- [Listener-14-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_2_1
2020-05-12 15:12:56.225 INFO 13720 --- [Listener-13-C-1] com.asyf.demo.config.Listener : 2020-05-12 15:12:56 - Listener-接收消息:topic_2_0
五数要約
同時消費を使用すると、メッセージの消費率が上がる可能性がありますが、次の問題に注意する必要があります。
1.同時メッセージリスナーコンテナを使用する場合、リスナーインスタンスはすべてのコンシューマスレッドで呼び出されます。したがって、リスナーはスレッドセーフである必要があります。
2.同じパーティションにないメッセージを順番に読み取ることはできません。