RabbitMQ消息队列实战(1)—— RabbitMQ的体系

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

RabbitMQ是一个开源的消息代理和队列服务器用来在不同的应用之间共享数据。1983年被认为是RabbitMQ的雏形的Teknekron创建首次提出了消息总线的概念。中间经历过数个阶段的发展一直到2004年AMQPAdvanced Message Queuing Protcol协议开发完成这是一款具有开放标准的通信协议它允许任何人都可以执行这一标准编码标准的任何人都可以和任何AMQP供应商提供的MQ服务器进行交互。直到2007年RabbitMQ 1.0正式诞生RabbitMQ开始在金融、电信领域大面积的使用。

RabbitMQ使得生产者和消费者真正做到了解耦。引入了代理服务器可以实现高可用和弹性的横向拓展。通过增加服务器实例的方式RabbitMQ可以做到每秒处理百万级别的消息。本文主要介绍RabbitMQ的逻辑体系结构先是简单介绍其整个体系架构以及相关名词的介绍然后介绍RabbitMQ三种消息路由的方式因第四种Header模式基本不会使用这里不包括在内最后介绍其典型的应用场景。

一、RabbitMQ的体系架构

我们知道RabbitMQ最初被用来主要是做应用之间的解耦所以它的整体的架构大概分为三个部分生产者Publisher消费者Consumer和消息代理Broker。其中最核心的是消息代理其内部结构稍显复杂主要针对不同的应用场景实现不同的消息处理逻辑。我们先来看下RabbitMQ的逻辑架构图

下面分别对这些名词进行解释

Publisher消息的生产者

Broker消息代理通常是一个服务实体。

VHostVirtual Host主要用来隔离一批交换机、队列在每个虚拟主机中都可以创建自己的交换机、队列和绑定虚拟主机之间互相不可见。

Connection一个网络连接

Channel信道AMQP中引入了信道进行消息的传递。主要是因为操作系统中创建和销毁一个TCP连接代价非常昂贵信道的引入就是为了复用一个TCP连接通过多线程的方式来传递消息。通常情况下一个信道对应一个线程。

Exchange用来接受消息并负责把消息传递给队列。

Queue用来存放消息的容器。

Binding用来关联交换机和队列的绑定只有绑定了之后交换机才知道向哪个队列发送消息。

二、RabbitMQ的路由策略

根据交换机的类型RabbitMQ的的路由策略可以分为三种直连默认Direct、主题模式topic、广播模式fanout。接下来我们分别进行介绍。不过在此之前我们需要先了解下RabbitMQ的消息发布的命令因为结合这个命令我们才能购真正理解RabbitMQ这几种路由策略的原理笔者以Java代码的API来解释这个命令

channel.basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body);

exchange交换机的名称在直连模式下为空。

routingKey路由键直连模式下是队列的名称主题模式下是主题的名称广播模式下为空。

props发送消息时附带的一些附加参数

body发送的消息转换成的二进制数组

2.1 直连模式

所谓的直连模式就是直连交换机直接将消息路由到对应的队列中。在直连模式下使用basicPublish在publish消息时routingKey必须设置成目标队列的名称。而exchange可以设置成我们创建的直连交换机的名称如果设置则使用RabbitMQ安装时默认的直连交换机。

要想使用直连模式路由消息必须先创建直连交换机。当安装完成RabbitMQ后会默认创建1个直连交换机下图中圈出部分如果我们在basicPublish时不指定交换机名称会使用这个默认交换机直连模式下

除此之外我们也可以创建支持直连模式的交换机如下

channel.exchangeDeclare("my-direct-exchange", "direct", true, false, null);

如上我们创建了一个名称为my-direct-exchange的交换机第二个参数指定了交换机的类型为direct类型。完成了交换机的创建之后接下来创建一个队列my-queue用来接受交换机的消息

channel.queueDeclare("my-queue", true, false, false, null);

最后创建一个binding将交换机和队列关联起来

channel.queueBind("my-queue", "my-direct-exchange", "my-queue");

queueBind的第1个参数是要关联的队列名称第2个参数是要关联的交换机名称第三个参数是路由键在直连模式下要填写队列名称。

完成了上述的过程之后我们就创建了一套完整的直连交换机、队列和绑定实现了以direct的方式路由消息。发布消息时路由键填写队列名称

channel.basicPublish("direct-exchange", "my-queue",...);

2.2 主题模式

RabbitMQ的第二种路由模式是topic模式topic类型的交换机会根据发送消息的topic寻找使用binding绑定了该topic的的队列然后将消息路由到该队列上。

需要注意的是这种交换机和队列的关系是一对多的也就是是说多个队列可以通过同一个主题和同一个交换机绑定

使用主题模式的路由策略同样先创建topic类型的交换机

channel.exchangeDeclare("topic-exchange", "topic", true, false, null);

topic-exchange交换机名称

topic交换机类型固定是topic

队列的创建这里不再演示因为队列没有direct、topic和fanout类型之分我们还是沿用上面创建的队列my-queue来进行接下来的说明。与direct路由策略的bind不同在为topic交换机和队列创建bind时要明确得指出主题

channel.queueBind("my-queue", "topic-exchange", "fruit");

可以看到在第3个参数路由键中我们明确得指出了topic为fruit。

然后在发布消息时也要明确得指出消息的topic

channel.basicPublish("topic-exchange", "fruit",...);

2.3 广播模式

RabbitMQ最后一种路由策略是fanout模式该模式的路由原理如下图

在该模式下只要将队列和fanout交换机进行了绑定那么向fanout交换机发布的所有的消息都会路由到所有绑定的队列中。与之前创建两种类型的交换机类似创建fanout交换机时需要指定交换机的类型为fanout

channel.exchangeDeclare("fanout-exchange", "fanout", true, false, null);

在创建bind时不需要指定路由键

channel.queueBind("my-queue", "fanout-exchange", "");

在发布消息时同样不需要指定路由键fanout 交换机会把消息发送到所有绑定它上面的队列中

channel.basicPublish("fanout-exchange", "", ...);

2.4 关于Exchange、Queue和Bind之间的关系

可能刚刚接触RabbitMQ的童鞋会被这三者的关系绕晕。我最开始学习RabbitMQ时也是花了很长时间才把这三者之间的关系梳理清楚。笔者这里所说的关系是指1个Exchange对应1个Queue或者1个Exchange对应多个Queue等这种数量上的对应关系。针对这种关系最终我发现了一个事实总结如下

1三者之间这种数量上的对应关系和RabbitMQ的路由策略以及Exchange的类型无关。只要保证一个bind对应在创建时绑定了一个exchange和一个queue即可。

2一个exchange可以和多个queue通过多个bind对象绑定不管exchange是什么类型。

3一个queue可以和多个exchange通过多个bind对象绑定不管exchange是什么类型。

三、RabbitMQ的应用场景

笔者根据RabbitMQ的特性和使用目的将RabbitMQ的应用场景归为了三类

  • 将消费者和生产者解耦。

这是RabbitMQ被提出来最基本也是最初的目的。通过这种解耦至少可以实现两种效果

1原本需要轮询获取生产者消息的消费者代码可以通过监听RabbitMQ队列实现有消息才处理从而省去了轮询的过程。

2通过RabbitMQ主题交互机或者广播交换机可以轻松实现当有新的消费者加入时而不需要修改生产者代码。比如在充值时我们可以发送一个充值事件日志服务捕获充值事件后写入日志。如果后面有新的需求比如积分服务只需要新增积分服务即可无需修改生产者代码。

  • 实现分布式事务

实现分布式事务。利用RabbitMQ代理可持久化可做高可用以及消息的ack机制可以实现分布式事务比如saga模式ebay的本地事务表。

  • 实现异步调用提高性能。

在微服务架构的系统种要实现某种业务往往需要调用多个其它服务的接口如果采用同步调用的方式需要在上一个接口调用完成之后再去调用下一个接口性能势必不高。如果使用RabbitMQ作为代理进行异步调用几乎可以实现和只调用一个接口同样的耗时。而且采用这种方式还有一个好处就是可以有选择地忽略完成该业务非必要地接口调用。

四、总结

本文主要介绍了RabbitMQ的体系结构、三种路由模式和应用场景结合上文现总结如下

1RabbitMQ中通过vhost实现了数据的隔离每个vhost下都有自己的exchange和queue可以以vhost为单位对用户进行授权。

2RabbitMQ中有三种路由策略

  • direct策略 —— 需要创建direct exchange和queue绑定时指定路由键为队列名称在发布消息时需要以目标队列的名称作为路由键。

  • topic策略 —— 需要创建topic exchange和queue绑定时需要将明确的topic作为路由键发布消息时需要将topic作为路由键。

  • fanout策略 —— 需要创建fanout exchange和queue绑定时无需指定路由键发布消息时无需指定路由键。

3一个exchange可以和多个queue通过多个bind对象绑定不管exchange是什么类型。

4一个queue可以和多个exchange通过多个bind对象绑定不管exchange是什么类型。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: RabbitMQ