关于SpringKafka消费者的几个监听器:[一次处理单条消息和一次处理一批消息]以及[自动提交offset和手动提交offset]
自己在使用Spring Kafka 的消费者消费消息的时候的实践总结:
接口 KafkaDataListener 是spring-kafka提供的一个供消费者接受消息的顶层接口,也是一个空接口;
public interface KafkaDataListener<T> {} 对于消费端接收消息的时候,spring-kafka的设计思路是,提供一个顶层接口,提供两个子类,一个子类是自动提交offset的,另一个子类是手动提交offset的.
无论是自动提交offset还是手动提交offset,又各分为两种,一种是一次只处理一条消息,另一种是一次可以处理一批消息. 该 KafkaDataListener 顶层接口有两个实现类:GenericMessageListener 和 GenericAcknowledgingMessageListener,
二者的区别是,前者是自动提交offset,后者是手动提交offset。 1、 GenericMessageListener
该接口是自动提交offset,它的onMessage方法的参数只有一个,就是传递过来的一条消息;
public interface GenericMessageListener<T> extends KafkaDataListener<T> {void onMessage(T data);}
这个接口又有两个子接口:MessageListener 和 BatchMessageListener
这两个接口也都是空接口,二者的区别是,前者一次只处理一条消息,后者一次处理一批消息. //一次处理一条消息
//消费者如果实现该接口的话,如果配置中设置max.poll.records参数大于1的话是无效的,因为它一次只处理一条消息
public interface MessageListener<K, V> extends GenericMessageListener<ConsumerRecord<K, V>> {}
//一次可以处理一批消息,每一批次的消息总条数是随机的,但可以在消费者的配置中设置一个最大值(max.poll.records,
//比如设置了最大拉取的消息条数为100,那么onMessage方法每次接受到的消息条数是随机的,但最大不会超过100)
public interface BatchMessageListener<K, V> extends GenericMessageListener<List<ConsumerRecord<K, V>>> {} 2、 GenericAcknowledgingMessageListener
该接口是手动提交offset,它的onMessage方法的参数有两个,第一个是传递过来的一条消息,第二个参数是用于提交offset的对象
public interface GenericAcknowledgingMessageListener<T> extends KafkaDataListener<T> {void onMessage(T data, Acknowledgment acknowledgment);} 这个接口也有两个子接口:AcknowledgingMessageListener 和 BatchAcknowledgingMessageListener,这两个接口也都是空接口.
//一次只处理一条消息,并手动提交offset,需要在消费者的配置中设置<property name="ackMode" value="MANUAL_IMMEDIATE"/>
public interface AcknowledgingMessageListener<K, V> extends GenericAcknowledgingMessageListener<ConsumerRecord<K, V>> {}
//一次处理一批消息,处理完这一批消息之后,在批量提交offset,需要在消费者的配置中设置<property name="ackMode" value="MANUAL"/>
public interface BatchAcknowledgingMessageListener<K, V> extends GenericAcknowledgingMessageListener<List<ConsumerRecord<K, V>>> {}
下面的消费者继承的是MessageListener这个监听器,就是一次处理一条消息,而且是自动提交offset:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.xxxxxx.consumer.dto.FriendRelationDto;
import com.xxxxxx.consumer.dto.MessageDto;
import com.xxxxxx.consumer.service.FriendRelationService;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.listener.MessageListener;
import org.springframework.stereotype.Service;
import java.io.IOException; /**
* Created by SYJ on 2017/3/21.
*/
@Service
public class ConsumerService implements MessageListener<Integer, String> { private static final Logger logger = LoggerFactory.getLogger(ConsumerService.class);
@Autowired
private FriendRelationService friendRelationService; /**
* 消息监听方法
* @param record
*/
@Override
public void onMessage(ConsumerRecord<Integer, String> record) {
logger.info("Before receiving:" + record.toString());
String value = record.value();
MessageDto<FriendRelationDto> message = JSON.parseObject(value, new TypeReference<MessageDto<FriendRelationDto>>(){});
try {
friendRelationService.process(message.getData());
} catch (IOException e) {
e.printStackTrace();
}
}
}
下面的消费者实现的BatchMessageListener这个监听器,就是一次接受一批消息,消息的数量是随机的,但最大不会超过"max.poll.records"参数配置的数量:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.xxxxxx.consumer.dto.FriendRelationDto;
import com.xxxxxx.consumer.dto.MessageDto;
import com.xxxxxx.consumer.service.FriendRelationService;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.listener.BatchMessageListener;
import org.springframework.stereotype.Service; import java.io.IOException;
import java.util.List; /**
* Created by SYJ on 2017/3/21.
*/
@Service
public class ConsumerService implements BatchMessageListener<Integer, String> { private static final Logger logger = LoggerFactory.getLogger(ConsumerService.class);
@Autowired
private FriendRelationService friendRelationService; @Override
public void onMessage(List<ConsumerRecord<Integer, String>> recordList) {
for (ConsumerRecord<Integer, String> record : recordList) {
logger.info("Before receiving:" + record.toString());
String value = record.value();
MessageDto<FriendRelationDto> message = JSON.parseObject(value, new TypeReference<MessageDto<FriendRelationDto>>(){});
try {
friendRelationService.process(message.getData());
} catch (IOException e) {
e.printStackTrace();
}
} }
}
下面的消费者实现的是AcknowledgingMessageListener这个监听器,它的特点是一次接收一条消息,可以通过acknowledgment来手动提交offset,需要在消费者的配置中指定<property name="ackMode" value="MANUAL_IMMEDIATE"/>:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.xxxxxx.consumer.dto.FriendRelationDto;
import com.xxxxxx.consumer.dto.MessageDto;
import com.xxxxxx.consumer.service.FriendRelationService;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.listener.AcknowledgingMessageListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.stereotype.Service; import java.io.IOException; /**
* Created by SYJ on 2017/3/21.
*/
@Service
public class ConsumerService implements AcknowledgingMessageListener<Integer, String> { private static final Logger logger = LoggerFactory.getLogger(ConsumerService.class);
@Autowired
private FriendRelationService friendRelationService; @Override
public void onMessage(ConsumerRecord<Integer, String> record, Acknowledgment acknowledgment) {
logger.info("Before receiving:" + record.toString());
String value = record.value();
MessageDto<FriendRelationDto> message = JSON.parseObject(value, new TypeReference<MessageDto<FriendRelationDto>>(){});
try {
friendRelationService.process(message.getData());
logger.info("===========开始提交offset=============");
acknowledgment.acknowledge();//提交offset
logger.info("===========已经提交offset=============");
} catch (IOException e) {
e.printStackTrace();
}
}
}
下面的消费者实现的是BatchAcknowledgingMessageListener这个监听器,它的特点是一次可以处理一批消息,并且可以在处理完这一批消息之后提交offset,需要在消费者的配置文件中配置"max.poll.records"参数指定本批消息可以达到的最大值,并指定<property name="ackMode" value="MANUAL"/>:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.xxxxxx.consumer.dto.FriendRelationDto;
import com.xxxxxx.consumer.dto.MessageDto;
import com.xxxxxx.consumer.service.FriendRelationService;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.listener.BatchAcknowledgingMessageListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.stereotype.Service; import java.io.IOException;
import java.util.List; /**
* Created by SYJ on 2017/3/21.
*/
@Service
public class ConsumerService implements BatchAcknowledgingMessageListener<Integer, String> { private static final Logger logger = LoggerFactory.getLogger(ConsumerService.class);
@Autowired
private FriendRelationService friendRelationService; @Override
public void onMessage(List<ConsumerRecord<Integer, String>> recordList, Acknowledgment acknowledgment) {
logger.info("Before receiving:" + recordList.toString());
logger.info("本次消息总数:" + recordList.size());
for (ConsumerRecord<Integer, String> record : recordList) {
String value = record.value();
MessageDto<FriendRelationDto> message = JSON.parseObject(value, new TypeReference<MessageDto<FriendRelationDto>>() {
});
try {
friendRelationService.process(message.getData());
} catch (IOException e) {
e.printStackTrace();
}
}
logger.info("===========开始提交offset=============");
acknowledgment.acknowledge();//提交offset
logger.info("===========已经提交offset=============");
}
}
下面是spring-kafka消费端的配置文件示例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="consumerProperties" class="java.util.HashMap">
<constructor-arg>
<map>
<entry key="bootstrap.servers" value="${bootstrap.servers}"/>
<!-- 指定消费组名 -->
<entry key="group.id" value="friend-group"/>
<entry key="enable.auto.commit" value="false"/>
<entry key="auto.commit.interval.ms" value="1000"/>
<entry key="session.timeout.ms" value="15000"/>
<!-- 当使用批量处理消息的时候,每次onMessage方法获取到的消息总条数虽然是随机的,但是不会超过此最大值 -->
<entry key="max.poll.records" value="50"/>
<entry key="key.deserializer" value="org.apache.kafka.common.serialization.IntegerDeserializer"/>
<!--<entry key="key.deserializer" value="org.apache.kafka.common.serialization.StringDeserializer"/>-->
<entry key="value.deserializer" value="org.apache.kafka.common.serialization.StringDeserializer"/>
</map>
</constructor-arg>
</bean> <bean id="consumerFactory" class="org.springframework.kafka.core.DefaultKafkaConsumerFactory">
<constructor-arg>
<ref bean="consumerProperties"/>
</constructor-arg>
</bean> <!-- 消费消息的服务类 -->
<bean id="messageListernerConsumerService" class="com.xxxxxxx.consumer.ConsumerService"/> <!-- 消费者容器配置信息 -->
<bean id="containerProperties" class="org.springframework.kafka.listener.config.ContainerProperties">
<constructor-arg value="friend"/>
<!--<constructor-arg>
<list>
<value>zptopic</value>
<value>ssmk</value>
<value>friend</value>
</list>
</constructor-arg>-->
<property name="messageListener" ref="messageListernerConsumerService"/> <!-- 提交offset,批量提交 -->
<property name="ackMode" value="MANUAL"/>
<!-- 提交offset的方式,处理完一条消息就立即提交 -->
<!--<property name="ackMode" value="MANUAL_IMMEDIATE"/>-->
</bean> <!-- 单线程消息监听容器,每启动一个消费者客户端,只会开启一个线程来消费 -->
<!--<bean id="messageListenerContainer" class="org.springframework.kafka.listener.KafkaMessageListenerContainer" init-method="doStart">
<constructor-arg ref="consumerFactory"/>
<constructor-arg ref="containerProperties"/>
</bean>--> <!-- 多线程消息监听容器,每启动一个消费者客户端,可以开启多个线程,开启多少个线程自己可以通过concurrency来指定 -->
<bean id="messageListenerContainer" class="org.springframework.kafka.listener.ConcurrentMessageListenerContainer" init-method="doStart">
<constructor-arg ref="consumerFactory"/>
<constructor-arg ref="containerProperties"/>
<property name="concurrency" value="5"/>
</bean> </beans>
关于SpringKafka消费者的几个监听器:[一次处理单条消息和一次处理一批消息]以及[自动提交offset和手动提交offset]的更多相关文章
- kafka原理和实践(四)spring-kafka消费者源码
系列目录 kafka原理和实践(一)原理:10分钟入门 kafka原理和实践(二)spring-kafka简单实践 kafka原理和实践(三)spring-kafka生产者源码 kafka原理和实践( ...
- spring-kafka手动提交offset
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- Spring-Kafka —— 实现批量消费和手动提交offset
spring-kafka的官方文档介绍,可以知道自1.1版本之后, @KafkaListener开始支持批量消费,只需要设置batchListener参数为true 把application.yml中 ...
- Kafka消费者手动提交消息偏移
生产者每次调用poll()方法时,它总是返回由生产者写入Kafka但还没有消费的消息,如果消费者一致处于运行状态,那么分区消息偏移量就没什么用处,但是如果消费者发生崩溃或者有新的消费者加入群组,就会触 ...
- Kafka权威指南 读书笔记之(四)Kafka 消费者一一从 Kafka读取数据
KafkaConsumer概念 消费者和消费者群组 Kafka 消费者从属于消费者群组.一个群组里的消费者订阅的是同一个主题,每个消费者接收主题一部分分区的消息. 往群组里增加消费者是横向伸缩消费能力 ...
- 初始 Kafka Consumer 消费者
温馨提示:整个 Kafka 专栏基于 kafka-2.2.1 版本. 1.KafkaConsumer 概述 根据 KafkaConsumer 类上的注释上来看 KafkaConsumer 具有如下特征 ...
- Kafka系列3:深入理解Kafka消费者
上面两篇聊了Kafka概况和Kafka生产者,包含了Kafka的基本概念.设计原理.设计核心以及生产者的核心原理.本篇单独聊聊Kafka的消费者,包括如下内容: 消费者和消费者组 如何创建消费者 如何 ...
- 深入了解Kafka【四】消费者的Offset管理
1.Offset Topic Consumer通过提交Offset来记录当前消费的最后位置,以便于消费者发生崩溃或者有新的消费者加入消费者组,而引发的分区再均衡操作,每个消费者可能会分到不同的分区.我 ...
- kafka知识点详解
第一部分:kafka概述 一.定义(消息引擎系统) 一句话概括kafka的核心功能就是:高性能的消息发送与高性能的消息消费. kafka刚推出的时候是以消息引擎的身份出现的,它具有强大的消息传输效率和 ...
随机推荐
- linux之间文件传输(之scp)
linux的scp命令 linux 的 scp 命令 可以 在 linux 之间复制 文件 和 目录: ==================scp 命令==================scp 可以 ...
- Linux下面安装和配置MySQL
如何从MySQL官方Yum仓库安装MySQL5.6 首先我们需要从MySQL开发者网站下载Yum仓库文件 Download MySQL Yum Repository 从上面的连接地址下载:Red Ha ...
- UAT测试后上线出现问题的引起的思考
最近公司有一个外部项目上线了,虽然我没有参与这个项目,仅仅只是作为一个旁观者,但是关于用户的UAT测试的问题,不得表达下我的看法, 在上线之前进行了近一个月的UAT测试,测试完成后进入了正式上线阶段. ...
- golang学习笔记 ---dup
对文件做拷贝.打印.搜索.排序.统计或类似事情的程序都有一个差不多的程序结构:一个处理输入的循环,在每个元素上执行计算处理,在处理的同时或最后产生输出.我们会展示一个名为 dup 的程序的三个版本 p ...
- [ACM] hdu 1253 胜利大逃亡 (三维BFS)
胜利大逃亡 Problem Description Ignatius被魔王抓走了,有一天魔王出差去了,这但是Ignatius逃亡的好机会. 魔王住在一个城堡里,城堡是一个A*B*C的立方体,能够被表示 ...
- 最烂编程语言得主:javascript
C++在我脑中一直是一门缺乏设计和远见的语言,其设计者也是缺少主见的人(我承认我对c++有一定偏见),在我看来,C++从一开始就是堆叠语言特性,成为最流行的语言,,只是这个时代将它推到了最前列,我心中 ...
- SharePoint 中时间轴 Timeline的实现
客户需要在OA中实现每日动态功能,能够记录每一位员工的每天的工作动态,我很快想到了时间轴,因为时间轴能很直观的现实员工每一刻的动态.就像Facebook的Timeline效果(点击查看). 尝试着搜索 ...
- windows(64位)下使用curl命令
Curl命令可以通过命令行的方式,执行Http请求.在Elasticsearch中有使用的场景,因此这里研究下如何在windows下执行curl命令. 工具下载 在官网处下载工具包:http://cu ...
- 【Python】Windows平台下Python、Pydev连接Mysql数据库
Mysql数据库是跨平台的,不是说Python一定就要连接Mongodb. Python连接Mysql数据库是非常easy的. 首先,你要配置好Python的开发环境,详见<[Python]Wi ...
- 如何将shell的打印日志输入到日志文件
如果shell打印的日志很多,屏幕无法完全显示,需要查看shell执行的情况,这是就需要输入到日值了: 如:echo "2012-6-14" | tee -a my.log -a表 ...