### 准备

## 目标

了解 Spring AMQP Message Listener 如何处理异常

## 前置知识

《Spring AMQP 源码分析 04 - MessageListener》

## 相关资源

Sample code:<https://github.com/gordonklg/study>,rabbitmq module
源码版本:Spring AMQP 1.7.3.RELEASE

## 测试代码

gordon.study.rabbitmq.springamqp.AsyncConsumerWithErrorHandler.java

 

### 分析

## 消息消费异常处理流程

根据上一遍文章的分析,示例代码 AsyncConsumerWithErrorHandler 第26行开始消费消息,方法运行在 AsyncMessageProcessingConsumer 线程实例中,调用栈如下:

由 AbstractMessageListenerContainer 的 doInvokeListener 方法直接发起对 onMessage 方法的调用,代码如下:
对于 onMessage 消息处理过程中抛出的异常, wrapToListenerExecutionFailedExceptionIfNeeded 方法将所有非 ListenerExecutionFailedException 异常包装为 ListenerExecutionFailedException 异常(通过 instanceof 判断,因此 ListenerExecutionFailedException 子类不会被包装),并重新抛出。
 
调用栈一直退出,直到 SimpleMessageListenerContainer(AbstractMessageListenerContainer).executeListener(Channel, Message) line: 729   

 
handleListenerException 方法最终调用 invokeErrorHandler 方法,通过属性 ErrorHandler errorHandler 的 handleError 方法正式处理异常。

 

默认的 errorHandle 就是 ConditionalRejectingErrorHandler,其 handleError 逻辑很简单:如果抛出的异常,其原因链中不包含 AmqpRejectAndDontRequeueException,同时 ConditionalRejectingErrorHandler 内部属性 FatalExceptionStrategy exceptionStrategy 的 isFatal 方法返回 true(可以看成是无法恢复的严重异常),则将异常包装为 AmqpRejectAndDontRequeueException 重新抛出。

 
这是判断原因链中是否包含 AmqpRejectAndDontRequeueException 的代码:
 

 
默认情况下, exceptionStrategy 是 DefaultExceptionStrategy 的实例:

只有 ListenerExecutionFailedException 异常及其子类才可能是 fatal,但是对于本次调用栈,wrapToListenerExecutionFailedExceptionIfNeeded 方法保证了抛出的异常是 ListenerExecutionFailedException。
isCauseFatal 方法定义了一些严重异常,很显然,这些异常都是些无论重试多少次都会出错的异常,因此应该被包装为 AmqpRejectAndDontRequeueException  异常。
DefaultExceptionStrategy 预留了 isUserCauseFatal 方法给用户扩展。
 
异常继续往外抛,到 SimpleMessageListenerContainer.doReceiveAndExecute(BlockingQueueConsumer) line: 1260,会调用 BlockingQueueConsumer 的 rollbackOnExceptionIfNecessary 方法。该方法先判断是否要向 RabbitMQ 确认消息,如果需要确认(意味着要调用 channel 的 basicReject 方法),再根据异常的原因链中是否存在 AmqpRejectAndDontRequeueException 异常决定如何设置  basicReject 方法的 requeue 参数。
 

## 示例代码分析

从流程分析中可知,onMessage 方法如果抛出异常,一般情况下会导致消息被 reject,同时重新入队。这个默认设置比较安全。
 
如果我们不想要消息重新入队呢?最简单的方法就是抛出 AmqpRejectAndDontRequeueException,就像示例代码中被注释掉的第33行代码那样。但是这导致业务消费逻辑与框架实现绑定过深,因此,我们采用重载 DefaultExceptionStrategy 的 isUserCauseFatal 方法来决定不同的业务异常要不要让 reject 的消息重新入队,正如示例代码第42行所示。当异常类型是 UserDefineException 时,消息被 reject 同时不会重入队列。
 

Spring AMQP 源码分析 05 - 异常处理的更多相关文章

  1. Spring AMQP 源码分析 08 - XML 配置

    ### 准备 ## 目标 通过 XML 配置文件使用 Spring AMQP ## 前置知识 <Spring AMQP 源码分析 07 - MessageListenerAdapter> ...

  2. Spring AMQP 源码分析 07 - MessageListenerAdapter

    ### 准备 ## 目标 了解 Spring AMQP 如何用 POJO 处理消息 ## 前置知识 <Spring AMQP 源码分析 04 - MessageListener> ## 相 ...

  3. Spring AMQP 源码分析 06 - 手动消息确认

    ### 准备 ## 目标 了解 Spring AMQP 如何手动确认消息已成功消费 ## 前置知识 <Spring AMQP 源码分析 04 - MessageListener> ## 相 ...

  4. Spring AMQP 源码分析 02 - CachingConnectionFactory

    ### 准备 ## 目标 了解 CachingConnectionFactory 在默认缓存模式下的工作原理   ## 前置知识   <Spring AMQP 源码分析 01 - Impatie ...

  5. Spring AMQP 源码分析 04 - MessageListener

    ### 准备 ## 目标 了解 Spring AMQP 如何实现异步消息投递(推模式) ## 前置知识 <RabbitMQ入门_05_多线程消费同一队列> ## 相关资源 Quick To ...

  6. Spring AMQP 源码分析 03 - MessageConverter

    ### 准备 ## 目标 了解 Spring AMQP 消息转化实现   ## 相关资源 Quick Tour for the impatient:<http://docs.spring.io/ ...

  7. Spring AMQP 源码分析 01 - Impatient

    ### 准备   ## 目标 了解 Spring AMQP 核心代码   ## 前置知识 RabbitMQ 入门   ## 相关资源   Quick Tour for the impatient:&l ...

  8. Spring AOP 源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  9. Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析

    Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析 前言 上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器 ...

随机推荐

  1. SpringMVC学习笔记二第一个小的程序

    首先:我们要用springmvc来写一个helloworld的例子: 首先我们需要导入所需要的架包: /demo1/WebRoot/WEB-INF/lib/commons-logging-1.1.1. ...

  2. centos添加epel源

    1. rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm    粗体部分需要根据自己的 ...

  3. sql 中延时操作

    select 1; WAITFOR DELAY '00:00:30'; select 2; --执行完第一个之后会 延时 30秒,才会执行第二个sql

  4. label--input

    .form-group { margin-bottom: 15px; position: relative;}.control-label{ float: left; width: 15%; text ...

  5. 【转】eclipse反编译插件

    原文地址:http://bbs.csdn.net/topics/390263414 离线安装包下载地址一:http://feeling.sourceforge.net/downloads/org.sf ...

  6. 数据在千万级别上进行全文检索有哪些技术?强大的大数据全文索引解决方案-ClouderaSearch

    数据在千万级别上进行全文检索有哪些技术?强大的大数据全文索引解决方案-ClouderaSearch1.lucene (solr, elasticsearch 都是基于它) 2.sphinx3.elas ...

  7. Ubuntu去掉命令行前用户名和主机名方法

    Ubuntu去掉命令行前用户名和主机名方法 $ vi ~/.bashrc 按a或i进入编辑模式 PS1='${debian_chroot:+(debian_chroot)}\w\$ ' 默认为 PS1 ...

  8. 使用 SSH 和 SFTP 协议

    通过 SSH 和 SFTP 协议,我们能够访问其他设备,有效而且安全的传输文件等等. 几年前,我决定配置另外一台电脑,以便我能在工作时访问它来传输我所需要的文件.要做到这一点,最基本的一步是要求你的网 ...

  9. MySQL数据库----数据锁

    数据锁 需求: 有一个账户,两个人在同一时间要对此账户操作,A要对账户充值100块,B要从账户中取出100块.操作前都要先看一下账户的 余额然后再操作. -- 窗口1 用户进行充值 -- 充值前 先查 ...

  10. 基于Spring Cloud的微服务落地

    微服务架构模式的核心在于如何识别服务的边界,设计出合理的微服务.但如果要将微服务架构运用到生产项目上,并且能够发挥该架构模式的重要作用,则需要微服务框架的支持. 在Java生态圈,目前使用较多的微服务 ...