目录
1、摘要概览
第一篇文章RabbitMQ(一) – 初识RabbitMQ中基于AMQP协议对RabbitMQ整体进行了简介,旨在帮助阅读本系列文章的朋友建立初步的概念。文中最后部分使用到的客户端操作API并未深入的进行学习理解,本文将从RabbbitMQ应用服务部分即Broker所包含的交换器与队列两方面深入学习。
2、Exchange
为什么要在生产者与队列之间提出交换器概念?试想如果生产者与队列直接耦合,当生产者客户端需要将一条消息发送至多个队列,那就需要多次操作。多次操作不仅仅意味着连接性能损耗,虽然Channel是轻量级,并且意味着客户端复杂度上升。所以,提出交换器概念,生产者只需要提前绑定设置好其与队列关系,发送消息时携带路由转发规则,后续逻辑交给服务端处理即可。
2.1 默认交换器
RabbitMQ官网第一个使用示例Hello World中编写类似如下示例代码。当时就在好奇这不就生产者与队列直接交互耦合了么?其实不然,查看basicPublish()源码实现,你会发现参数含义依次为交换器、routingKey、BasicProperties、messageBytes。当第一个参数为空时消息将发送到默认交换器,即RabbitMQ服务端提前创建好的交换器,该交换器类型为direct。
@SneakyThrows
public static void main (String[] args) {
Channel channel = createChannel();
String queueName = "queueName" , message = "测试消息";
channel.basicPublish("",queueName,null,message.getBytes());
}
打开RabbitMQ管理界面,查看AMQP Default Exchange的Bindings描述有如下一句话。所以这就可以解决很多初学者的疑惑,为什么官网给的第一个例子会出现将生产者与队列直接耦合的假象。
// 默认交换器隐式地绑定到每个队列,其路由键等于队列名称
// 无法显式绑定到默认交换器或从默认交换器解绑定。它也不能被删除。
The default exchange is implicitly bound to every queue,
with a routing key equal to the queue name.
It is not possible to explicitly bind to,
or unbind from the default exchange.
It also cannot be deleted.
2.2 交换器类型
不同类型的交换器针对不同场景应用而设计,如将消息路由至全部绑定队列选用fanout、将消息路由至指定routingKey队列选用direct、将消息路由至模糊匹配routingKey则选用topic。当然还有一种header类型交换器,因为其性能与依赖消息头header进行规则匹配的原因,所以基本不会使用。
2.3 交换器创建
如下Demo示例创建持久化、自动删除的交换器,打开控制台可以看到D、AD标识则标识这两个特性,如下图所示:
String exchangeName = "fanoutExchange";
// 持久化、自动删除
boolean durable = true , autoDelete = false;
channel.exchangeDeclare(
exchangeName, BuiltinExchangeType.FANOUT,durable,autoDelete,null
);
2.4 删除交换器
// 是否校验交换器正在使用
boolean ifUnused = true;
channel.exchangeDelete(exchangeName,true);
2.5 补充API
3、Queue
3.1 队列创建
如下Demo创建一个独占、自动删除的队列。看到效果图可能会奇怪为什么设置了持久化但是没有D持久化标志,只有代表自动删除的AD、独占标识Excl。自动删除的队列还要持久化干嘛?
// 持久化、独占、自动删除
boolean durable = true, exclusive = true, autoDelete = true;
channel.queueDeclare(queueName,durable,exclusive,autoDelete,null);
3.2 队列删除
// 校验队列是否正在使用
boolean ifUnused = true;
// 校验队列是否为空
boolean ifEmpty = true;
channel.queueDelete(queueName,ifUnused,ifEmpty);
3.3 补充API
4、Binding
RabbitMQ服务应用通过前面两大节基本就介绍完了,两个重要组成就是交换器和队列。两者之间的绑定是通过Binding实现,同时需要注意的一点就是交换器与交换器之间也可以进行绑定。内置的交换器客户端不能直接发送消息,就需要通过绑定交换器的方式使用。
4.1 交换器绑定
需要明确的一点就是当创建两个交换器绑定关系之后,当第二个参数的交换器接收消息之后会将消息发送给第一个参数的交换器。
// 创建交换器
String directExchangeA = "directExchangeA",directExchangeB= "directExchangeB";
boolean durable = true,autoDelete = false;
channel.exchangeDeclare(
directExchangeA,BuiltinExchangeType.TOPIC,durable,autoDelete,null
);
channel.exchangeDeclare(
directExchangeB,BuiltinExchangeType.DIRECT,durable,autoDelete,null
);
// 交换器绑定
String exchangeBinding = "com.message.info";
channel.exchangeBind(directExchangeB,directExchangeA,exchangeBinding);
// 队列声明
String queueName1= "queue1" , queueName2 = "queue2";
boolean exclusive = false;
channel.queueDeclare(queueName1,durable,exclusive,autoDelete,null);
channel.queueDeclare(queueName2,durable,exclusive,autoDelete,null);
// B交换器绑定队列
String queueBinding1 = "com.message.info" ,
queueBinding2 = "com.message.error";
channel.queueBind(queueName1,directExchangeB,queueBinding1);
channel.queueBind(queueName2,directExchangeB,queueBinding2);
// 发送消息
String messageBytes = "测试消息", routingKey = "com.message.info";
channel.basicPublish(directExchangeA,routingKey,null,messageBytes.getBytes());
通过控制台查看最后结果,发送到交换器A的消息路由到了交换器B,并最终路由到队列1。基本可以断定其中交换器A – B,交换器B – 队列1使用的routingKey就是最初消息发送的routingKey。可以修改相关binding验证,具体验证过程这里不再给出 。
4.2 队列绑定
队列绑定在上面例子中其实已经有了对应的API操作,就是将队列与交换器进行绑定。参数中需要定义绑定关系的Binding,当然fanout交换器绑定的时候不需要用到这个关系。借用上面例子截图队列绑定关系如下: