@RabbitListener处理重试机制完成后的异常捕获

  • application.properties中配置开启手动签收
java 复制代码
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.acknowledge-mode=manual
  • 定义一个重试器
java 复制代码
@Slf4j
@Configuration
public class RabbitMQRetryConfing {

    @Bean("customRetry")
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3); // 设置重试次数
        retryTemplate.setRetryPolicy(retryPolicy);

        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(2000); // 重试间隔,单位:毫秒
        retryTemplate.setBackOffPolicy(backOffPolicy);

        // 添加 RetryListener 以观察重试过程
        retryTemplate.registerListener(new RetryListener() {
            @Override
            public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                return true;
            }

            @Override
            public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
            }

            @Override
            public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                //春姐,这里可以灵活配置重试失败后的回调,例如发送告警、更新数据库状态等
                if (context.getRetryCount() >= retryPolicy.getMaxAttempts()) {
                    int retryCount = context.getRetryCount();
                    // 当重试次数耗尽时进行处理
                    log.info("已达到最大重试次数{}次,丢弃本次任务",retryCount);
                }
            }
        });

        return retryTemplate;
    }
}

这个重试器可以配置最大重试次数、重试之间间隔次数等策略配置,调用重试器的execute方法可以进行队列消费,如果在执行一次任务期间发生了异常,则会根据配置的重试次数以及间隔时间自动触发下一次重试,每一次重试都是在同一个线程中执行完成的,并且RetryTemplate会为每一次重试失败进行回调,提供了诸如 onOpen、onClose、onError等回调时机。

  • RabbitMQ监听器回调方法
java 复制代码
	@Autowired
    @Qualifier("customRetry")
    private RetryTemplate retryTemplate;

    //queues消费的队列  ackMode确认模式 MANUAL 手动确认
    @RabbitListener(queues = "q",ackMode = "MANUAL")
    @Override
    public void onMessage(Message message, Channel channel)   {
        try {
            retryTemplate.execute(new RetryCallback<Object, Throwable>() {
                @Override
                public Object doWithRetry(RetryContext retryContext) throws Throwable {
                    // 消息的唯一标识id
                    long deliveryTag = message.getMessageProperties().getDeliveryTag();
                    log.info("接收到mq信息" + new String(message.getBody()));
                    try{
                        //todo 开始业务处理
                        String msg = new String(message.getBody());
                        Integer articleId = Integer.parseInt(msg);
                        newsService.pullNews(articleId);
                        // 手动签收的第一个参数为消息的唯一标识id、第二个参数表示是否批量签收
                        channel.basicAck(deliveryTag,false);
                        log.info("消费mq消息成功,articleId为:{}",articleId);
                        return null;
                    }catch (Throwable e){
                        log.error(String.format("失败,异常信息为:%s",new String(message.getBody()),e.getMessage()));
                        //重新抛出异常  触发重试机制
                        throw e;
                    }
                    finally {
                        //重试次数达到限制
                        log.error(String.format("失败",new String(message.getBody())));
                        //不重新入队,发送到死信队列
                        // 手动拒绝签收的第一个参数为消息id、
                        // 第二个参数表示是否批量签收
                        // 第三个参数消息是否重回队列
                        channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
                    }
                }
            });
        } catch (Throwable throwable) {
            log.info("执行回调重试上下文出现异常");
        }

    }

这样,当耗尽完重试次数之后就会被回调到onError方法中执行自定义的异常逻辑处理。

相关推荐
SelectDB2 分钟前
Apache Doris 中的 Data Trait:性能提速 2 倍的秘密武器
数据库·后端·apache
zhengzizhe11 分钟前
LangGraph4j LangChain4j JAVA 多Agent编排详解
java·后端
程序员鱼皮16 分钟前
又被 Cursor 烧了 1 万块,我麻了。。。
前端·后端·ai·程序员·大模型·编程
embrace9922 分钟前
【C语言学习】结构体详解
android·c语言·开发语言·数据结构·学习·算法·青少年编程
无心水24 分钟前
【Python实战进阶】4、Python字典与集合深度解析
开发语言·人工智能·python·python字典·python集合·python实战进阶·python工业化实战进阶
福大大架构师每日一题25 分钟前
2025-11-27:为视频标题生成标签。用go语言,给定一个字符串 caption(视频标题),按下面顺序处理并输出一个标签: 1. 将标题中的各个词合并成一
后端
程序员爱钓鱼26 分钟前
Go语言 OCR 常用识别库与实战指南
后端·go·trae
tonydf32 分钟前
动态表单之后:如何构建一个PDF 打印引擎?
后端
allbs34 分钟前
spring boot项目excel导出功能封装——4.导入
spring boot·后端·excel
代码不停39 分钟前
Java单链表和哈希表题目练习
java·开发语言·散列表