最近在碰到一个问题: 创建一个新的topic(event_a),向event_a的发送数据,不能立马消费到数据。但是对于老的topic可以立即消费到数据。
@KafkaListener(topicPattern = "event_.*", groupId = "event_1")
public void event(ConsumerRecord<?, ?> record, Acknowledgment ack, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
经过查询相关资料,发现问题出现在topicPattern这个属性上,首先我们来看topicPattern属性的官方解释:
The topic pattern for this listener.
The entries can be 'topic pattern', a'property-placeholder key' or an 'expression'.
The framework will create acontainer that subscribes to all topics matching the specified pattern to getdynamically assigned partitions.
The pattern matching will be performedperiodically against topics existing at the time of check.
An expression mustbe resolved to the topic pattern (String or Pattern result types are supported).
将其翻译过来:
此侦听器的主题模式。
条目可以是“主题模式”,“属性占位符键”或“表达式”。
该框架将创建一个容器,该容器订阅与指定模式匹配的所有主题以获取动态分配的分区。
模式匹配将针对检查时存在的主题【定期执行】。
表达式必须解析为主题模式(支持字符串或模式结果类型)。
问题原因: 从官方解释,topicPattern 会定期检查topic列表,然后将新加入的topic分配至某个消费者。这个定期 检查时间metadata.max.age.ms=300000 ( ms ),即5 分钟。 因此我们需要修改这个时间,让检查的时间更短 ,引入containerFactory属性。 配置轮询时间
@KafkaListener(topicPattern = "event_.*",containerFactory = "kafkaListenerFactory" )
public void event(ConsumerRecord<?, ?> record, Acknowledgment ack, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
@Bean("kafkaListenerFactory")
public KafkaListenerContainerFactory<?> kafkaListenerContainerFactory() {
Map<String, Object> props = new HashMap<>(5);
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); //2s轮询一次
props.put(ConsumerConfig.METADATA_MAX_AGE_CONFIG,200);
props.put(ConsumerConfig.GROUP_ID_CONFIG, "event_scheduler");
ConcurrentKafkaListenerContainerFactory<String, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(new DefaultKafkaConsumerFactory(props));
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
return factory; }