RabbitMQ 高可用之镜像队列

     若是RabbitMQ集群只有一个broker节点,那么该节点的失效将致使整个服务临时性的不可用,而且可能会致使message的丢失(尤为是在非持久化message存储于非持久化queue中的时候)。能够将全部message都设置为持久化,而且使用持久化的queue,可是这样仍然没法避免因为缓存致使的问题:由于message在发送以后和被写入磁盘并执行fsync之间存在一个虽然短暂可是会产生问题的时间窗。经过publisher的confirm机制可以确保客户端知道哪些message已经存入磁盘,尽管如此,通常不但愿遇到因单点故障致使服务不可用。node

     若是RabbitMQ集群是由多个broker节点构成的,那么从服务的总体可用性上来说,该集群对于单点失效是有弹性的,可是同时也须要注意:尽管exchange和binding可以在单点失效问题上幸免于难,可是queue和其上持有的message却不行,这是由于queue及其内容仅仅存储于单个节点之上,因此一个节点的失效表现为其对应的queue不可用。正则表达式

     举例说明一下,若是一个MQ集群由三个节点组成(MQ集群节点的模式也是有讲究的,通常三个节点会有一个RAM,两个DISK),exchange、bindings 等元数据会在三个节点之间同步,但queue上的消息是不会同步的,且不特殊设置的状况下,Queue只会在一个节点存在。可能有的同窗会提另外一个问题,我从三个MQ几点的监控面板,均可以看到这个Queue?这个是对的,这是因为Queue的元数据也是在三个节点之间同步,但Queue的实际存储只会在一个节点。咱们发送消息到指定Queue,实际上是发送消息到指定节点下的Queue。以下图所示,消息发送至队列testQueue,不管发送者经过哪一个MQ节点执行发送,其最终的执行都会是在MQ03节点执行消息的存储。

数据库

说到这儿,可能有的小伙伴就要问了?说好的,RabbitMQ集群提供高可用性呢。缓存

分析一下,RabbitMQ集群搭建完成后,若是不进行任何高可用配置,会有哪些问题呢?负载均衡

  1. 单点故障会致使消息丢失:若是MQ03节点故障,那么MQ03 中的消息就会丢失
  2. 没法最大化的利用MQ提供,提高执行效率:既然每次发送到队列testQueue的消息都会在MQ03节点存储,那么何须搭建集群。

     引入RabbitMQ的镜像队列机制,将queue镜像到cluster中其余的节点之上。在该实现下,若是集群中的一个节点失效了,queue能自动地切换到镜像中的另外一个节点以保证服务的可用性。在一般的用法中,针对每个镜像队列都包含一个master和多个slave,分别对应于不一样的节点。slave会准确地按照master执行命令的顺序进行命令执行,故slave与master上维护的状态应该是相同的。除了publish外全部动做都只会向master发送,而后由master将命令执行的结果广播给slave们,故看似从镜像队列中的消费操做其实是在master上执行的。
      一旦完成了选中的slave被提高为master的动做,发送到镜像队列的message将不会再丢失:publish到镜像队列的全部消息老是被直接publish到master和全部的slave之上。这样一旦master失效了,message仍然能够继续发送到其余slave上。spa

简单来讲,镜像队列机制就是将队列在三个节点之间设置主从关系,消息会在三个节点之间进行自动同步,且若是其中一个节点不可用,并不会致使消息丢失或服务不可用的状况,提高MQ集群的总体高可用性。code

先来看下设置镜像队列后的效果: 镜像队列会出现+2标识。blog

 

1.设置队列为镜像队列:Howrabbitmq

 两种方式:队列

  1. 经过监控面板设置

     

  2. 经过命令设置

 

rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]

-p Vhost: 可选参数,针对指定vhost下的queue进行设置
Name: policy的名称
Pattern: queue的匹配模式(正则表达式)
Definition:镜像定义,包括三个部分ha-mode, ha-params, ha-sync-mode
        ha-mode:指明镜像队列的模式,有效值为 all/exactly/nodes
        all:表示在集群中全部的节点上进行镜像
        exactly:表示在指定个数的节点上进行镜像,节点的个数由ha-params指定
        nodes:表示在指定的节点上进行镜像,节点名称经过ha-params指定
        ha-params:ha-mode模式须要用到的参数
        ha-sync-mode:进行队列中消息的同步方式,有效值为automatic和manual
priority:可选参数,policy的优先级

 请注意一个事实,镜像配置的pattern 采用的是正则表达式匹配,也就是说会匹配一组。

 

RabbitMQ集群节点失效,MQ处理策略

      若是某个slave失效了,系统处理作些记录外几乎啥都不作:master依旧是master,客户端不须要采起任何行动,或者被通知slave失效。
若是master失效了,那么slave中的一个必须被选中为master。被选中做为新的master的slave一般是最老的那个,由于最老的slave与前任master之间的同步状态应该是最好的。然而,特殊状况下,若是存在没有任何一个slave与master彻底同步的状况,那么前任master中未被同步的消息将会丢失。

镜像队列消息的同步:

     将新节点加入已存在的镜像队列时,默认状况下ha-sync-mode=manual,镜像队列中的消息不会主动同步到新节点,除非显式调用同步命令。当调用同步命令后,队列开始阻塞,没法对其进行操做,直到同步完毕。当ha-sync-mode=automatic时,新加入节点时会默认同步已知的镜像队列。因为同步过程的限制,因此不建议在生产的active队列(有生产消费消息)中操做。

rabbitmqctl list_queues name slave_pids synchronised_slave_pids   查看那些slaves已经完成同步
rabbitmqctl sync_queue name    手动的方式同步一个queue
rabbitmqctl cancel_sync_queue name 取消某个queue的同步功能 

以上针对消息同步的命令,都可以经过监控界面来进行操做,最终也是经过这些操做命令执行。

 

说明:

  1. 镜像队列不是负载均衡,镜像队列没法提高消息的传输效率,或者更进一步说,因为镜像队列会在不一样节点之间进行同步,会消耗消息的传输效率。
  2. 对exclusive队列设置镜像并不会有任何做用,由于exclusive队列是链接独占的,当链接断开,队列自动删除。因此实际上这两个参数对exclusive队列没有意义。那么有哪些队列是exclusive呢?通常来讲,发布订阅队列及设置了该参数的队列都是exclusive 排他性队列。 如何肯定一个队列是否是排他性队列呢? 若是队列的features包含Excl,就表明它是排他性队列。

     

 镜像队列中某个节点宕掉的后果:

  当slave宕掉了,除了与slave相连的客户端链接所有断开以外,没有其余影响。

   当master宕掉时,会有如下连锁反应:

      1. 与master相连的客户端链接所有断开;
      2.选举最老的slave节点为master。若此时全部slave处于未同步状态,则未同步部分消息丢失;
      3.新的master节点requeue全部unack消息,由于这个新节点没法区分这些unack消息是否已经到达客户端,亦或是ack消息丢失在老的master的链路上,亦或者是丢在master组播ack消息到全部slave的链路上。因此处于消息可靠性的考虑,requeue全部unack的消息。此时客户端可能有重复消息;
     4.若是客户端连着slave,而且Basic.Consume消费时指定了x-cancel-on-ha-failover参数,那么客户端会受到一个Consumer Cancellation Notification通知。若是未指定x-cancal-on-ha-failover参数,那么消费者就没法感知master宕机,会一直等待下去。
这就告诉咱们,集群中存在镜像队列时,从新master节点有风险。

镜像队列中节点启动顺序,很是有讲究: 

假设集群中包含两个节点,通常生产环境会部署三个节点,但为了方便说明,采用两个节点的形式进行说明。

场景1:A先停,B后停
该场景下B是master,只要先启动B,再启动A便可。或者先启动A,再在30s以内启动B便可恢复镜像队列。(若是没有在30s内回复B,那么A本身就停掉本身

场景2:A,B同时停
该场景下多是由掉电等缘由形成,只需在30s内联系启动A和B便可恢复镜像队列。

场景3:A先停,B后停,且A没法恢复。
由于B是master,因此等B起来后,在B节点上调用rabbitmqctl forget_cluster_node A以接触A的cluster关系,再将新的slave节点加入B便可从新恢复镜像队列。

场景4:A先停,B后停,且B没法恢复
该场景比较难处理,旧版本的RabbitMQ没有有效的解决办法,在如今的版本中,由于B是master,因此直接启动A是不行的,当A没法启动时,也就没版本在A节点上调用rabbitmqctl forget_cluster_node B了,新版本中forget_cluster_node支持-offline参数,offline参数容许rabbitmqctl在离线节点上执行forget_cluster_node命令,迫使RabbitMQ在未启动的slave节点中选择一个做为master。当在A节点执行rabbitmqctl forget_cluster_node -offline B时,RabbitMQ会mock一个节点表明A,执行forget_cluster_node命令将B提出cluster,而后A就能正常启动了。最后将新的slave节点加入A便可从新恢复镜像队列

场景5:A先停,B后停,且A和B均没法恢复,可是能获得A或B的磁盘文件
这个场景更加难以处理。将A或B的数据库文件($RabbitMQ_HOME/var/lib目录中)copy至新节点C的目录下,再将C的hostname改为A或者B的hostname。若是copy过来的是A节点磁盘文件,按场景4处理,若是拷贝过来的是B节点的磁盘文件,按场景3处理。最后将新的slave节点加入C便可从新恢复镜像队列。

场景6:A先停,B后停,且A和B均没法恢复,且没法获得A和B的磁盘文件
无解。

启动顺序中有一个30s 的概念,这个是MQ 的时间间隔,用于检测master、slave是否可用,所以30s 很是关键。

 对于生产环境MQ集群的重启操做,须要分析具体的操做顺序,不可无序的重启,会有可能带来没法弥补的伤害(数据丢失、节点没法启动)。

简单总结下:镜像队列是用于节点之间同步消息的机制,避免某个节点宕机而致使的服务不可用或消息丢失,且针对排他性队列设置是无效的。另外很重要的一点,镜像队列机制不是负载均衡 

相关文章
相关标签/搜索