一、镜像模式集群简介

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

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

    引入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上。

    RabbitMQ的镜像队列同时支持publisher confirm和事务两种机制。在事务机制中,只有当前事务在全部镜像queue中执行之后,客户端才会收到Tx.CommitOk的消息。同样的,在publisher confirm机制中,向publisher进行当前message确认的前提是该message被全部镜像所接受了。

二、开始配置

镜像队列是基于普通的集群模式的,所以还是得先配置普通集群,然后才能设置镜像队列,这里不再赘述,可以参考我的另一篇文章http://linuxg.blog.51cto.com/4410110/1965369配置普通集群。

1、首先查看策略
#rabbitmqctl -n rabbit1 list_policies    #以节点rabbit2和rabbit3查询结果是一样的
Listing policies
2、设置镜像队列策略
用法:
set_policy [-p vhost] [--priority priority] [--apply-to apply-to] {name} {pattern} {definition}
      Sets a policy.
      name
         The name of the policy.
      pattern
         The regular expression, which when matches on a given resources causes the policy to apply.
      definition
         The definition of the policy, as a JSON term. In most shells you are very likely to need to quote this.
      priority
         The priority of the policy as an integer. Higher numbers indicate greater precedence. The default is 0.
      apply-to
         Which types of object this policy should apply to - "queues", "exchanges" or "all". The default is "all".
      For example:
         rabbitmqctl set_policy federate-me "^amq." '{"federation-upstream-set":"all"}'
         This command sets the policy federate-me in the default virtual host so that built-in exchanges are federated.
 释义:
-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的优先级
ha-promote-on-shutdown: 用来控制选主的行为的,有效值为when-synced,always
3、开始设置:
#rabbitmqctl -n rabbit1 set_policy mirror_queue "^" '{"ha-mode":"all","ha-sync-mode":"automatic","ha-promote-on-shutdown":"always"}'
Setting policy "mirror_queue" for pattern "^" to "{\"ha-mode\":\"all\",\"ha-sync-mode\":\"automatic\",\"ha-promote-on-shutdown\":\"always\"}" with priority "0"
注意:"^" 可以使用正则表达式,比如"^queue_" 表示对队列名称以“queue_”开头的所有队列进行镜像
4、再次查看策略
#rabbitmqctl -n rabbit1 list_policies
Listing policies
/       mirror_queue    all     ^       {"ha-mode":"all","ha-sync-mode":"automatic","ha-promote-on-shutdown":"always"}  0
#rabbitmqctl -n rabbit2 list_policies
Listing policies
/       mirror_queue    all     ^       {"ha-mode":"all","ha-sync-mode":"automatic","ha-promote-on-shutdown":"always"}  0
#rabbitmqctl -n rabbit3 list_policies
Listing policies
/       mirror_queue    all     ^       {"ha-mode":"all","ha-sync-mode":"automatic","ha-promote-on-shutdown":"always"}  0

可以通过rabbitmq_management界面查看策略,Admin-->Policies, 如下图:

wKiom1nA4E_RarTDAADhKIL1UB0560.png

同样的,也可以通过此界面添加policies,下面会介绍。

5、取消镜像队列(在哪一个节点都可以)
#rabbitmqctl -n rabbit1 clear_policy mirror_queue
Clearing policy "mirror_queue"
在每个节点查看, 就不会在看到镜像队列了
#rabbitmqctl -n rabbit3 list_policies
Listing policies
#rabbitmqctl -n rabbit2 list_policies
Listing policies
#rabbitmqctl -n rabbit1 list_policies
Listing policies

6、通过rabbitmq_management界面创建策略:Admin-->Policies

wKioL1nEpjjAhQWIAABVqkJmitM489.jpg

填写好策略信息之后,点击“Add Policy”,然后会看到添加好的策略:

wKioL1nEpsaADKCWAAB4X2Rx4h8955.jpg

三、消息的同步 

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

使用下面的命令来查看那些slaves已经完成同步:
#rabbitmqctl -n rabbit1 list_queues consumers messages name slave_pids synchronised_slave_pids
Listing queues
0       0       book.data.chapter.queue [<rabbit2@localhost.2.5129.0>, <rabbit3@localhost.3.5082.0>]    [<rabbit2@localhost.2.5129.0>, <rabbit3@localhost.3.5082.0>]
可以通过手动的方式同步一个queue:
#rabbitmqctl -n rabbit1 sync_queue name
同样也可以取消某个queue的同步功能:
#rabbitmqctl -n rabbit1 cancel_sync_queue name
这些都可以通过management插件来设置。

四、rabbitmq持久化

 RabbitMQ持久层的目的是为了得到好的结果,在大多数情况下没有配置。然而,一些配置有时是有用的。此页解释了如何配置它。在采取任何行动之前,你都应该阅读这一切。

持久化如何工作?

首先,先讲一下背景: 持久化和短暂消息都可以写入磁盘.持久化消息一旦到达队列,就会写入磁盘,而短暂消息只在内存压力较大被赶出内存时才会写入磁盘.持久化消息在内存紧张释放内存时,依然也会存在内存中. 持久层指的是存储这两种类型消息到磁盘的机制.我们说的队列是指无镜像队列或master队列或slave队列. 队列镜像会发生以上的持久化.

持久层有两个组件: 队列索引和消息存储.队列索引负责维护消息在队列的位置,以及是否被投递,是否应答的信息. 因此,每个队列都有一个队列索引。

消息存储是消息的key-value存储, 由服务器中的所有队列共享.消息(消息体, 消息属性或消息头)可直接存储于队列索引,也可以写到消息存储中.在技术上有两个消息存储(一个暂时的和一个持久的消息),但他们通常一起被被认为是“消息存储”。

queue的持久化是通过durable=true来实现的。 
一般程序中这么使用:
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("queue.persistent.name", true, false, false, null);
关键的是第二个参数设置为true,即durable=true.
参考链接:

RabbitMQ安全特性

1、publish消息确认机制
如果采用标准的 AMQP 协议,则唯一能够保证消息不会丢失的方式是利用事务机制 — 令 channel 处于 transactional 模式、向其 publish 消息、执行commit动作
在这种方式下,事务机制会带来大量的多余开销,并会导致吞吐量下降 250% 。为了补救事务带来的问题,引入了 confirmation 机制(即 Publisher Confirm)。
confirm 机制是在channel上使用 confirm.select方法,处于 transactional 模式的 channel 不能再被设置成 confirm 模式,反之亦然。
在 channel 被设置成 confirm 模式之后,所有被 publish 的后续消息都将被 confirm(即 ack) 或者被 nack 一次。但是没有对消息被confirm的快慢做任何保证
并且同一条消息不会既被 confirm 又被 nack 。
RabbitMQ 将在下面的情况中对消息进行 confirm :
RabbitMQ发现当前消息无法被路由到指定的 queues 中;
非持久属性的消息到达了其所应该到达的所有 queue 中(和镜像 queue 中);
持久消息到达了其所应该到达的所有 queue 中(和镜像 queue 中),并被持久化到了磁盘(被 fsync);
持久消息从其所在的所有 queue 中被 consume 了(如果必要则会被 acknowledge)。
2、consumer消息确认机制
为了保证数据不被丢失,RabbitMQ支持消息确认机制,即acknowledgments。
如果没启动消息确认机制,RabbitMQ在consumer收到消息后就会把消息删除。
启用消息确认后,consumer在处理数据后应通过回调函数显示发送ack, RabbitMQ收到ack后才会删掉数据。如果consumer一段时间内不回馈,RabbitMQ会将该消息重
新分配给另外一个绑定在该队列上的consumer。另一种情况是consumer断开连接,但是获取到的消息没有回馈,则RabbitMQ同样重新分配。
注意:如果consumer 没调用basic.qos 方法设置prefetch_count=1,那即使该consumer有未ack的messages,RabbitMQ仍会继续发messages给它。

3、消息持久化
消息确认机制确保了consumer退出时消息不会丢失,但如果是RabbitMQ本身因故障退出,消息还是会丢失。为了保证在RabbitMQ出现意外情况时数据仍没有丢失,
需要将queue和message都要持久化。

queue持久化:
channel.queue_declare(queue=’hello’, durable=True)
message持久化:
channel.basic_publish(exchange=”,
routing_key=”task_queue”,
body=message,
properties=pika.BasicProperties(
delivery_mode = 2,)        #消息持久化
)

即使有消息持久化,数据也有可能丢失,因为rabbitmq是先将数据缓存起来,到一定条件才保存到硬盘上,这期间rabbitmq出现意外数据有可能丢失。
网上有测试表明:持久化会对RabbitMQ的性能造成比较大的影响,可能会下降10倍不止。

rabbitm.jpg

参考链接:http://blog.csdn.net/u013256816/article/details/71097186