RabbitMQ集群对时间非常敏感,应该在局域网中使用,不应在关于网中使用。而Federation插件可以很好地解决这个问题。这篇文章和大家分享RabbitMQ Federation的使用场景、实现原理和具体用法。
使用场景
Federation插件的目的就是解决RabbitMQ节点之间进行消息传递而不需要建立集群,这个功能在很多场景中都很有用:
- 在不同管理域(不同的用户、不同的vhost、不同的Rabbit版本)的节点、集群或者数据中心之间传递消息。
- 在不同的Broker之间进行通信时,需要解决网络的不稳定因素。
- 需要更高的扩展性。
以上只是列举了三个适用场景,了解它可以做什么,下面介绍它是如何做到的,也就是实现原理。
实现原理
Federation可以让多个交换器或者多个队列进行联邦,一个Federation Exchange或者一个Federation Queue可以接受其它Broker上的消息。Federation Exchange能够将原本发送其它Broker上的消息路由到本地队列中,Federation Queue允许本地Consumer消费其它Broker 队列上的消息。
下面结合一个例子介绍Federation的原理。如下图,假设有两个数据中心DC1和DC2,分别部署了RabbitMQ服务,Broker1和Broker2,DC1中的Clinet1向Broker1发送消息,由于是在一个DC,所以很快,如果Client1想DC2中的Broker2发送消息,就会有一定的网络延时,尤其是在开启了Publisher Confirm或者事务的情况下,甚至会造成一定的阻塞。此时,可以用Federation解决。
先看下面一张图。在Broker2上声明Exchange:federation_exchange,Queue:federation_queue,通过绑定键 federation_routing_key 将Exchange和Queue绑定,然后通过操作(下面演示)创建Federation Link,那么Federation插件会自动在Broker1上创建一个相同名字的Exchange:federation_exchange, 还会创建一个内部Exchange:"federation: federation_exchange -> rabbit02@LM-SHB-40513514 A","rabbit02@LM-SHB-40513514 A" 是Broker2所在集群的名字,通过federation_routing_key 将 federation_exchange 和 "federation: federation_exchange -> rabbit02@LM-SHB-40513514 A" 绑定,同时会自动创建内部Queue:"federation: federation_exchange -> rabbit02@LM-SHB-40513514" ,和内部Exchange绑定。
通过RabbitMQ UI可以观察到创建的federation exchange , federation queue,和它们绑定关系的详细信息,如下图。
当Client1发送消息到Broker1,Consumer2就可以消费到Broker2上federation_queue中的消息,消息的流转过程如下图。
经过federation link转发的消息,header部分会有特殊的属性值,如下图。
Broker1的"federation: federation_exchange -> rabbit02@LM-SHB-40513514" 也是一种普通队列,Consumer1可以直接消它上面的消息,需要注意的是:如果发往Broker1 federation_exchange 的消息需要转发到Broker2上 federation_queue,那么"federation: federation_exchange -> rabbit02@LM-SHB-40513514"就不能有Consumer。
另外federation exchange可以成为其它节点上exchange的upstream exchange,而且还能互为upstream exchange,此时max_hops 参数表示一条消息最多被转发的此时为1。
除了federation exchange,还有federation queue,其实现原理类似,如下图,broker2上常见queue1和queue2,然后创建federation link,federation插件会自动在broker1上创建两个相同名字的queue1和queue2,并且分别对应有一条federation link,consumer2订阅broker2上的queue1或queue2,也能消费到broker1上的queue1或者queue2上的消息。
需要注意的是:加入broker2上的队列本身有消息堆积,那么broker2上的队列不会从broker1上的队列拉取消息,如果broker2上队列堆积的消息被消费完了,才会从broker1上拉取消息到本地。
但和federation exchange总结了以下三点不同:
- 一条消息可以在互为federation queue的队列上转发无限次,而federation exchange 不行。
- federation queue 只能用basic.consumer 方法来消费,不能有basic.get 方法消费。
- federation qeueu不具备传递性,如下图,queue1作为queue2的upstream queue(queue2是federation queue),queue2又作为queue3的upstream queue,但是queue3无论处于何种状态,都不会消费到queue1的消息。
如何使用
federation exchange或 federation queue 可以理解为 consumer,接收上游系统的消息,federation link是由创建federation 的节点主动发起连接,连到上游系统。
哪个节点作为consumer,接受上游的消息,就在哪个节点上创建federation。
单节点或者集群中,每个节点都需要开启 federation 插件。
如上例中,broker2上创建的federation link,那么broker2相当于broke1的consumer,broker1是broker2的上游系统。如果是在DC1和DC2中部署的集群,那么DC1和DC2的集群中的每个节点都需要启动federation插件。
下面是具体步骤。
开启federation 插件
在每个节点中启用federation插件,为方便观察,同时启用federation UI。
rabbitmq-plugins enable rabbitmq_federation
rabbitmq-plugins enable rabbitmq_federation_management
# 如果自定义了broker名字,需要使用 -n 参数指定broker名字
./rabbitmq-plugins --node rabbit01@localhost enable rabbitmq_federation rabbitmq_federation_management
./rabbitmq-plugins --node rabbit02@localhost enable rabbitmq_federation rabbitmq_federation_management
定义upstream
在broker2中定义upstream为broker1
./rabbitmqctl -n rabbit02@localhost set_parameter federation-upstream my_upstream '{"uri":"amqp://guest:guest@localhost:5672", "ack-mode":"on-confirm"}'
创建policy
使用上面的定义的upstream,创建policy,匹配所有以 federation_exchange 开头的交换器,都创建对应的federation exchange。
./rabbitmqctl -n rabbit02@localhost set_policy --apply-to exchanges my_policy "^federation_exchange" '{"federation-upstream":"my_upstream"}'
producer发布消息
Porducer可以发布消息到borker1上的 federation_exchange,代码和发送到普通交换器的代码一样,可以参考之前的文章,有很多实例代码。
consumer消费消息
Consumer可以订阅broker2上的federation_queue,代码也和普通Consumer订阅Queue的代码一样,可以参考以前的文章,有很多的实例。
好了,以上就是关于RabbitMQ Federation的使用场景、原理和使用方法的介绍了,federation queue的操作方式类似,有兴趣的小伙伴可以自己实验。
RabbitMQ系列文章会陆续更新,欢迎各位小伙伴关注后面的技术分享。