如果消费者 运行时候 报错了
packagecom.toov5.msg.SMS;importorg.springframework.amqp.rabbit.annotation.RabbitHandler;importorg.springframework.amqp.rabbit.annotation.RabbitListener;importorg.springframework.stereotype.Component;
@Component
@RabbitListener(queues="fanout_sms_queue")public classSMSConsumer {
@RabbitHandlerpublic voidprocess(String mString) {
System.out.println("短信消费者获取生产者消息msg"+mString);int i = 1/0;
}
}
当生产者投递消息后:
消费者会不停的进行打印:
消息一直没有被消费
原因 Rabbitmq 默认情况下 如果消费者程序出现异常情况 会自动实现补偿机制 也就是 重试机制
@RabbitListener底层使用AOP进行拦截,如果程序没有抛出异常,自动提交事务。 如果Aop使用异常通知 拦截获取异常信息的话 , 自动实现补偿机制,该消息会一直缓存在Rabbitmq服务器端进行重放,一直重试到不抛出异常为准。
可以修改重试策略
一般来说默认5s重试一次,
消费者配置:
listener:
simple:
retry:
####开启消费者重试
enabled: true
####最大重试次数(默认无数次)
max-attempts: 5
####重试间隔次数
initial-interval: 3000
效果: 充实5次 不行就放弃了
MQ重试机制机制 需要注意的问题
如何合适选择重试机制
情况1: 消费者获取到消息后,调用第三方接口,但接口暂时无法访问,是否需要重试?
需要重试 别人的问题不是我自己的问题
情况2: 消费者获取到消息后,抛出数据转换异常,是否需要重试?
不需要重试 充实一亿次也是如此 木有必要 需要发布版本解决
总结:
对于情况2,如果消费者代码抛出异常是需要发布新版本才能解决的问题,那么不需要重试,重试也无济于事。应该采用 日志记录+定时任务job健康检查+人工进行补偿
把错误记录在日志里面,通过定时Job去自动的补偿,或通过人工去补偿。
传统的HTTP请求 如果失败了没法自动重试 ,当然自己可以写个循环实现。MQ完全自己自带的。
情况2的拓展延申:
将之前的案例改为 邮件消费者 调用邮件第三方接口
伪代码:
在consumer 中 调用接口后 判断返回值 由于RabbitMQ 在消费者异常时候 会进行重试机制 进行补偿
所以可以抛出个异常 来实现
Consumer:
String result = template.Email();
if(result == null){
throw new Exception("调用第三方邮件服务器接口失败!");
}
producer:
pom:
4.0.0
com.itmayiedu
rabbitmq_producer_springboot
0.0.1-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
2.0.0.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-amqp
org.apache.commons
commons-lang3
com.alibaba
fastjson
1.2.49
config:
packagecom.itmayiedu.rabbitmq.config;importorg.springframework.amqp.core.Binding;importorg.springframework.amqp.core.BindingBuilder;importorg.springframework.amqp.core.FanoutExchange;importorg.springframework.amqp.core.Queue;importorg.springframework.context.annotation.Bean;importorg.springframework.stereotype.Component;//Fanout 类型 发布订阅模式
@Componentpublic classFanoutConfig {//邮件队列
private String FANOUT_EMAIL_QUEUE = "fanout_email_queue";//短信队列
private String FANOUT_SMS_QUEUE = "fanout_sms_queue";//fanout 交换机
private String EXCHANGE_NAME = "fanoutExchange";//1.定义邮件队列
@BeanpublicQueue fanOutEamilQueue() {return newQueue(FANOUT_EMAIL_QUEUE);
}//2.定义短信队列
@BeanpublicQueue fanOutSmsQueue() {return newQueue(FANOUT_SMS_QUEUE);
}//2.定义交换机
@Bean
FanoutExchange fanoutExchange() {return newFanoutExchange(EXCHANGE_NAME);
}//3.队列与交换机绑定邮件队列
@Bean
Binding bindingExchangeEamil(Queue fanOutEamil