消息监听
对于Kafka中Topic的数据消费,我们一般都选择使用消息监听器进行消费,怎么把消息监听器玩出花来呢,那就得看看它所实现的功能了。
Spring-Kafka中消息监听大致分为两种类型,一种是单条数据消费,一种是批量消费;两者的区别只是在于监听器一次性获取消息的数量。GenericMessageListener是我们实现消息监听的一个接口,向上扩展的接口有非常多,比如:单数据消费的MessageListener、批量消费的BatchMessageListener、还有具备ACK机制的AcknowledgingMessageListener和BatchAcknowledgingMessageListener等等。接下来我们就一一解析一下。
GenericMessageListener
这里可以看到GenericMessageListener使用注解标明这是一个函数式接口,默认实现了三种不同参数的onMessage方法。data就是我们需要接收的数据,Consumer则是消费者类,Acknowledgment则是用来实现Ack机制的类。这里需要注意一下的是,Consumer对象并不是线程安全的。
@FunctionalInterface
public interface GenericMessageListener {
void onMessage(T var1);
default void onMessage(T data, Acknowledgment acknowledgment) {
throw new UnsupportedOperationException("Container should never call this");
}
default void onMessage(T data, Consumer, ?> consumer) {
throw new UnsupportedOperationException("Container should never call this");
}
default void onMessage(T data, Acknowledgment acknowledgment, Consumer, ?> consumer) {
throw new UnsupportedOperationException("Container should never call this");
}
}
接下来先浏览一下继承了GenericMessageListener接口的类。前缀为Batch的接口都是批处理类型的消息监听接口,里面的参数也都讲解过了
public interface MessageListener {
void onMessage(ConsumerRecord data);
}
public interface AcknowledgingMessageListener {
void onMessage(ConsumerRecord data, Acknowledgment acknowledgment);
}
public interface ConsumerAwareMessageListener extends MessageListener {
void onMessage(ConsumerRecord data, Consumer, ?> consumer);
}
public interface AcknowledgingConsumerAwareMessageListener extends MessageListener {
void onMessage(ConsumerRecord data, Acknowledgment acknowledgment, Consumer, ?> consumer);
}
public interface BatchMessageListener {
void onMessage(List> data);
}
public interface BatchAcknowledgingMessageListener {
void onMessage(List> data, Acknowledgment acknowledgment);
}
public interface BatchConsumerAwareMessageListener extends BatchMessageListener {
void onMessage(List> data, Consumer, ?> consumer);
}
public interface BatchAcknowledgingConsumerAwareMessageListener extends BatchMessageListener {
void onMessage(List> data, Acknowledgment acknowledgment, Consumer, ?> consumer);
}
在把@KafkaListener玩出花前,我们还需要了解怎么使用非注解方式去监听Topic。
我们在创建监听容器前需要创建一个监听容器工厂,这里只需要配置一下消费者工厂就好了
,之后我们使用它去创建我们的监听容器。consumerFactory()这个参数在之前就已经定义过了,这里就不重复贴代码了。
@Bean
public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
return factory;
}
有了监听容器工厂,我们就可以使用它去创建我们的监听容器
Bean方式创建监听容器
@Bean
public KafkaMessageListenerContainer demoListenerContainer() {
ContainerProperties properties = new ContainerProperties("topic.quick.bean");
properties.setGroupId("bean");
properties.setMessageListener(new MessageListener() {
private Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public void onMessage(ConsumerRecord record) {
log.info("topic.quick.bean receive : " + record.toString());
}
});
return new KafkaMessageListenerContainer(consumerFactory(), properties);
}
启动项目我们可以看一下控制台的日志,监听容器成功分配给某个消费者的结果很清晰的显示出来了,顺便就写个测试方法测试一下监听器能不能正常运行。
2018-09-11 10:36:15.732 INFO 1168 --- [erContainer-C-1] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer-4, groupId=bean] Successfully joined group with generation 1
2018-09-11 10:36:15.733 INFO 1168 --- [erContainer-C-1] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer-4, groupId=bean] Setting newly assigned partitions [topic.quick.bean-0]
2018-09-11 10:36:15.733 INFO 1168 --- [erContainer-C-1] o.s.k.l.KafkaMessageListenerContainer : partitions assigned: [topic.quick.bean-0]
@Test
public void test() {
kafkaTemplate.send("topic.quick.bean", "send msg to beanListener");
}
@KafkaListener参数讲解
在前几章入门的时候就已经写过一个用@KafkaListener注解实现监听的代码,这里就贴一下之前写的代码
@KafkaListener(id = "demo", topics = "topic.quick.demo")
public void listen(String msgData) {
log.info("demo receive : "+msgData);
}
使用@KafkaListener这个注解并不局限于这个监听容器是单条数据消费还是批量消费,区分单数据还是多数据消费只需要配置一下注解的containerFactory属性即可,先讲解一下这个监听方法都能接收写什么参数吧。
data : 对于data值的类型其实并没有限定,根据KafkaTemplate所定义的类型来决定。data为List集合的则是用作批量消费。
ConsumerRecord:具体消费数据类,包含Headers信息、分区信息、时间戳等
Acknowledgment:用作Ack机制的接口
Consumer: