如何确保消息至少消费一次?

本文探讨了消息队列中消息至少消费一次的保障机制,分析了push和pull两种消费方式下消息消费失败的处理策略,包括重试机制、延迟处理和本地消息落地后的异步消费,以确保消息消费的可靠性。

1. 如何确保消息至少消费一次,确保消费者最大程度消费成功

消费者消费消息有2种方式:

1. push方式

消息服务接收到消息之后,主动将消息推送给消费者消费。

2. pull方式

消费者定时从消息服务中拉取消息进行消费。

下面我们将讨论2种方式如何确保消息至少被消费一次。

 

push模式

消费的过程:

1. 消息服务查询待消费的消息列表

2. 轮询待消息列表

3. 调用消费者

4. 消费者收到消费请求,执行业务处理,将处理结果返回给消息服务

5. 消息服务接收到消费成功的信息,将消息状态置为消费成功状态

6. 继续消费下一条消息

探讨一下上面需要考虑的问题:

若消息一直消费失败如何处理?

先说一下影响:

1. 消息被阻塞

消息如果一直消费失败,消息服务会不断调用消费者进行消费,会阻塞其他消息的消费,直接影响到业务的正常进行.

 

消费失败的原因:

1. 代码问题

这种情况不管尝试多少次,消息都会消费失败,需要人工介入修复bug,这个可以依靠监控系统发现bug,同时开发进行修复。

2. 系统运行异常

如调用超时、网络问题等一些不可控的因素。产生这种错误,继续重试,最终会处理成功。

 

此处咱们只用讨论消息服务中重试机制如何设计?

系统异常情况下,可能过一段时间,系统恢复了,此时去重试,消费也就成功了。

所以我们对于消费失败的消息采用延迟处理的方式,可以这么实现:

消息中增加几个字段用于重试:

next_dispose_time【下次处理时间】

max_failure【最大允许失败次数】

failure【当前失败次数】

消息入库时:

next_dispose_time=需消费的时间

max_failure = 运行最大失败次数,

failure=0;

 

当消费失败时,处理过程:

1. 计算下次处理时间(next_dispose_time),可以在当前时间上面做指数递增,比如根据失败次数依次在当前时间上递增2的failure次方秒,如:

第1次失败:当前时间 + 2秒

第2次失败:当前时间 + 4秒

第3次失败:当前时间 + 8秒

第4次失败:当前时间 + 16秒

.......

第n次失败:当前时间 + 2的n次方秒

 

2.failure++

 

消息服务查询待消费的消息也需要做调整:

select * from 消息表 where next_dispose_time<=当前时间 and failure<max_failure and status = 待处理;

此时能够最大程度保证消息最少消费成功一次。

 

pull方式

这种会复杂一些,为何会复杂一些,咱们先看一下常规的流程:

1. 消费者从消息服务中拉取消息

2. 本地进行处理

3. 从消息服务中删除此消息

4. 继续拉取下一条进行处理

如果本地一直处理失败,那么后面拉取到的都是同一条消息,这条消息直接阻塞后续消息的消费,这种情况如何解?

咱们先分析一下出现这种问题的后果及原因:

1. 后果:消息被阻塞,业务无法正常运行

2. 原因:代码问题或其他异常

3. 确保代码没问题,可以解决上面问题,及时性不够高,线上要考虑系统的容错能力。

 

遇到这种问题还是挺严重了,业务方都是无法接受的,一条消息消费失败,会影响到其他所有消息的消费,这个我们还是得想办法解决,可以这样:

1. 消费者拉取消息

2. 落地到本地

3. 从消息服务中删除此消息

4. 异步去消费本地落地的消息

消息先落地,然后异步处理,本地需要有个补偿的job,去处理本地消费失败的消息,这个可以参考push方式消费的过程。

是的,**Kafka消费者可以批量消费消息**,而不是必须逐条处理。这是通过消费者配置参数控制的,以下是具体实现方式和注意事项: --- ### 一、批量消费的3种实现方式 #### 1. 配置参数批量拉取(推荐) ```java @Bean public ConsumerFactory<String, String> consumerFactory() { Map<String, Object> props = new HashMap<>(); props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 100); // 单次拉取最大消息数 props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, 52428800); // 单次拉取最大字节数(50MB) props.put(ConsumerConfig.FETCH_MIN_BYTES_CONFIG, 1048576); // 至少累积1MB才返回 props.put(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG, 500); // 等待时间上限(ms) return new DefaultKafkaConsumerFactory<>(props); } ``` #### 2. Spring Kafka批量监听器 ```java @KafkaListener(topics = "my-topic", containerFactory = "batchFactory") public void batchConsume(List<ConsumerRecord<String, String>> records) { records.forEach(record -> { System.out.printf("Partition:%d Offset:%d Value:%s%n", record.partition(), record.offset(), record.value()); }); } // 配置批量容器工厂 @Bean public ConcurrentKafkaListenerContainerFactory<String, String> batchFactory() { ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(consumerFactory()); factory.setBatchListener(true); // 关键配置!启用批量模式 return factory; } ``` #### 3. 原生KafkaConsumer API ```java ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000)); for (ConsumerRecord<String, String> record : records) { // 批量处理逻辑 } ``` --- ### 二、批量消费 vs 单条消费对比 | 特性 | 批量消费 | 单条消费 | |---------------------|----------------------------------|-----------------------------| | 吞吐量 | 高(减少网络/IO开销) | 低 | | 延迟 | 较高(需等待消息累积) | 低(即时处理) | | 内存占用 | 大(需缓存批量消息) | 小 | | 异常处理 | 需自行实现批处理原子性 | 天然单条隔离 | | 适用场景 | 日志处理/ETL等吞吐敏感场景 | 交易类等延迟敏感场景 | --- ### 三、关键配置参数说明 | 参数名 | 默认值 | 建议值 | 作用说明 | |--------------------------------|---------|-------------|--------------------------------------------------------------------------| | `max.poll.records` | 500 | 100-1000 | 控制单次poll()返回的最大记录数 | | `fetch.min.bytes` | 1 | 102400(100KB)| broker需累积足够数据才返回 | | `fetch.max.wait.ms` | 500 | 300-1000 | 等待fetch.min.bytes的超时时间 | | `max.partition.fetch.bytes` | 1MB | 5-10MB | 每个partition返回的最大数据量 | --- ### 四、生产环境注意事项 1. **内存管理**: - 设置合理的`max.poll.records`防止OOM - 监控消费者组的`records-lag`指标 2. **异常处理**: ```java @KafkaListener(...) public void batchConsume(List<Message> messages) { try { // 批量处理逻辑 } catch (Exception e) { // 建议记录失败消息offset kafkaTemplate.send("dead-letter-topic", failedRecords); } } ``` 3. **提交策略**: - 改为手动提交(避免部分消息失败导致重复消费) ```properties spring.kafka.consumer.enable-auto-commit=false ``` --- ### 五、性能测试建议 ```bash # 使用kafka-producer-perf-test测试 bin/kafka-producer-perf-test.sh \ --topic test-topic \ --num-records 1000000 \ --record-size 1000 \ --throughput 50000 \ --producer-props bootstrap.servers=localhost:9092 # 观察消费者吞吐量 kafka-consumer-groups.sh --describe \ --group your-group \ --bootstrap-server localhost:9092 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值