Spring AMQP 源码分析 04 - MessageListener
### 准备
## 目标
## 前置知识
## 相关资源
## 测试代码

### 分析
## MessageListener

## MessageListenerContainer

## 内部实现思路
- MessageListenerContainer 的实现类,即 org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer。它作为整个异步消息投递的核心类存在。
- 因为 MessageListenerContainer 实际上管理了一个消费者线程组,因此需要相关线程类与线程调度类。Spring AMQP 中该线程类为 org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer,调度类当然就是 SimpleMessageListenerContainer,其 start 方法会启动线程
- 消息队列推送过来的消息需要一个本地队列缓存。
- 需要实现 amqp client 的 Consumer 接口。在该接口实现类中,我们简单的把消息放到本地队列中。org.springframework.amqp.rabbit.listener.BlockingQueueConsumer$InternalConsumer 负责这件事
- 根据单一职责原则,线程类只负责异步消费者的创建与(无限循环)消息消费;InternalConsumer 只负责实现 amqp client 的 Consumer 接口,与 amqp client 原生的异步消息投递实现对接,将消息放入本地队列。那么,我们还需要一个真正的异步消费者模型,用来管理消费行为与状态。org.springframework.amqp.rabbit.listener.BlockingQueueConsumer 承担这部分责任。从名字可以看出,BlockingQueueConsumer 采用 BlockingQueue 作为本地队列缓存消息。
- 用户的业务逻辑是在 MessageListener 接口中实现的,框架的主要处理过程为:创建合适的连接与信道,从 amqp client 中获取消息暂存到本地缓存,从本地缓存读取消息并调用 MessageListener 接口的 onMessage 方法消费消息。
## 内部流程分析
- NONE:不确认,相当于 amqp client 中 Channel.basicConsume 方法中 autoAck 参数值设为 true
- MANUAL:用户通过 channel aware listener 手动控制消息确认
- AUTO:Spring AMQP 框架根据 message listener 的 onMessage 执行过程中是否抛出异常来决定是否确认消息消费
- 调用 BlockingQueueConsumer 的 start 方法(不是 Runnable 接口)。
- start 方法先通过 ConnectionFactoryUtils.getTransactionalResourceHolder 静态方法创建出供该线程使用的 channel,该方法返回类型是 RabbitResourceHolder。这部分代码涉及到事务,很复杂,但是本文的测试代码不涉及事务,目前只要了解多个 AsyncMessageProcessingConsumer 会生成多个 RabbitResourceHolder 实例,但是由于使用了 CachingConnectionFactory 的默认缓存模式,所以这些 RabbitResourceHolder 实例共用同一个(AMQP)连接,每个 AsyncMessageProcessingConsumer 独享该连接创建的一个(AMQP)信道即可
- start 方法接着创建 InternalConsumer 实例,并调用刚创建的 AMQP 信道的 basicQos 和 basicConsume 方法开始接受消息。这样,当队列接受到消息时,amqp client 会主动调用 InternalConsumer 的 handleDelivery 方法。该方法调用 BlockingQueueConsumer.this.queue.put(new Delivery(consumerTag, envelope, properties, body)); 将消息放到 BlockingQueueConsumer 的 BlockingQueue<Delivery> queue 中。org.springframework.amqp.rabbit.support.Delivery 类封装了 amqp client 通过 handleDelivery 方法回送过来的所有参数。
有两个细节值得说一下:第一,BlockingQueueConsumer 可以同时消费多个队列,对每个队列,都会调用 basicConsume 方法让 InternalConsumer 监听当前队列(即同一个信道,同一个 Consumer ,不同的队列);其二,可以通过 ConsumerTagStrategy tagStrategy 设定 Tag 命名规则。 - 接着,在 while 循环中调用 SimpleMessageListenerContainer 的 receiveAndExecute 方法,不停的尝试从 queue 中获取 Delivery 实例,将之转化为 Message,然后执行 MessageListener 的 onMessage 回调方法。
- 如果执行成功,则调用 AMQP 信道的 basicAck 方法确认消息消费成功。
- 如果执行过程中发生异常,则将异常转化为 ListenerExecutionFailedException 抛出。默认情况下,Spring AMQP 处理用户自定义异常的逻辑非常简单:调用 AMQP 信道的 basicReject 方法将消息退回队列,打印 warning 级别的日志,但不会打破 AsyncMessageProcessingConsumer 线程的 while 循环,消息消费继续进行。这部分内容下篇文章分析。
Spring AMQP 源码分析 04 - MessageListener的更多相关文章
- Spring AMQP 源码分析 07 - MessageListenerAdapter
### 准备 ## 目标 了解 Spring AMQP 如何用 POJO 处理消息 ## 前置知识 <Spring AMQP 源码分析 04 - MessageListener> ## 相 ...
- Spring AMQP 源码分析 06 - 手动消息确认
### 准备 ## 目标 了解 Spring AMQP 如何手动确认消息已成功消费 ## 前置知识 <Spring AMQP 源码分析 04 - MessageListener> ## 相 ...
- Spring AMQP 源码分析 05 - 异常处理
### 准备 ## 目标 了解 Spring AMQP Message Listener 如何处理异常 ## 前置知识 <Spring AMQP 源码分析 04 - MessageListene ...
- Spring AMQP 源码分析 08 - XML 配置
### 准备 ## 目标 通过 XML 配置文件使用 Spring AMQP ## 前置知识 <Spring AMQP 源码分析 07 - MessageListenerAdapter> ...
- Spring AMQP 源码分析 02 - CachingConnectionFactory
### 准备 ## 目标 了解 CachingConnectionFactory 在默认缓存模式下的工作原理 ## 前置知识 <Spring AMQP 源码分析 01 - Impatie ...
- Spring AMQP 源码分析 03 - MessageConverter
### 准备 ## 目标 了解 Spring AMQP 消息转化实现 ## 相关资源 Quick Tour for the impatient:<http://docs.spring.io/ ...
- Spring AMQP 源码分析 01 - Impatient
### 准备 ## 目标 了解 Spring AMQP 核心代码 ## 前置知识 RabbitMQ 入门 ## 相关资源 Quick Tour for the impatient:&l ...
- Spring Security 源码分析(四):Spring Social实现微信社交登录
社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...
- spring事务源码分析结合mybatis源码(一)
最近想提升,苦逼程序猿,想了想还是拿最熟悉,之前也一直想看但没看的spring源码来看吧,正好最近在弄事务这部分的东西,就看了下,同时写下随笔记录下,以备后查. spring tx源码分析 这里只分析 ...
随机推荐
- qt用mingw编译时报错 multiple definition of
网上相关回答不少,但过于简单,这里做一下记录. qt用mingw编译程序时报“multiple definition of …”这个错误,错误信息大概是如下图所示: 1 2 3 首先,检查自己的程序是 ...
- mysql5.7服务器The innodb_system data file 'ibdata1' must be writable导致无法启动服务器
现象如下:The innodb_system data file 'ibdata1' must be writable. 解决方案如下: 1.关闭mysqld进程: 2.删除配置文件中datadir所 ...
- mongodbtemplate配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- zw版【转发·台湾nvp系列Delphi例程】HALCON HomMat2dRotate1
zw版[转发·台湾nvp系列Delphi例程]HALCON HomMat2dRotate1 procedure TForm1.Button1Click(Sender: TObject);var img ...
- AdaBoost学习笔记
学习了李航<统计学习方法>第八章的提升方法,现在对常用的一种提升方法AdaBoost作一个小小的笔记,并用python实现书本上的例子,加深印象.提升方法(boosting)是一种常用的统 ...
- python chunk模块
chunk模块用于读取TIFF格式的文件,打开应该使用二进制模式 TIFF 标签图像文件格式 import chunk import chunk f=open('E:\\test.tiff','rb' ...
- 让nodepad++编辑时链接能双击打开
让nodepad++编辑时链接能双击打开,Notepad++自动把代码或编辑状态里的链接或URL地址转成可点击的链接,当你双击该URL会打开相应的网页地址,不用复制地址到浏览器打开了,非常方便好用. ...
- Python入门之获取当前所在目录的方法详解
#本文给大家讲解的是使用python获取当前所在目录的方法以及相关示例,非常的清晰简单,有需要的小伙伴可以参考下 sys.path 模块搜索路径的字符串列表.由环境变量PYTHONPATH初始化得到. ...
- WebService(JAX-WS、XFire、Axis三种)获取客户端ip
WebService(JAX-WS.XFire.Axis三种)获取客户端ip JAX-WS.XFire.Axis三种webservice的获取客户端IP的简单实现过程: 1,基于JDK6 jax-ws ...
- 依赖注入(DI)在PHP中的实现
什么是依赖注入? IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection,简称DI). 当一个类的实例需要另一 ...