AMQP
AMQP 是 Advanced Message Queuing Protocol(高级消息队列协议) 的缩写,是一套跨语言、跨平台的消息传递开放标准。
核心特点是与语言 / 平台无关: 它解决了不同技术栈(比如 Java、Python、Go)、不同平台(Windows、Linux)的应用之间消息通信的兼容性问题------ 只要遵循 AMQP 协议,无论应用用什么语言开发,都能通过支持 AMQP 的消息中间件(比如 RabbitMQ)实现可靠的消息传递。
SpringAMQP介绍
Spring AMQP 是 基于 AMQP 协议的一套 Java API 规范 ,它把 AMQP 协议的能力封装成了更易用的 Spring 风格 API(比如RabbitTemplate、@RabbitListener),让 Java 开发者可以用更简洁的方式使用 AMQP 协议。
它包含两部分:
spring-amqp:是 AMQP 协议的基础抽象层,定义了核心接口和模型;
spring-rabbit:是底层实现(默认基于 RabbitMQ),负责与 RabbitMQ 服务器通信。
RabbitMQ 的 4 大核心模型
SpringAMQP 完美实现了 RabbitMQ 的 4 大核心模型。
RabbitMQ 的核心是交换机(Exchange) + 队列(Queue) + 绑定(Binding),4 大核心模型本质是交换机的四种类型,决定了消息如何从生产者路由到消费者。 他们分别是:
-
工作队列模式(Work Queue / 任务队列)
-
发布订阅模式(Fanout / 广播模式)
-
路由模式(Direct / 路由模式)
-
主题模式 (topic)
核心差异在于交换机的类型和路由规则。
WorkQueues
1、引入:当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。 此时就可以使用work 模型,多个消费者共同处理消息处理,消息处理的速度就能大大提高了。
2、workQueues的问题:默认情况下,RabbitMQ 会将消息一次轮询投递给绑定在队列上的每一个消费者,但并没有考虑消费者是否已经处理完消息,导致 "能者闲、弱者忙"。
3、解决方案:消费者端配置prefetch=1,表示消费者每次仅预取 1 条消息,处理完成后再获取下一条,实现 "能者多劳":
bash
spring:
rabbitmq:
listener:
simple:
prefetch: 1 # 关键配置,解决平均分配问题
总结:Work模型的使用
-
多个消费者绑定到一个队列,同一条消息只会被一个消费者处理
-
通过设置prefetch来控制消费者预取的消息数量
Fanout
1、Fannout exchange会将收到的消息广播到每一个跟其绑定的queue,所有绑定队列的消费者都会收到消息;所以也叫广播模式;
无路由规则,是最简单的交换机;
适合 "一对多" 的消息通知场景(如公告、通知)。
2、使用要点:
1)需先创建 Fanout 交换机和多个队列,并将队列绑定到交换机(无 bindingKey);
2)生产者:RabbitTemplate.convertAndSend(交换机名, "", 消息),第二个参数为 routingKey,Fanout 模式下传空即可
3)消费者:分别监听绑定到该交换机的不同队列,均可收到相同消息。
java
//生产者
@Test
public void testFanoutExchange() {
// 交换机名称
String exchangeName = "hmall.fanout";
// 消息
String message = "hello, everyone!";
rabbitTemplate.convertAndSend(exchangeName, "", message);
}
//消费者
@RabbitListener(queues = "fanout.queue1")
public void listenFanoutQueue1(String msg) {
System.out.println("消费者1接收到Fanout消息:【" + msg + "】");
}
@RabbitListener(queues = "fanout.queue2")
public void listenFanoutQueue2(String msg) {
System.out.println("消费者2接收到Fanout消息:【" + msg + "】");
}
Direct(直连)
一、特点:生产者发送消息时指定RoutingKey(路由键),交换机会将消息路由到绑定键(BindingKey)与路由键完全匹配的队列。
具体讲,在Direct模型下:
队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey路由key) 消息的发送方在 向 Exchange发送消息时,也必须指定消息的 RoutingKey。 Exchange不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routingkey与消息的 Routing key完全一致,才会接收到消息。
二、实践
1、控制台操作
首先在控制台声明两个队列direct.queue1和direct.queue2, 然后声明一个direct类型的交换机,命名为hmall.direct: 然后使用red和blue作为key,绑定direct.queue1到hmall.direct:
同理,使用red和yellow作为key,绑定direct.queue2到hmall.direct。


代码:
消费者:在consumer服务中添加下述方法:
java
@RabbitListener(queues = "direct.queue1")
public void listenDirectQueue1(String msg) {
System.out.println("消费者1接收到direct.queue1的消息:【" + msg + "】");
}
@RabbitListener(queues = "direct.queue2")
public void listenDirectQueue2(String msg) {
System.out.println("消费者2接收到direct.queue2的消息:【" + msg + "】");
}
publisher:在publisher服务的SpringAmqpTest类中添加测试方法:
java
@Test
public void testSendDirectExchange() {
// 交换机名称
String exchangeName = "hmall.direct";
// 消息
String message = "红色警报!日本乱排核废水,导致海洋生物变异,惊现哥斯拉!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName, "red", message);
}
@Test
public void testSendDirectExchange() {
// 交换机名称
String exchangeName = "hmall.direct";
// 消息
String message = "最新报道,哥斯拉是居民自治巨型气球,虚惊一场!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName, "blue", message);
}
结果:测试第一个方法是由于使用的red这个key,所以两个消费者都收到了消息

我们再切换为blue这个key(即测试第二个方法)你会发现,只有消费者1收到了消息。

Topic
Topic类型的Exchange与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。 只不过Topic类型Exchange可以让队列在绑定BindingKey 的时候使用通配符! BindingKey 一般都是有一个或多个单词组成,多个单词之间以.分割,例如: item.insert通配符规则;
#:匹配一个或多个词 *:匹配不多不少恰好1个词

如图所示,假如此时publisher发送的消息使用的RoutingKey共有四种:
-
china.news代表有中国的新闻消息; -
china.weather代表中国的天气消息; -
japan.news则代表日本新闻 -
japan.weather代表日本的天气消息;
解释:
-
topic.queue1:绑定的是china.#,凡是以china.开头的routing key都会被匹配到,包括:china.news ;china.weather -
topic.queue2:绑定的是#.news,凡是以.news结尾的routing key都会被匹配。包括:china.news;japan.news;
二、控制台实操
在控制台按照图示例子创建队列、交换机,并利用通配符绑定队列和交换机

讨论
1、RabbitMq和AMQP 协议的关系
rabbitmq是一个中间件,而 AMQP 协议是一个协议,也就是说使用rabbitmq作为message queue的发送者和消费者只要遵循 AMQP 协议就能够跨平台,跨语言的进行交流;
2、本文介绍的4中模型中,
workqueues模型没有交换机的参与。(可通过prefetch实现能者多劳的效果)
而真正生产环境都会经过exchange来发送消息,而不是直接发送到队列,交换机的类型有以下三种:fanout:广播;direct:定向;topic:话题。它们核心差异体现在交换机类型和路由规则的不同。
3、 SpringAMQP 则为这些模型提供了简洁易用的 Java 实现方式,通过对应的 API 和注解,能让开发者高效实现不同场景下的分布式消息传递 。
总结:
本文介绍了 AMQP 跨语言跨平台的核心特性,以及 SpringAMQP 对 AMQP 协议的 Spring 风格封装和其两大组成部分;并介绍了RabbitMQ 以交换机、队列、绑定为核心的四大模型(WorkQueues,Fanout,Direct,Topic);介绍他们的特点,解决的问题,相应的控制台实现,并举了一些代码实例来辅助说明他们。