本文转载自RabbitMq手动确认时的重试机制

消息手动确认模式的几点说明

  • 监听的方法内部必须使用channel进行消息确认,包括消费成功或消费失败

  • 如果不手动确认,也不抛出异常,消息不会自动重新推送(包括其他消费者),因为对于rabbitmq来说始终没有接收到消息消费是否成功的确认,并且Channel是在消费端有缓存的,没有断开连接

  • 如果rabbitmq断开,连接后会自动重新推送(不管是网络问题还是宕机)

  • 如果消费端应用重启,消息会自动重新推送

  • 如果消费端处理消息的时候宕机,消息会自动推给其他的消费者

  • 如果监听消息的方法抛出异常,消息会按照listener.retry的配置进行重发,但是重发次数完了之后还抛出异常的话,消息不会重发(也不会重发到其他消费者),只有应用重启后会重新推送。因为retry是消费端内部处理的,包括异常也是内部处理,对于rabbitmq是不知道的(此场景解决方案后面有)

  • spring.rabbitmq.listener.retry配置的重发是在消费端应用内处理的,不是rabbitqq重发

  • 可以配置MessageRecoverer对异常消息进行处理,此处理会在listener.retry次数尝试完并还是抛出异常的情况下才会调用,默认有两个实现:

    • RepublishMessageRecoverer:将消息重新发送到指定队列,需手动配置,如:
    @Bean
    public MessageRecoverer messageRecoverer(RabbitTemplate rabbitTemplate){
    return new RepublishMessageRecoverer(rabbitTemplate, "exchangemsxferror", "routingkeymsxferror");
    }
    • RejectAndDontRequeueRecoverer:如果不手动配置MessageRecoverer,会默认使用这个,实现仅仅是将异常打印抛出,源码如下:
public class RejectAndDontRequeueRecoverer implements MessageRecoverer {

    protected Log logger = LogFactory.getLog(RejectAndDontRequeueRecoverer.class);

    @Override
public void recover(Message message, Throwable cause) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Retries exhausted for message " + message, cause);
}
throw new ListenerExecutionFailedException("Retry Policy Exhausted", new AmqpRejectAndDontRequeueException(cause), message);
}
}
  • 可以通过给队列(Queue)绑定死信队列,使用nack反馈给mq,会将消息转发到死信队列里面,此种方式需要自己在消费消息的方法内部将异常处理掉
//声明队列,并给队列增加x-dead-letter-exchange和x-dead-letter-routing-key参数,用于指定死信队列的路由和routingKey
@Bean
public Queue queue(){
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange",IntegralConstant.DEAD_EXCHANGE_NAME);
args.put("x-dead-letter-routing-key",IntegralConstant.DEAD_ROUTING_KEY);
return new Queue(IntegralConstant.QUEUE_NAME, true, false, false, args); } //消息确认时使用nack,并且requeue参数传false
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
  • 消息变成死信有以下几种情况:

    • 消息被拒绝(basic.reject/ basic.nack)并且requeue=false
    • 消息TTL过期(参考:RabbitMQ之TTL(Time-To-Live 过期时间))
    • 队列达到最大长度

总结

  • 消息监听内必须使用channel对消息进行确认,不管是确认消费成功还是确认消费失败
  • 消息监听内的异常处理有两种方式:
    • 内部catch后直接处理,然后使用channel对消息进行确认
    • 配置RepublishMessageRecoverer将处理异常的消息发送到指定队列专门处理或记录
  • 监听的方法内抛出异常貌似没有太大用处。因为抛出异常就算是重试也非常有可能会继续出现异常,当重试次数完了之后消息就只有重启应用才能接收到了,很有可能导致消息消费不及时。当然可以配置RepublishMessageRecoverer来解决,但是万一RepublishMessageRecoverer发送失败了呢。。那就可能造成消息消费不及时了。所以即使需要将处理出现异常的消息统一放到另外队列去处理,个人建议两种方式:
    • catch异常后,手动发送到指定队列,然后使用channel给rabbitmq确认消息已消费
    • 给Queue绑定死信队列,使用nack(requque为false)确认消息消费失败

最新文章

  1. 探索ASP.NET MVC5系列之~~~1.基础篇---必须知道的小技能
  2. vs插件ZunKoIDE
  3. eclipse绘制activiti无法生成图形
  4. JVM内存模型和性能优化 转
  5. .NET:Entity Framework 笔记
  6. ES5 bind方法
  7. Android studio 快捷添加构造方法以及set与get
  8. 【php学习】时间函数
  9. Python升级Yum不能使用解决
  10. 2016年2月18日 JAVA基础
  11. sqlserver定时备份
  12. 操作笔记:tomcat在正式环境
  13. qt中如何启动其他应用程序(如果不成功,还有许多原因即QProcess::ProcessError可供分析)
  14. Codeforces Round #180 (Div. 2) B. Sail 贪心
  15. 日志式文件系统:SGI的xfs, Reiserfs, IBM的jfs, ext3fs
  16. linux-2.6.33移植到FL2440
  17. 使用sqlite保存数据返回主键
  18. 1.Node.js 接入微信公众平台开发
  19. 【翻译】Ext JS 6 Beta发布
  20. SSRS报表服务随笔(rdl报表服务)-报表结构与样式

热门文章

  1. Linux 输入输出重定向, &>file, 2>&1, 1>&2
  2. python模块----optparse模块、argparse模块 (命令行解析模块)
  3. 【BFS】hdu 1973 Prime Path
  4. 小希的迷宫B - B
  5. Codeforces Round #626 (Div. 2) E. Instant Noodles(二分图,最大公因数)
  6. AtCoder Beginner Contest 163
  7. hdu4352 XHXJ's LIS (数位dp)
  8. Codeforces Round #481 (Div. 3) D. Almost Arithmetic Progression (暴力)
  9. .net core 更换yum源 / “No package libgdiplus-devel available.” 错误解决方法
  10. C# 网络加密与解密