第8章 跨越集群的界限

第8章 跨越集群的界限

8.1 Federation

Federation插件的设计目标是使RabbitMQ在不同的Broker节点之间进行消息传递而无须建立集群,该功能在很多场景下都非常有用:

Federation插件能够在不同管理域(可能设置了不同的用户和vhost,也可能运行在不同版本的RabbitMQ和Erlang上)中的Broker或集群之间传递消息。

Federatin插件基于AMQP 0-9-1 协议在不同的Broker之间进行通信,并设计成能够容忍不稳定的网络连接情况。

一个Broker节点中可以同时存在联邦交换器(或队列)或者本地交换器(或队列),只需要对特定的交换器(或队列)创建Federation连接(Federation link)。

Federation不需要在N个Broker节点之间创建O(N2)个连接(尽管这是最简单的使用方式),这也就意味着Federation在使用时更容易扩展。

Federation 插件可以让多个交换器或者多个队列进行联邦。一个联邦交换器(federated exchange)或则一个联邦队列(federated queue)接收上游(upstream)的消息,这里的上游是指位于其他Broker上的交换器或者队列。联邦交换器能够将原本发送给上游交换器(upstream exchange)的消息路由到本地的某个队列中;联邦队列则允许一个本地消费者接收到来自上游队列(upstream queue)的消息。

为了能够详细解释Federation,我们先从架构来分析联邦交换器和联邦队列,之后再来讲解如何使用Federation。

8.1.1 联邦交换器

假设图8-1中broker1部署在北京,broker2部署在上海,而broker3部署在广州,彼此之间相距甚远,网络延迟是一个不得不面对的问题。

图 8-1 联邦交换器

有一个在广州的业务ClientA需要连接broker3,并向其中的交换器exchangeA发送消息,此时的网络延迟很小,ClientA可以迅速将消息发送至exchangeA中,就算在开启了publisher confirm机制或者事务机制的情况下,也可以迅速收到确认消息。

此时又有一个在北京的业务ClientB需要向exchangeA发送消息,那么ClientB与broker3之间有很大的网络延迟,ClientB将发送消息至exchangeA会经历一定的延迟,尤其是在开启了publiser confirm 机制或者事务机制的情况下,ClientB会等待很长的延迟时间来接受broker3的确认信息,进而必然造成这条发送线程的性能降低,甚至造成一定程度上的阻塞

那么要怎么优化业务ClientB呢?将业务ClientB部署到广州的机房中可以解决这个问题,但是如果ClientB调用的另一些服务都部署在北京,那么又会引发新的时延问题,总不见得将所有业务全部部署在一个机房,那么容灾又何以实现?这里使用Federation插件就可以很好地解决这个问题。

如图8-2所示,在broker3中为交换器exchangeA(broker3中的队列queueA通过“rkA”与exchangeA进行了绑定)与广州的broker1之间建立一条单向的Federation link。此时Federation插件会在broker1上会建立一个同名的交换器exchangeA(这个名称可以配置,默认同名),同时建立一个内部的交换器“exchangeA -> broker3 B”,并通过路由键“rkA”将这两个交换器绑定起来。这个交换器“exchangeA -> broker3 B”名字中的“broker3”是集群名,可以通过 rabbitmqctl set_cluster_name {new_name}命令进行修改。

与此同时Federation插件还会在broker1上建立一个队列“federation:exchangeA->broker3 B”,并与交换器“exchangeA->broker3 B”进行绑定。

Federation插件会在队列“federation:exchangeA->broker3”与broker3中的交换器exchangeA之间建立一条AMQP连接来实时地消费队列“federation:exchangeA->borker3 B”中的数据。这些操作都是内部的,对外部业务客户端来说这条Federation link建立在broker1的exchangeA和broker3的exchangeA之间。

图 8-2 建立Federation link​​​

回到前面的问题,部署在北京的业务ClientB可以连接broker1并向exchangeA发送消息,这样ClientB可以迅速发送完消息并收到确认信息,而之后消息会通过Federation link转发到broker3的交换器exchangeA中经过Federation link 转发的消息会带有特殊的headers属性标记。例如向broker1中的交换器exchangeA发送一条内容为“federation test payload.”的持久化消息,之后可以在broker3中的队列queueA中消费到这条消息,详细如图8-3所示。

图 8-3 消息的内容

Federation 不仅便利于消息生产方,同样也便利于消息消费方。

假设某生产者将消息存入broker1中的某个队列queueB,在广州的业务ClientC想要消费queueB的消息,消息的流转及确认必然要忍受较大的网络延迟,内部编码逻辑也会因这一因素变得更加复杂,这样不利于ClientC的发展。不如将这个消息转发的过程以及内部复杂的编码逻辑交给Federation去完成,而业务方在编码时不必再考虑网络延迟的问题。Federation使得生产者和消费者可以异地部署而又让这两方感受不到过多的差异。

图 8-2 中 broker1 的队列“federation:exchangeA->broker3 B”是一个相对普通的队列,可以直接通过客户端进行消费。假设此时还有一个客户端ClientD通过Basic.Consume来消费队列“federation:exchangeA->broker3 B”的消息,那么发往broker1中的exchangeA的消息会有一部分(一半)被ClientD消费掉,而另一半会发往broker3的exchangeA。所以如果业务应用有要求所有发往broker1中的exchangeA的消息都要转发至broker3的exchangeA中,此时就要注意队列“federation:exchangeA->broker3 B”不能有其他的消费者;而对于“异地均摊消费”这种特殊需求,队列“federation:exchangeA->broker3 B”这种天生特性提供了支持。对于broker1的交换器exchangeA而言,它是一个普通的交换器,可以创建一个新的队列绑定它,对它的用法没有什么特殊之处。

如图 8-4 所示,一个federated exchange 同样可以成为另一个交换器的upstream exchange。

图 8-4 federated exchange 成为另一个交换器的 upstream exchange

同样如图 8-5 所示,两方的交换器可以互为federated exchange 和 upstream exchange。其中参数“max_hops=1”表示一条消息最多被转发的次数为1。

图 8-5 两方的交换器互为federated exchange 和 upstream exchange

需要特别注意的是,对于默认的交换器(每个vhost下都会默认创建一个名为“”的交换器)和内部交换器而言,不能对其使用Federation的功能。

对于联邦交换器而言,还有更复杂的拓扑逻辑部署方式。比如图8-6中“fan-out”的多叉树形式,或者图8-7中“三足鼎立”的情形。 

图 8-6 “fan-out”的多叉树

 

图 8-7 “三足鼎立”

还有环形的拓扑部署,如图8-8所示。关于更多的拓扑部署就留给读者自行思考了。

图 8-8 环形的拓扑部署

 

 

8.1.2 联邦队列

除了联邦交换器,RabbitMQ还可以支持联邦队列(federated queue)。联邦队列可以在多个Broker节点(或者集群)之间为单个队列提供均衡负载的功能。一个联邦队列可以连接一个或者多个上游队列(upstream queue),并从这些上游队列中获取消息以满足本地消费者消费消息的需求

图8-9演示了位于两个Broker中的几个联邦队列(灰色)和非联邦队列(白色)。队列queue1和queue2原本在broker2中,由于某种需求将其配置为federated queue并将broker1作为upstream。Federation 插件会在broker1上创建同名的队列queue1和queue2,与broker2中的队列queue1和queue2分别建立两条单向独立的Federation link。当消费者ClientA连接broker2并通过Basic.Consume消费队列queue1(或queue2)中的消息时,如果队列queue1(或queue2)中本身有若干消息堆积,那么ClientA直接消费这些消息,此时broker2中的queue1(或queue2)并不会拉取broker1中的queue1(或queue2)的消息;如果队列queue1(或queue2)中没有消息堆积或者消息被消费完了,那么它会通过Federation link拉取在broker1中的上游队列queue1(或queue2)中的消息(如果有消息),然后存储到本地,之后再被消费者ClientA进行消费。

图 8-9 联邦队列

消费者既可以消费broker2中的队列,又可以消费broker1中的队列,Federation的这种分布式队列的部署可以提升单个队列的容量。如果在broker1一端部署的消费者来不及消费队列queue1中的消息,那么broker2一端部署的消费者可以为其分担消费,也可以达到某种意义上的负载均衡。

和federated exchange 不同,一条消息可以在联邦队列间转发无限次。如图8-10中两个队列queue互为联邦队列。

图 8-10 互为联邦队列

队列中的消息除了被消费,还会转向有多余消费能力的一方,如果这种“多余的消费能力”在broker1和broker2中来回切换,那么消费也会在broker1和broker2中的队列queue中来回转发。 

可以在其中一个队列上发送一条消息“msg”,然后再分别创建两个消费者ClientB和ClientC分别连接broker1和broker2,并消费队列queue中的消息,但是并不需要确认消息(消费完消息不需要调用Basic.Ack)。来回开启/关闭ClientB和ClientC可以发现消息“msg”会在broker1和broker2之间串来串去。

图 8-11 中的 broker2 的队列queue 没有消息堆积或者消息被消费完之后并不能通过Basic.Get来获取broker1中队列queue的消息。因为Basic.Get是一个异步的方法,如果要从broker1中队列queue拉取消息,必须要阻塞等待通过Federation link拉取消息存入broker2中的队列queue之后再消费消息,所以对于federated queue 而言只能使用Basic.Consume进行消费。

federated queue并不具备传递性。考虑图 8-11 的情形,队列queue2作为federated queue 与 队列 queue1 进行联邦,而队列 queue2 又作为队列 queue3 的 upstream queue,但是这样队列queue1与queue3之间并没有产生任何联邦的关系。如果队列queue1中有消息堆积,消费者连接broker3消费queue3中的消息,无论queue3处于何种状态,这些消费者都消费不到queue1中的消息,除非queue2有消费者。

图 8-11 联邦队列的传递性

注意要点:

理论上可以将一个federated queue与一个federated exchange 绑定起来,不过这样会导致一些不可预测的结果,如果对结果评估不足,建议慎用这种搭配方式。 

8.1.3 Federation的使用

为了能够使用Federation功能,需要配置以下2个内容:

(1)需要配置一个或多个upstream,每个upstream均定义了到其他节点的Federation link。这个配置可以通过运行时的参数(Runtime Parameter)来完成,也可以通过federation management插件来完成。

(2)需要定义匹配交换器或者队列的一种/多种策略(Policy)

Federation 插件默认在RabbitMQ发布包中,执行 rabbitmq-plugins enable rabbitmq_federation 命令可以开启Federation功能。

由前面的讲解可知,Federation内部基于AMQP协议拉取数据,所以在开启rabbitmq_federation插件的时候,默认会开启amqp_client插件。同时,如果要开启Federation的管理插件,需要执行 rabbitmq-plugins enable rabbitmq_federation_management 命令。

注意要点:

当需要在集群中使用Federation功能的时候,集群中所有的节点都应该开启Federation插件。

有关Federation upstream 的信息全部都保存在RabbitMQ的Mnesia数据库中,包括用户信息、权限信息、队列信息等。在Federation中存在3种级别的配置。

(1)Upstreams:每个upstream用于定义与其他Broker建立连接的信息。

(2)Upstream sets:每个upstream set 用于对一系列使用Federation功能的upstream进行分组。

(3)Policies:每一个Policy会选定出一组交换器,或者队列,亦或者两者皆有而进行限定,进而作用于一个单独的upstream或者upstream set之上。

正确地使用Federation插件,首先以图8-2中broker1和broker3的关系来讲述如何建立federated exchange。

第一步

需要在broker1和broker3中开启rabbitmq_federation插件,最好同时开启rabbitmq_federation_management插件。

第二步

在broker3中定义一个upstream。

rabbitmqctl set_parameter federation-upstream f1 '{"uri":"amqp://root:root@192.168.0.2:5672","ack-mode":"on-confirm"}'

通用的参数如下所述

  • Name:定义这个upstream的名称。必填项。
  • URI:定义upstream的AMQP连接。必填项。
  • Prefetch count(prefetch_count):定义Federation内部缓存的消息条数,即在收到上游消息之后且在发送到下游之前缓存的消息条数。
  • Reconnect delay(reconnect-delay):Federation link 由于某种原因断开之后,需要等待多少秒开始重新建立连接。
  • Acknowledgement Mode(ack-mode):定义Federation link的消息确认方式。共有3种:on-confirmon-publishno-ack。默认为on-confirm,表示在接收到下游的确认消息(等待下游的Basic.Ack)之后再向上游发送消息确认,这个选项可以确保网络失败或者Broker宕机时不会丢失消息,但也是处理速度最慢的选项。如果设置为on-publish,则表示消息发送到下游后(并不需要等待下游的Basic.Ack)再向上游发送消息确认,这个选项可以确保在网络失败的情况下不会丢失消息,但不能确保Broker宕机时不会丢失消息。no-ack表示无须进行消息确认,这个选项处理速度最快,但也最容易丢失消息。
  • Trust User-ID(trust-user-id):设定Federation是否使用“Validated User-ID”这个功能。如果设置为false或者没有设置,那么Federation会忽略消息的user_id这个属性;如果设置为true,则Federation只会转发user_id为上游任意有效的用户的消息。

第三步

定义一个Policy用于匹配交换器exchangeA,并使用第二步中所创建的upstream。

rabbitmqctl set_policy --apply-to exchanges p1 "^exchange" '{"federation-upstream":"f1"}'

8.2 Shovel

8.2.1 Shovel的原理

8.2.2 Shovel的使用

8.2.3 案例:消息堆积的治理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值