跳到主要内容
版本:4.1.1

暂停和恢复监听容器

Doubao 中英对照 Pausing and Resuming Listener Containers

2.1.3 版本向监听容器添加了 pause() 和 resume() 方法。此前,你可以在 ConsumerAwareMessageListener 内暂停消费者,并通过监听 ListenerContainerIdleEvent 来恢复它,该事件提供了对 Consumer 对象的访问。虽然你可以使用事件监听器在空闲容器中暂停消费者,但在某些情况下这并不是线程安全的,因为无法保证事件监听器在消费者线程上被调用。要安全地暂停和恢复消费者,你应使用监听容器上的 pause 和 resume 方法。pause() 在下一次 poll() 之前立即生效;resume() 在当前 poll() 返回之后立即生效。当容器被暂停时,它会继续对消费者进行 poll(),如果使用了消费者组管理则避免再均衡,但不会检索任何记录。更多信息请参阅 Kafka 文档。

从 2.1.5 版本开始,你可以调用 isPauseRequested() 来查看是否已调用 pause()。不过,消费者可能尚未实际暂停。如果所有 Consumer 实例都已实际暂停,isConsumerPaused() 返回 true。

此外(同样自 2.1.5 起),发布 ConsumerPausedEvent 和 ConsumerResumedEvent 实例时,以容器作为 source 属性,并以涉及的 TopicPartition 实例作为 partitions 属性。

从 2.9 版本开始,新增了一个容器属性 pauseImmediate,当其设置为 true 时,会使暂停在当前记录处理完成后生效。默认情况下,暂停在上一次 poll 的所有记录都处理完成后生效。请参阅 pauseImmediate。

以下这个简单的 Spring Boot 应用演示了如何使用容器注册表获取对某个 @KafkaListener 方法容器的引用,暂停或恢复其消费者,并接收相应的事件:

@SpringBootApplication
public class Application implements ApplicationListener<KafkaEvent> {

public static void main(String[] args) {
SpringApplication.run(Application.class, args).close();
}

@Override
public void onApplicationEvent(KafkaEvent event) {
System.out.println(event);
}

@Bean
public ApplicationRunner runner(KafkaListenerEndpointRegistry registry,
KafkaTemplate<String, String> template) {
return args -> {
template.send("pause.resume.topic", "thing1");
Thread.sleep(10_000);
System.out.println("pausing");
registry.getListenerContainer("pause.resume").pause();
Thread.sleep(10_000);
template.send("pause.resume.topic", "thing2");
Thread.sleep(10_000);
System.out.println("resuming");
registry.getListenerContainer("pause.resume").resume();
Thread.sleep(10_000);
};
}

@KafkaListener(id = "pause.resume", topics = "pause.resume.topic")
public void listen(String in) {
System.out.println(in);
}

@Bean
public NewTopic topic() {
return TopicBuilder.name("pause.resume.topic")
.partitions(2)
.replicas(1)
.build();
}

}

以下清单展示了上一示例的结果:

partitions assigned: [pause.resume.topic-1, pause.resume.topic-0]
thing1
pausing
ConsumerPausedEvent [partitions=[pause.resume.topic-1, pause.resume.topic-0]]
resuming
ConsumerResumedEvent [partitions=[pause.resume.topic-1, pause.resume.topic-0]]
thing2