spring-kafka
spring-kafka
使用spring-kafka的小伙伴,看过来。
说明
因为spring-kafka
封装的比较厉害,可能跟你实际使用起来有很大的差别。
一个简单的消费例子
在spring-boot
基础上添加依赖:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
注意要检查下依赖的kakfa-clients
,是否与你服务端的匹配。
不要忘记了通过注解@EnableKafka
开启自动配置。
采用默认的配置:
spring:
kafka:
consumer:
bootstrap-servers:
- 127.0.0.1:9092
# 消费组
group-id: myGroup
# 消费者是否自动提交偏移量,默认为true
enable-auto-commit: false
# 消费者在读取一个没有偏移量或者偏移量无效的情况下,从起始位置读取partition的记录,默认是latest
auto-offset-reset: earliest
# 单次调用poll方法能够返回的消息数量
max-poll-records: 50
然后写个测试用例试试:
package tk.fishfish.easyjava.kafka;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.test.context.junit4.SpringRunner;
/**
* 消费者
*
* @author 奔波儿灞
* @since 1.0
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class ConsumerTest {
private static final Logger LOG = LoggerFactory.getLogger(ConsumerTest.class);
@KafkaListener(topics = "test")
public void onMessage(ConsumerRecord<String, String> record) {
LOG.info("record: {}", record);
String value = record.value();
if (value.length() % 2 == 0) {
throw new RuntimeException("模拟业务出错");
}
}
@Test
public void run() {
try {
// 阻塞5分钟,方便调试
Thread.sleep(5 * 60 * 1000);
} catch (InterruptedException e) {
LOG.warn("sleep error", e);
}
}
}
上面通过@KafkaListener
来监听topic
,处理消息。
为了模拟业务会出现一些异常,我特意在判断value长度为偶数的情况下抛出异常,看在默认配置的情况下,如果业务出错,是否仍会提交offsets
。
结果发现,仍提交了offsets。
下面是日志:
INFO 2019-05-17 10:25:48.557 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] tk.fishfish.easyjava.kafka.ConsumerTest - record: ConsumerRecord(topic = test, partition = 0, offset = 261, CreateTime = 1558059947687, serialized key size = -1, serialized value size = 0, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = )
ERROR 2019-05-17 10:25:48.557 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] o.s.kafka.listener.LoggingErrorHandler - Error while processing: ConsumerRecord(topic = test, partition = 0, offset = 261, CreateTime = 1558059947687, serialized key size = -1, serialized value size = 0, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = )
org.springframework.kafka.listener.ListenerExecutionFailedException: Listener method 'public void tk.fishfish.easyjava.kafka.ConsumerTest.onMessage(org.apache.kafka.clients.consumer.ConsumerRecord<java.lang.String, java.lang.String>)' threw exception; nested exception is java.lang.RuntimeException: 模拟业务出错
at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:302)
at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:79)
at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:50)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeOnMessage(KafkaMessageListenerContainer.java:1220)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeOnMessage(KafkaMessageListenerContainer.java:1213)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeRecordListener(KafkaMessageListenerContainer.java:1174)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeWithRecords(KafkaMessageListenerContainer.java:1155)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeRecordListener(KafkaMessageListenerContainer.java:1096)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.invokeListener(KafkaMessageListenerContainer.java:924)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.pollAndInvoke(KafkaMessageListenerContainer.java:740)
at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.run(KafkaMessageListenerContainer.java:689)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: 模拟业务出错
at tk.fishfish.easyjava.kafka.ConsumerTest.onMessage(ConsumerTest.java:29)
at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:170)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:120)
at org.springframework.kafka.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:48)
at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:283)
... 13 common frames omitted
DEBUG 2019-05-17 10:25:48.557 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] o.s.k.l.a.RecordMessagingMessageListenerAdapter - Processing [GenericMessage [payload=d, headers={kafka_offset=262, kafka_consumer=org.apache.kafka.clients.consumer.KafkaConsumer@f46d581, kafka_timestampType=CREATE_TIME, kafka_receivedMessageKey=null, kafka_receivedPartitionId=0, kafka_receivedTopic=test, kafka_receivedTimestamp=1558059947851}]]
INFO 2019-05-17 10:25:48.557 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] tk.fishfish.easyjava.kafka.ConsumerTest - record: ConsumerRecord(topic = test, partition = 0, offset = 262, CreateTime = 1558059947851, serialized key size = -1, serialized value size = 1, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = d)
DEBUG 2019-05-17 10:25:48.557 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] o.s.k.l.KafkaMessageListenerContainer$ListenerConsumer - Commit list: {test-0=OffsetAndMetadata{offset=263, metadata=''}}
DEBUG 2019-05-17 10:25:48.557 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] o.s.k.l.KafkaMessageListenerContainer$ListenerConsumer - Committing: {test-0=OffsetAndMetadata{offset=263, metadata=''}}
DEBUG 2019-05-17 10:25:48.560 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] o.a.k.c.consumer.internals.ConsumerCoordinator - [Consumer clientId=consumer-2, groupId=myGroup] Committed offset 263 for partition test-0
从日志可以看到offset = 261
的记录处理失败了,但最后仍提交了Committed offset 263 for partition test-0
。
总结:如果你采取我这样的配置,当处理record
出错的时候,仍会提交偏移量。那么我们就需要业务处理失败的情况了。比如try...catch
之后保存错误的record
,然后定时重试。
出错的情况下不提交offsets
那么,能不能在出错的情况下不提交咧?
通过查看文档发现,发现可以使用Acknowledgment
去确认该条record
是否提交。
修改下配置,配置spring.kafka.listener.*
:
spring:
kafka:
consumer:
bootstrap-servers:
- 127.0.0.1:9092
group-id: myGroup
# 消费者是否自动提交偏移量,默认为true
enable-auto-commit: false
# 消费者在读取一个没有偏移量或者偏移量无效的情况下,从起始位置读取partition的记录,默认是latest
auto-offset-reset: earliest
# 单次调用poll方法能够返回的消息数量
max-poll-records: 50
listener:
# Listener AckMode
ack-mode: MANUAL_IMMEDIATE
# 并发消费者
concurrency: 1
然后代码有些调整(就不贴全了),使用Acknowledgment
:
@KafkaListener(topics = "test")
public void onMessage(ConsumerRecord<String, String> record, Acknowledgment ack) {
LOG.info("record: {}", record);
String value = record.value();
if (value.length() % 2 == 0) {
throw new RuntimeException("模拟业务出错");
}
// 业务处理成功确认
ack.acknowledge();
}
如果在业务出错的情况下,不会提交offsets
,然而真的是这样的吗?
测试发现,在业务出错的情况下,确实不会提交offsets
,但是只要后面的记录处理成功,就会提交offsets
,这样前面的失败的数据还是需要自己去手动处理。要么重新获取该offset
的数据,要么记录错误record
,业务重试。
总结
在错误的情况下,建议自己业务记录后,去重试;或者使用spring-kafka
的ErrorHandler
,处理错误的情况。
spring-kafka的更多相关文章
- Spring Kafka和Spring Boot整合实现消息发送与消费简单案例
本文主要分享下Spring Boot和Spring Kafka如何配置整合,实现发送和接收来自Spring Kafka的消息. 先前我已经分享了Kafka的基本介绍与集群环境搭建方法.关于Kafka的 ...
- Spring Kafka整合Spring Boot创建生产者客户端案例
每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 创建一个kafka-producer-master的maven工程.整个项目结构如下: ...
- Spring Kafka中关于Kafka的配置参数
#################consumer的配置参数(开始)################# #如果'enable.auto.commit'为true,则消费者偏移自动提交给Kafka的频率 ...
- Docker部署Kafka以及Spring Kafka操作
从https://hub.docker.com/ 查找kafka 第三个活跃并stars数量多 进去看看使用 我们使用docker-compose来构建镜像 查看使用文档中的docker-compos ...
- spring kafka生产、消费消息
参考网址: https://blog.csdn.net/lansetiankong12/article/details/54946641 1.新建Maven项目-KafkaMaven ->点击n ...
- Spring XD 1.1 M2 and 1.0.3 released---support kafka
官方地址:http://spring.io/blog/2014/12/23/spring-xd-1-1-m2-and-1-0-3-released On behalf of the Spring XD ...
- Spring for Apache Kafka
官方文档详见:http://docs.spring.io/spring-kafka/docs/1.0.2.RELEASE/reference/htmlsingle/ Authors Gary Russ ...
- Spring Boot Kafka
1.创建集群 http://kafka.apache.org/documentation/#quickstart 有一句我觉得特别重要: For Kafka, a single broker is j ...
- spring boot与kafka
1.项目搭建 2.关键代码与配置 3.性能调优 注意,本项目基于spring boot 1,如果是spring boot 2有可能会报错.相应的包需要更新 1.项目搭建 kafka版本:kafka_2 ...
- spring boot+kafka整合
springboot版本是2.0.4 首先,在maven中引入spring-kafka的jar包 <dependency> <groupId>org.springframewo ...
随机推荐
- CF949B A Leapfrog in the Array
思路: 最终的时候,对于位置p,若p是奇数,则该位置的元素是(p + 1) / 2:若p是偶数,需要从p开始不断地迭代寻找上一次跳跃所处的位置(p = p + n - p / 2),直到p是奇数为止. ...
- Eclipse打包多渠道包(库工程版)
请先移步多渠道打包http://www.cnblogs.com/bhm666/p/6438776.html 自从上次使用了Gradle打渠道包后,遇到了各种各样的问题,不过也是小问题,仍然在几个项目上 ...
- OC 实现一个TODO宏
实现一个TODO宏 转载http://blog.sunnyxx.com/2015/03/01/todo-macro/ 实现一个能产生warning的TODO宏,用于在代码里做备忘,效果: 下面一步步来 ...
- CentOS 7 samba server 配置
samba是linux上的文件共享服务软件,相当与Windows上的共享文件夹,当然也是要在同一网段上的. 当前用的版本是4.4.4,好吧!下面介绍怎么去安装配置它,here we go! 1. 安装 ...
- QList模板类常用接口函数
插入操作:insert()函数原型:void QList::insert(int i, const T &value) 在索引后插入值 i:索引 value:插入值 Example: QLis ...
- ascii - 在八进制,十进制,十六进制中的 ASCII 字符集编码
描述 ASCII 是美国对于信息交换的标准代码,它是7位码,许多8位码(比如 ISO 8859-1, Linux 的默认字符集)容纳 ASCII 作为它们的下半部分.对应的国际 ASSII 是 ISO ...
- Adobe Dreamweaver CC 2014 代码颜色目录 dw
他的颜色代码配置文件,不在安装目录下,这让我好找啊~ C:\Users\Administrator\AppData\Roaming\Adobe\Dreamweaver CC 2014\zh_CN\Co ...
- CE工具里自带的学习工具--第五关
图解: 此时会弹出一个对话框,选择是就可以了,最终会看到:
- java正则表达式进阶运用20181023
直接上代码. package org.jimmy.autotranslate20181022.test; import java.util.regex.Matcher; import java.uti ...
- print keys %map_function 输出 散列的值: OK_funcsplit_funcpackage_VAR
my %map_function = ( 88 "OK_func" => "open_statement", 89 &qu ...