kafka-KafkaConsumer.subscribe()和KafkaConsumer.assign()区别
场景
有两种方式监听kafka消费者消息
- 指定消费分区
- 不指定消费分区
不指定消费分区:
@Slf4j
@Component
public class ConsumerListener {
@KafkaListener(topics = "test", groupId = "test-group")
public void partition(ConsumerRecord<?, ?> record) {
handle(record,"consumer-1");
}
@KafkaListener(topics = "test", groupId = "test-group")
public void partition2(ConsumerRecord<?, ?> record) {
handle(record,"consumer-2");
}
private void handle(ConsumerRecord<?, ?> record,String consumerId) {
final String value = (String) record.value();
log.info("consumer: {} consume message: {}, partition: {}", consumerId,value, record.partition());
}
}
指定消费分区:
@Slf4j
@Component
public class SpecialPartitionConsumerListener {
@KafkaListener(topicPartitions = {@TopicPartition(topic = "test", partitions = {"0"})},
groupId = "test-group")
public void partition1(ConsumerRecord<?, ?> record) {
handle(record);
}
@KafkaListener(topicPartitions = {@TopicPartition(topic = "test", partitions = {"1"})},
groupId = "test-group")
public void partition2(ConsumerRecord<?, ?> record) {
handle(record);
}
@KafkaListener(topicPartitions = {@TopicPartition(topic = "test", partitions = {"2"})},
groupId = "test-group")
public void partition3(ConsumerRecord<?, ?> record) {
handle(record);
}
private void handle(ConsumerRecord<?, ?> record) {
final String value = (String) record.value();
log.info("consume message: {}, partition: {}", value, record.partition());
}
}
问题
在多应用实例的情况下,指定分区消费的方式,会出现重复消费的情况,而没有指定分区消费者就不会。这就很奇怪了,于是就探索一下
在搜索了一番之后,发现是kafka的 subscribe() 和 assign()的区别
- subscribe: 消费者使用
subscribe()
方法订阅一个或多个主题。在这种模式下,消费者会自动参与消费者组中的平衡机制,即当消费者组中的成员发生变化时(例如消费者加入或离开),Kafka 会重新分配主题分区给消费者组中的消费者。这种模式适用于大多数应用场景,特别是在需要动态平衡分区的情况下 - assgin: 费者使用
assign()
方法直接分配一组特定的分区。在这种模式下,消费者不会参与消费者组的平衡机制。这意味着一旦你使用assign()
方法指定了要消费的分区,这些分区就固定了,即使消费者组中的成员发生变化也不会影响这些分区的分配。这种方式通常用于特定的场景,比如需要精确控制哪些分区由哪个消费者消费。在多个应用实例的场景下,同一个分区下,会被不同实例消费,从而出现重复消费方式
如何在指定分区的情况下不会产生重复消费方式呢?
解决方案
使用自定义消费者分区分配策略,确保每个实例只处理一个分区
即在实例启动时,设置实例的Id, 然后用 id对分区数进行取模,取id%partitionNum
为当前实例只能处理的分区数据