本文分析一下RabbitMQ的可用性和可靠性。分析一下RabbitMQ如何在可用性和可靠性之间进行权衡的。
1. 基本概念。
首先,介绍几个RabbitMQ的重要的基本概念。这些概念是理解RabbitMQ的可用性和可靠性的基础。
镜像队列(Mirrored Queue)
RabbitMQ集群的队列(Queue)在默认的情况下只存在单一节点(node)上。但是,我们也可以把队列配置成同时存在在多个节点上,也就是说队列可以被镜像到多个节点上。发布(publish)到镜像队列上的消息(message)会被复制(replicated)到说有的节点上。一个镜像队列包含一个主(master)和多个从(slave)。
非同步的Slave (unsynchronised slave)
在很多其他产品中,同步(Synchronation)这个词是和异步(Asynchronation)对立使用。但在RabbitMQ中,同步(synchronised)这个词是和非同步(unsynchronised)这个词对立的。这有什么区别那?在rabbitmq中同步(synchronised)是用来描述master和slave之间的数据状态是否一致的。如果slave包含master中的所有message,则这个slave是synchronised,如果这个slave并没有包含所有master中的message,则这个slave是unsynchronised。同步与异步的对立使用的典型的例子是mysql的主从数据复制,数据复制过程可以分为同步复制和异步复制。
在什么情况下会出现unsynchronised slave?
当一个新slave加入到一个镜像队列时,这时这个新slave是空的,而master中这时可能包含之前接收到的消息。假设这时master包含了5条消息,这是第6条消息被添加到这个镜像队列中,这个新slave会从这个第6条消息开始接收。这时这个slave就是unsynchronised slave。随着前5条消息从镜像队列中被消费掉(consumed), 这个slave变成了synchronised。
另外一种情况,slave 重新加入(rejoin) 到镜像队列时,也会出现非同步的情况。一个slave出于很多情况会重新加入镜像队列,网络分区(network partition)就可能导致这种情况。一个slave要重新加入镜像队列之前,slave可能已经接收了一些消息,它要重新加入镜像队列,就要清空自己之前已经接收的所有消息,好像自己是第一次加入队列一样。
选主方式
由于RabbitMQ的slave加入和重新加入队列的方式,我们得出一个结论,越早加入队列的slave,越有更大的机会是同步状态的,所以RabbitMQ通过这种方式选主:但master因为某种原因消失时,最老的slave被提升成master。
2. RabbitMQ的可用性(Availablity)和数据可靠性(Reliability)
接下来,我们分析一下RabbitMQ的可用性(Availablity)和数据可靠性(Reliability)。RabbitMQ通过参数配置的方式,在可用性(Availablity)和数据可靠性(Reliability)做出了一定的权衡。下面我们来看看这些参数。
参数ha-sync-mode
镜像队列有一个配置参数ha-sync-mode,这个参数有2中取值:automatic, manual。
manual是默认值。如果镜像队列被设置成munual,当一个slave加入和重新加入队列时的行为,就是我们上面描述的行为,之所以叫manual,就是我们可以通过命令行手工(manually)进行同步。命令如下:
rabbitmqctl sync_queue name
如果镜像队列被设置成automatic,当一个新slave加入时,slave会自动同步master中的所有消息,直到所有消息被同步完成之前,所有的操作都会被阻塞(blocking)。
这个参数是可用性和可靠性的一个平衡,manual不保证数据可靠性,在某些情况会出现丢消息的可能,但是保证了队列的可用性。automatic提高了数据的可靠性,但是当有新slave加入时,可能会出现队列的暂时不可用。
参数ha-promote-on-shutdown
镜像队列的另外一个参数ha-promote-on-shutdown,也在可用性和可靠性之间做了一个平衡。ha-promote-on-shutdown有2个取值:when-synced,always。默认是when-synced。ha-promote-on-shutdown是用来控制选主的行为的。
当取值为when-synced时,在可控的master关闭时(比如停止RabbitMQ服务或者关闭操作系统),RabbitMQ会拒绝故障恢复(fail over)到一个非同步slave,也即拒绝把一个非同步的slave提升成新的master。只有在非可控的master关闭时(比如server crash, 断网),才会故障恢复到一个非同步的slave。
当取值为always时,则在所有情况下,都不会拒绝故障恢复到非同步的slave。
很明显,这个参数也是平衡可用性和可靠性的,当when-synced,可靠性更好,可用性降低了,因为如果所有的slave都是非同步状态,那就没有符合条件的slave可以被提升成master,这时队列就处在不可用状态。