Kafka 消费端消费重试和死信队列

系列文章目录


文章目录


前言

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。
在这里插入图片描述


Spring-Kafka 提供消费重试的机制。当消息消费失败的时候,Spring-Kafka 会通过消费重试机制,重新投递该消息给 Consumer ,让 Consumer 重新消费消息 。

默认情况下,Spring-Kafka 达到配置的重试次数时,【每条消息的失败重试时间,由配置的时间隔决定】Consumer 如果依然消费失败 ,那么该消息就会进入到死信队列。

Spring-Kafka 封装了消费重试和死信队列, 将正常情况下无法被消费的消息称为死信消息(Dead-Letter Message),将存储死信消息的特殊队列称为死信队列(Dead-Letter Queue)。

我们在应用中可以对死信队列中的消息进行监控重发,来使得消费者实例再次进行消费,消费端需要做幂等性的处理。
在这里插入图片描述
引入POM依赖

<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
    <version>2.8.6</version>
</dependency>

YML配置文件

这里Kafka也安装到了本地,Kafka安装Windows版:http://www.javacui.com/tool/667.html 。

# Web配置
server:
  servlet:
    context-path: /
  port: 1088
# kafka 配置
spring:
  kafka:
    bootstrap-servers: 127.0.0.1:9092
    consumer:
      enable-auto-commit: true # 是否自动提交offset
      auto-commit-interval: 100  # 提交offset延时(接收到消息后多久提交offset)
      # earliest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
      # latest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
      # none:topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
      auto-offset-reset: latest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      # 表示接受反序列化任意的类,也可限定包路径
      properties:
        spring:
          json:
            trusted:
              packages: '*'
    producer:
      retries: 0 # 重试次数
      # 0:producer不等待broker的ack,broker一接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据;
      # 1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据;(数据要求快,重要性不高时用)
      # -1:producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack,数据一般不会丢失,延迟时间长但是可靠性高。(数据重要时用)
      acks: 1 # 应答级别:多少个分区副本备份完成时向生产者发送ack确认(可选0、1、all/-1,0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。)
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
    listener:
      missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错

配置ErrorHandler,用于定制重试次数和间隔时间

package com.example.springboot.config.kafka;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.listener.ConsumerRecordRecoverer;
import org.springframework.kafka.listener.DeadLetterPublishingRecoverer;
import org.springframework.kafka.listener.ErrorHandler;
import org.springframework.kafka.listener.SeekToCurrentErrorHandler;
import org.springframework.util.backoff.BackOff;
import org.springframework.util.backoff.FixedBackOff;
 
/**
 * Spring-Kafka 通过实现自定义的 SeekToCurrentErrorHandler ,当 Consumer 消费消息异常的时候,进行拦截处理:
 * 重试小于最大次数时,重新投递该消息给 Consumer
 * 重试到达最大次数时,如果Consumer 还是消费失败时,该消息就会发送到死信队列。 死信队列的 命名规则为: 原有 Topic + .DLT 后缀 = 其死信队列的 Topic
 */
 
@Configuration
public class KafkaConfiguration {
   
    private Logger logger = LoggerFactory.getLogger(getClass());
 
    @Bean
    @Primary
    public ErrorHandler kafkaErrorHandler(KafkaTemplate<?, ?> template) {
   
        logger.warn("kafkaErrorHandler begin to Handle");
        // <1> 创建 DeadLetterPublishingRecoverer 对象
        ConsumerRecordRecoverer recoverer = new DeadLetterPublishingRecoverer(template);
        // <2> 创建 FixedBackOff 对象   设置重试间隔 10秒 次数为 1 次
        // 创建 DeadLetterPublishingRecoverer 对象,它负责实现,在重试到达最大次数时,Consumer 还是消费失败时,该消息就会发送到死信队列。
        // 注意,正常发送 1 次,重试 1 次,等于一共 2 次
        BackOff backOff = new FixedBackOff(10 * 1000L, 1L);
        // <3> 创建 SeekToCurrentErrorHandler 对象
        return new SeekToCurrentErrorHandler(recoverer, backOff);
    }
}

编写生产者代码

package com.example.springboot.controller;
 
import cn.hutool.core.date.DateUtil;
import com.example.springboot.model.Blog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.Date;
 
@RestController
@RequestMapping("/test/kafka")
public class MessSendController {
   
    @Autowired
    private KafkaTemplate kafkaTemplate;
    private static final String messTopic = "test";
 
    @RequestMapping("/send")
    public String sendMess() {
   
        Blog blog = Blog.builder().id(1).name("测试").isDel(false).birthday(new Date()).build();
        kafkaTemplate.send(messTopic, blog);
        System.out.println("客户端 消息发送完成");
        return DateUtil.now();
    }
}

这里请求路径:http://localhost:1088/test/kafka/send 时,会发送一条消息到队列。

编写消费端代码

package com.example.springboot.listener;
 
import com.alibaba.fastjson.JSON;
import com.example.springboot.model.Blog;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
 
@Component
public class KafkaMessListener {
   
    private static final String messTopic = "test";
 
    @KafkaListener(id="KafkaMessListener", topics = messTopic
  • 42
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Kafka消费重试机制可以通过建立一个专门用于重试的topic(retry topic)来实现。当消费者没有正确消费一条消息时,将该消息转发(发布)到重试主题(retry topic)上,并提交消息的偏移量,以便继续处理下一个消息。这个时候,这个没有正确消费的消息,对于这个消费者来说,也算是消费完成了,因为也正常提交了偏移量,只不过是业务没有正确处理,而且这个消息被发布到另一个topic中了(retry topic)。之后再创建一个重试消费者,用于订阅这个重试主题,只不过这个重试消费者,跟之前那个消费者处理相同的业务,两个逻辑是一样的。如果这个重试消费者也无法消费这条消息,那就把这个消息发布到另一个重试主题上,并提交该消息的偏移量。循环,递归。最后,当创建了很多重试消费者的时候,在最终重试消费者无法处理某条消息后,把该消息发布到一个死信队列(DLQ)。 ```shell # 代码示例 # 创建一个专门用于重试的topic bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic retry_topic # 消费者处理消息时,如果出现异常,将消息转发到重试主题上 # 代码示例 try: # 处理消息的业务逻辑 except Exception as e: # 将消息转发到重试主题上 producer.send('retry_topic', value=message.value, key=message.key) # 创建一个重试消费者,用于订阅重试主题 # 代码示例 consumer = KafkaConsumer('retry_topic', bootstrap_servers=['localhost:9092'], group_id='retry_group') for message in consumer: try: # 处理消息的业务逻辑 except Exception as e: # 将消息转发到另一个重试主题上 producer.send('retry_topic_2', value=message.value, key=message.key) # 提交消息的偏移量 consumer.commit() # 将无法处理的消息发布到死信队列 # 代码示例 producer.send('dead_letter_queue', value=message.value, key=message.key) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java毕设王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值