一次kafka消息丢失问题处理
背景&现象
生产微服务架构环境,kafka消息消费服务架构如下:

当服务B接口出现宕机或者B接口调用超时,kafka消息消费端服务A出现异常,异常发生后未执行手动提交offset操作。待服务B恢复后,消费端A服务也恢复正常,但之前消费异常的消息在broker自动变为已消费,实际未消费(数据库中无处理消息的业务数据)。
问题原因
问题定位
在灰度环境复刻了一样的操作,100%复现此问题。增加了debug打印日志,确认了业务代码在异常后,仍继续消费服务端产生的消息,只不过会抱异常,具体业务代码如下:
@KafkaListener(containerFactory = "kafkaBatchListener", topics = {CAMERA_DEV_FAULT})
public void alarmListener(List<ConsumerRecord<?, ?>> records, Acknowledgment ack) throws Exception{
try{
//1.处理
for (ConsumerRecord consumerRecord : records) {
Long startTime = (new Date()).getTime();
log.info("recv message offset" + String.valueOf(consumerRecord.offset())+ " | " + consumerRecord.partition() +
" | " + consumerRecord.topic() + " | " + consumerRecord.value());
JSONObject msg = JSONObject.fromObject(consumerRecord.value());
uCameraAlarmProcessor.process(msg);
// List ss = new ArrayList();
// System.out.println("=========================="+ss.get(5).toString());
Long endTime = (new Date()).getTime();
log.info("recv message offset222 = " + consumerRecord.offset() + "| oprTime = " + (endTime-startTime));
}
//2.消费提交偏移量
ack.acknowledge();
log.info("OK records {}",records.size());
} catch (DuplicateKeyException e){
ack.acknowledge();
log.info("Exception 1");
e.printStackTrace();
} catch (Exception e){
log.info("Exception 2"+e.getMessage());
throw e;
}
}
spring-kafka配置信息如下:
private Map<String, Object> consumerConfigs() {
Map<String, Object> props = new HashMap<>(10);
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitInterval);
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, autoCommit);
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset);
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, sessionTimeout);
props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, maxPollInterval);
props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, maxPartitionFetchBytes);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.GROUP_ID_CONFIG,groupId);
props.put("security.protocol", "SASL_PLAINTEXT");
props.put("sasl.mechanism", "SCRAM-SHA-512");
props.put("sasl.jaas.config",
"org.apache.kafka.common.security.scram.ScramLoginModule required username=\""+username+"\" password=\""+password+"\";");
return props;
}
private ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
//批量消费
factory.setBatchListener(batchListener);
//如果消息队列中没有消息,等待timeout毫秒后,调用poll()方法。
// 如果队列中有消息,立即消费消息,每次消费的消息的多少可以通过max.poll.records配置。
//手动提交无需配置
factory.getContainerProperties().setPollTimeout(pollTimeout);
//设置提交偏移量的方式, MANUAL_IMMEDIATE 表示消费一条提交一次;MANUAL表示批量提交一次
// factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
// factory.getContainerProperties().setCommitLogLevel(LogIfLevelEnabled.Level.INFO);
// factory.getContainerProperties().setLogContainerConfig(true);
factory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.MANUAL_IMMEDIATE);
return factory;
}
结合的spring-kafka源码如下:


设置客户端消费线程记录的offse位置,此处是在消息处理之前进行更新,待消息处理结束后的回掉进行提交broker。消息处理内部如果发生异常并被捕获的话,此处拿不到回掉,所以只会记录offset变化。



消费工厂初始化消费容器,每个容器的start和stop都是对象锁同步,每个容器绑定一个消费者线程,这里执行一个消息分区的消息拉取,提交等操作。

消息拉取后,提交给业务消费者实现,等待结果回掉,并提交offset.
详细原因思考
当服务C接口出现宕机或者接口超时情况时,kafka消息处理服务业务逻辑内部出现处理异常,虽然未执行手动提交offset的操作,但是内部抛出的异常被catch住然后继续消费产生的新消息,并且消费端继续的offset一只在增加,只是为提交broker。
此时一旦下游服务恢复,kafka消费端会直接把本地存储的最新offset提交到broker,服务端在收到同一分区offset后会把之前的offset重置,并把小于此offset的消息全部状态置为已消费,导致之前处理失败的offset消息丢失的情况。示意图如下:

解决方案
高清问题原因后,我们可以针对具体原因进行解决,解题思路有2种
死信队列
这样就是把处理异常的消息写入死信队列,待服务恢复后重新消费私信队列中的异常消息,这种方案有2点问题:
1、需要写新的处理逻辑死信队列逻辑代码,并定义新的topic
2、此方案适合无唯一主键的消息处理
异常监听器
解决业务消息处理异常时不要自增本地的offset,这样即使服务恢复后本地客户端仍旧从异常的消息节点开始消费,消费成功时才能提交offset。
弊端是服务在异常时会进入死循环模式,即同一分区一只在消费一条消息;
优点是只需要引入spring-kafka异常监听器对异常消息进行处理即可,不需要引入复杂的代码和更多的消息topic。
我是采用第二种解决方案,经过测试无误后发布到生产环境,代码如下:

首先增加未知异常抛出逻辑,其次定义异常处理errorHandler。
异常处理类
@Component
public class ErrorListenner {
private static final Logger log= LoggerFactory.getLogger(ErrorListenner.class);
@Bean
public ConsumerAwareListenerErrorHandler consumerAwareErrorHandler() {
return new ConsumerAwareListenerErrorHandler() {
@Override
public Object handleError(Message<?> message, ListenerExecutionFailedException e, Consumer<?, ?> consumer) {
log.info("consumerAwareErrorHandler receive : "+message.getPayload().toString());
LinkedList<ConsumerRecord> records = (LinkedList)message.getPayload();
ConsumerRecord consumerRecord = records.get(0);
Long offset = consumerRecord.offset();
String topic = consumerRecord.topic();
Integer partition = consumerRecord.partition();
// Map<TopicPartition, OffsetAndMetadata> offsetsToReset = new HashMap<>();
// 设置每个分区的偏移量
TopicPartition topicPartition = new TopicPartition(topic, partition);
// OffsetAndMetadata offsetAndMetadata = new OffsetAndMetadata(offset, "manual_immediate");
// offsetsToReset.put(topicPartition, offsetAndMetadata);
log.info("重置客户端offset位置 topic {} partition {} {}",topic, partition,offset);
// acknowledgment.acknowledge();
consumer.seek(topicPartition,offset);
// consumer.commitSync(offsetsToReset);
return null;
}
};
}
}
一次kafka消息丢失问题处理的更多相关文章
- Kafka消息丢失
1.Kafka消息丢失的情况: (1)auto.commit.enable=true,消费端自动提交offersets设置为true,当消费者拉到消息之后,还没有处理完 commit interval ...
- 实际业务处理 Kafka 消息丢失、重复消费和顺序消费的问题
关于 Kafka 消息丢失.重复消费和顺序消费的问题 消息丢失,消息重复消费,消息顺序消费等问题是我们使用 MQ 时不得不考虑的一个问题,下面我结合实际的业务来和你分享一下解决方案. 消息丢失问题 比 ...
- 一文了解清楚kafka消息丢失问题和解决方案
前言 今天分享一下kafka的消息丢失问题,kafka的消息丢失是一个很值得关注的问题,根据消息的重要性,消息丢失的严重性也会进行放大,如何从最大程度上保证消息不丢失,要从生产者,消费者,broker ...
- 【消息队列面试】11-14:kafka高可靠、高吞吐量、消息丢失、消费模式
十一.kafka消息高可靠的解决方案 1.高可靠=避免消息丢失 解决消息丢失的问题 2.如何解决 (1)保证消息发送是可靠的(发成功了/落到partition) a.ack参数 发送端,采用ack机制 ...
- Kafka无消息丢失配置
Kafka到底会不会丢数据(data loss)? 通常不会,但有些情况下的确有可能会发生.下面的参数配置及Best practice列表可以较好地保证数据的持久性(当然是trade-off,牺牲了吞 ...
- kafka消息会不会丢失
转载:https://baijiahao.baidu.com/s?id=1583469327946027281&wfr=spider&for=pc 消息发送方式 想清楚Kafka发送的 ...
- Kafka leader副本选举与消息丢失场景讨论
如果某个broker挂了,leader副本在该broker上的分区就要重新进行leader选举.来简要描述下leader选举的过程 1.4.1 KafkaController会监听ZooKeeper的 ...
- kafka系列八、kafka消息重复和丢失的场景及解决方案分析
消息重复和丢失是kafka中很常见的问题,主要发生在以下三个阶段: 生产者阶段 broke阶段 消费者阶段 一.生产者阶段重复场景 1.根本原因 生产发送的消息没有收到正确的broke响应,导致pro ...
- Kafka设计解析(十一)Kafka无消息丢失配置
转载自 huxihx,原文链接 Kafka无消息丢失配置 目录 一.Producer端二.Consumer端 Kafka到底会不会丢数据(data loss)? 通常不会,但有些情况下的确有可能会发生 ...
- Kafka在高并发的情况下,如何避免消息丢失和消息重复?kafka消费怎么保证数据消费一次?数据的一致性和统一性?数据的完整性?
1.kafka在高并发的情况下,如何避免消息丢失和消息重复? 消息丢失解决方案: 首先对kafka进行限速, 其次启用重试机制,重试间隔时间设置长一些,最后Kafka设置acks=all,即需要相应的 ...
随机推荐
- Fabric区块链浏览器(2)
本文是区块链浏览器系列的第四篇. 在上一篇文章介绍如何解析区块数据时,使用session对客户端上传的pb文件进行区分,到期后自动删除. 在这片文章中,会着重介绍下认证系统的实现,主要分为三部分: 添 ...
- 🛠 Open Source Instant Messaging (IM) Project OpenIM Source Code
Deploying OpenIM involves multiple components and supports various methods, including source code, D ...
- 开源IM项目OpenIM发布消息推送api,支持应用与IM互通深度融合
以办公场景为例,比如员工入职通知,放假通知等业务通知,由oa系统处理具体的业务逻辑,再调用消息推送api,触达到目标用户. 效果示例 以协同办公为例,员工收到系统推送的工作通知,有新任务需要处理. 员 ...
- c++基础之表达式
这次接着更新<c++ primer> 这本书的读书笔记,上一篇博文更新到了书中的第三章,本次将记录书中的第四章--表达式 左值与右值 在理解表达式之前需要先理解c++中左值和右值的概念. ...
- 手撕Vue-监听数据变化
经过上一篇的介绍,已经实现了将模板编译成具体数据,接下来要介绍的是如何监听数据的变化,本章主要完成这个需求即可. 在我们文章的开始,我写了一个 Vue 双向数据绑定原理的文章当中封装了一个 Obser ...
- TienChin 活动管理-工程创建
工程模块的创建,与之前创建渠道管理一样的,所以这里就不贴图带着大家一起动手进行了,表结构我已经给到大家了,这里我只贴一下代码生成器的代码部分即可. @Test void generateActivit ...
- 基于AvaloniaUI开发跨平台.NET三维应用:环境搭建
本文介绍在Vistual Studio 2022中使用Avalonia和集成AnyCAD AvaloniaUI三维控件的的过程. 目录 0 初始化环境 1 创建项目 2 集成AnyCAD Avalon ...
- 从CPU100%高危故障到稳定在10%:一个月的优化之旅,成功上线!
引言 经过三个月的开发,项目通过了所有测试并上线,然而,我们发现项目的首页几乎无法打开,后台一直发生超时错误,导致CPU过度负荷.在这次项目开发过程中,我制定了一份详细的技术优化方案.考虑到客户无法提 ...
- USACO 2022 Cu 题解
USACO 2022 Cu 题解 AK用时:$ 3 $ 小时 $ 30 $ 分钟. A - Cow College 原题 Farmer John 计划为奶牛们新开办一所大学! 有 $ N $($ 1 ...
- .NET 云原生架构师训练营(模块二 基础巩固 EF Core 关系)--学习笔记
2.4.4 EF Core -- 关系 一对多 一对一 多对多 示例 关系:https://docs.microsoft.com/zh-cn/ef/core/modeling/relationship ...