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源码分析 这里只分析 ...
随机推荐
- 优化改良版:数组,List,等集合需要加逗号或其它符合转成字符串
大家经常需要数组加逗号拼接成字符串的情况传统作法就是写for,foreach拼接, 现给出优化改良版数组,List,等集合需要加逗号或其它符合转成字符串方法: List<string> l ...
- Java Nested Classes(内部类~第一篇英文技术文档翻译)
鄙人最近尝试着翻译了自己的第一篇英文技术文档.Java Nested Classes Reference From Oracle Documentation 目录 嵌套类-Nested Classes ...
- 76. Minimum Window Substring(hard 双指针)
Given a string S and a string T, find the minimum window in S which will contain all the characters ...
- uva1201 DAG 最小路径覆盖,转化为 二分图
大白例题P356 你在一座城市里负责一个大型活动的接待工作.你需要去送m个人从出发地到目的地,已知每个人的出发时间出发地点,和目的地点,你的任务是用尽量少的出租车送他们,使得每次出租车接客人,至少能提 ...
- 【转】eclipse反编译插件
原文地址:http://bbs.csdn.net/topics/390263414 离线安装包下载地址一:http://feeling.sourceforge.net/downloads/org.sf ...
- keepalived+lvs高可用集群
LVS+Keepalived 介绍 LVS LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统.本项目在1998年5月由章文嵩博士成立,是中国 ...
- 使用token实现在有效期内APP自动登录功能
实现此功能的场景是在当下用户对手机APP体验要求高,并且相对安全前提的推动下诞生:当你下载了一个QQ,微信第一次进行了账号和密码的登录,你从此以后打开应用免去了你每日打开应用都要输入账号跟密码的痛苦过 ...
- 微服务:Java EE的拯救者还是掘墓人?
有人认为,微服务的大行其道是在给Java EE下达死刑判决书.也有人认为,Java EE已死的论调可笑至极.读者朋友,你们怎么看? 引言 有人说,Java确实过于臃肿,经常“小题大做”.但PHP.No ...
- 10:Python2与Python3比较
1.print 函数 1. print语句没有了,取而代之的是print()函数. Python 2.6与Python 2.7部分地支持这种形式的print语法. 2.Unicode 1. 在pyt ...
- @Transactional引起的NullPointerException
https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-Transactional-NullPointerException ...