RabbitMQ概念及环境搭建(四)RabbitMQ High Availability

####################################################
RabbitMQ High Availability
####################################################
1.高可用queue
默认情况下RabbitMQ cluster中的queues位于单独的节点(queues被首次声明的节点),而exchanges和bindings存在于cluster中各节点。因而每个节点失效exchanges和bindings并无影响,单弱queues所在的节点失效就会影响该节点上存在的queues。但是。queues可以在多个节点上镜像。每个被镜像的queue包含一个master和一个或者多个slave。若原master失效则最旧的slave被提升为新的master。

publish到queue的message会被复制到各个slave、consumers无论从cluster中哪个节点连接都会连接到master上,对于已经自master上被确认的message各slave会丢弃。所以,queue镜像只是增强了可用性。并没有在各节点间均衡负载。

该方案需要RabbitMQ cluster, 不能再cluster内处理network partitions因而不推荐在WAN中使用,仅用于LAN。

2.配置镜像
通过policy进行,可在任意时刻更改,将non-mirrored queue改为mirrored queue,或者反之。需注意的是non-mirrored queue与没有slave的mirrore的queue并不相同,前者无需额外mirroring架构运行的更快。
可用的policy
ha-mode   ha-params   行为
all                              queue被mirror到cluster中所有节点。
                                 cluster中新添加节点,queue也会被mirror到该节点。
exactly   count            queue被mirror到指定数目的节点。
                                 count大于cluster中节点数则queue被mirror到所有节点。
                                 若count小于cluster中节点数,在包含mirror的某节点down后不会在其他节点建新的mirror(为避免cluster中queue migrating)
nodes     node names  queue被mirror到指定名字的节点。
                                 若任一名称在cluster中不存在并不会引发错误。
                                若指定的任何节点在queue声明时都不在线则queue在被连接到的节点创建。

关于nodes类型的policy
设置或更改nodes类型的policy可能引起原master迁移。比如原master没有出现的新的lolicy节点列表中时。RabbitMQ采取的策略是保持原master直到至少一个新的slave已经同步,一旦同步后原master马上自动失效。原consumer连接会断掉,需重连。
如,原queue在节点{A,B},A为master。来一个新的policy{C,D},会有一段时间的状态为{A,C,D},待queue同步后A上的master关闭,新的mirror为{C,D}

特殊queue:Exclusivequeues
exclusive queues不会被镜像(mirror)也不会被持久化(durable),即使被声明为如此。因为一但存放queue的节点down了上边的连接也会断掉,对于exclusive queues相关的queue会被马上删除。

3.镜像配置示例(可通过多种方式)
下面的例子将“ha.”开头的queue mirror到cluster中所有节点
rabbitmqctl   rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all"}'
HTTP API    PUT /api/policies/%2f/ha-all {"pattern":"^ha\.", "definition":{"ha-mode":"all"}}
Web UI        Navigate to Admin > Policies > Add / update a policy.
                  Enter "ha-all" next to Name, "^ha\." next to Pattern, and "ha-mode" = "all" in the first line next to Policy.
                  Click Add policy.

下面的例子将“two.”开头的queue mirror到cluster中两个节点,且自动同步
rabbitmqctl    rabbitmqctl set_policy ha-two "^two\." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
HTTP API    PUT /api/policies/%2f/ha-two {"pattern":"^two\.", "definition":{"ha-mode":"exactly", "ha-params":2,"ha-sync-mode":"automatic"}}
Web UI        Navigate to Admin > Policies > Add / update a policy.
                   Enter "ha-two" next to Name and "^two\." next to Pattern.
                   Enter "ha-mode" = "exactly" in the first line next to Policy, then "ha-params" = 2 in the second line, then "ha-sync-mode" = "automatic" in the third, and set the type on the second line to "Number".
                   Click Add policy.


下面的例子将“nodes.”开通的queue mirror到cluster中特定的node
rabbitmqctl   rabbitmqctl set_policy ha-nodes "^nodes\." '{"ha-mode":"nodes","ha-params":["nodeA", "nodeB"]}'
HTTP API    PUT /api/policies/%2f/ha-nodes {"pattern":"^nodes\.", "definition":{"ha-mode":"nodes", "ha-params":["nodeA", "nodeB"]}
Web UI        Navigate to Admin > Policies > Add / update a policy.
                   Enter "ha-nodes" next to Name and "^nodes\." next to Pattern.
                   Enter "ha-mode" = "nodes" in the first line next to Policy, then "ha-params" in the second line, set the second line's type to "List", and then enter "nodeA" and "nodeB" in the sublist which appears.
                  Click Add policy.

4.其他注意事项
非同步的slaves
node可在任意时刻加入RabbitMQ cluster。根据queue的policy,当一个node加入cluster时,queues可能在这个新的node上添加一个slave。此时此刻,新的slave是空的,并不包含被镜像的queue中存在的内容。该slave将接收publish到其queue的新的message。这样一来,一段时间后salve上的queue相当于被镜像的queue的“尾巴”,且随着时间的推移被镜像的queue的“头部”越来越小,而salve上的queue的“尾巴”越来越长,最终salve的内容会与master上的内容完全同步。需要注意的是,这里有个提前提条件,即客户端需从master上的queue中不断消耗既存的message以达到master和salve的同步。

因此,新添加进的slave并不能为该slave被加入前就已存在的queue内容增加额外的冗余或可用性,直至master/slave完全同步。因为执行明确的同步操作会使queue无响应,因此只允许非活跃的queues进行明确的同步操作而活跃的queues进行自然的同步操作是一种好的策略。

配置明确的同步
明确的/显示的同步操作可以以两种方式触发:手动方式和自动方式。若queue被设置为自动同步则新salve一旦被加入同步便开始进行,queue变的无响应直至同步完成。可以通过将policy中的ha-sync-mode指定为automatic来将同步设置为自动的或者将其指定为manual或者不指定将同步设置为手动的。

可通过如下命令确认哪些salve在同步:
rabbitmqctl list_queues name slave_pids synchronised_slave_pids
手动同步queue:
rabbitmqctl sync_queue name
取消queue同步:
rabbitmqctl cancel_sync_queue name
(也可在management plugin中进行)

停止nodes与同步
当停掉运行有被镜像的queue的master node后,其他node上的slave将被提升为新的master(假定当时存在一个已完全同步的slave)。当继续停掉nodes后会达到没有多余的slave用来镜像queue的境地,queue此时只存在于一个node中,该node此时为master。若该queue被声明为持久化的,则在仅剩的node被停掉并重启后被声明为持久化的message会仍然存在。通常,这个时候再重启其余的nodes,若这些nodes之前是镜像queue的一部分,则会重新加入被镜像的queue。
然而,目前,在重新加入备镜像的queue时,salve没有办法知道自己的queue中的内容是否和master的queu中的内容存在偏差(这在 network partition时是可能存在的),因此,目前的策略是,当salve重新加入被镜像的queue时,丢掉本地所有的内容,以空的状态开始,就好比cluster中加入了新node一样。

停止只有未同步的salves的master nodes

在关掉master node时所有可用的salves都还未同步完的情况是可能的。为了避免message丢失,默认情况下,在有意的/可控的关闭master时(有意的停RabbitMQ服务或关闭OS)RabbitMQ不会fail over到还未同步完成的slave上,相反整个queue将被关闭就好像未同步完成的salve不存在一样。然而,若是无意的/不可控的关闭master node时(例如RabbitMQ server或者node crash了或者网络掉电了)将触发master fail over到slave node即使slave还未同步完成。

若希望在所有情形下(即使slave node还未同步完成)master node都能fail over到其他slave node,则可通过将policy中的ha-promote-on-shutdown由when-synced值为always来实现。

所有slaves处于关闭状态时的master丢失
可能发生这样一种情形,在queue的所有slaves均处于关闭状态时queue的master丢失了(可控/不可控的关闭)。正常情况下queue的nodes中最后一个被关闭的会变成master,在这个节点重启时我们仍希望其作为master(因为该node可能接收了其他node未接收到的新message)。然而,当调用rabbitmqctl forget_cluster_node命令从cluster中移除一个node时RabbitMQ会试图为master在该node上的每个queue找到一个当前被停掉的salve node并在这个slave node重新启动后将其“提升”为新的master。若有多个node可供选择,则最近被停掉的salve node将被选中。

这里需要理解为什么在执行forget_cluster_node命令时RabbitMQ只能选择已停止的slave来提升为master。之所以这样是因为就像“
停止nodes与同步”部分将的:任何slave node在重新启动后会清空本地所有的内容,因而只能选择还处于停止状态的salve。因此,当从cluster中移除“丢失的”master时需在slaves重启之前执行forget_cluster_node命令。

镜像队列的实现和语义

对于每个被镜像的queue存在一个master和多个slaves,各自在不同的node。slaves以完全相同的顺序执行发生在master上的操作因此可以维护与master一致的状态。publish操作仅发生在master上然后由master broadcasts到所有的slaves。因此从被镜像的queue consume message的 client实际上是从master consume的。

当一个slave fail后需要做的很少:master仍为master,所有的client不会被通知slave的fail,也无需其他额外的操作。注意:slave的失败可能不会被立即探测到,每个连接的流控机制的干预可以延迟message的发送。

当master fail时,其中一个slave必须被提升为master,此时会发生以下情况:
一个slave被提升为新的master。被提升的slave是“最旧”的slave。因为该slave与原master中内容完全同步的几率最大。然而,也有可能所有的salve都未与master完全同步,此时只有master中存在而slaves中不存在的message将丢失。
slave认为所有之前的consumers的连接突然断开了。因此,它重新将已经投递但还未被确认的messages重新排队。这些“未被确认的”message可能包含client已发出确认但确认在到达master前丢失了的情形也包含client已发出确认且确认已到达master但在master广播给slaves时丢失的情形。在上述任意一种情况下,新的master都必须为他认为未收到确认的message重新排队。
之前请求原master的clients被取消。
由于重新排队,从queue重新consume的clients需要知道此时可能接收到之前已经被处理过的message

在选则的salve变成master时,这段时间内发往原被镜像的queue的message不会丢失:发给被镜像的queue的message总是直接发给master和所有的slaves。因此,一旦master失败,messages将继续被发往slaves且一旦某个salve被完全提升为master这些message将被添加到queue中。

同样,clients发送的使用publisher confirms确认的message仍然会被正确的确认即使是在message被publish和被confirm之间master(或任何的slave)fail了。因此,从publisher的角度看,发message到mirrored queue与发message到任何其他类型的queue并无差别。

若是从mirrored queue使用noAck=true消费message,message就有可能丢失。RabbitMQ认为一旦message被发给了noAck=true的consumer即被视为确认过了,若这时client意外的断开了,message将永远不会被收到。在mirrored queue情况下,在master故障时queue中被发给noAck=True的consumer的message将永远不会被收到,也不会被新的master重新排队。因为正在consuming的client可能连接在存活的node上, Consumer Cancellation Notification机制可以用于检测是否发生了上述情况。当然,在实际应用场景中,若关心message的可靠性则建议使用noAck=false的consumer。

Publisher Confirms 和 Transactions
Mirrored queue支持Publisher Confirms 和 Transactions。在该情形下,confirms和transactions动作在queue的所有mirror中传播。如,只有transaction在queue的所有mirror中都应用后才回返回tx.commit-ok 。同样,只有queue的所有mirror都接收到message后才会给publisher以确认。

流量控制
RabbitMQ使用基于credit的算法来限制message的发送速率。Publishers只有在接收到queue的所有mirrors反馈的credit时才允许publish message。若salves不能反馈credit则会导致publisher阻塞。Publisher会一直被阻塞直至slaves反馈credit或者直至剩余的nodes认为该slave从cluster断开连接。Erlang通过定期发送tick至所有nodes来监测连接的断开。tick的频率可通过net_ticktime参数配置。

Consumer Cancellation
从mirrored queue消费message的clients可能想要知道它们从中消费message的queue是否发生了fail over。当mirrored queue发生了fail over后,关于messages已被发送到哪个consumer的信息将丢失,因此所有未确认的messages会被设置redelivered flag后重新发送。Consumers可能想要知道是否发生了这个过程。Consumer可以通过将x-cancel-on-ha-failover 设置为true来达到此目的。这样一来在failover发生时消费将取消, consumer cancellation notification会被发出。接下来由consumer决定是否重新

 

参考:

http://www.rabbitmq.com/ha.html

 

转载: https://blog.csdn.net/zyz511919766/article/details/41896823

 

posted @ 2019-01-02 17:51  我是一颗小菜  阅读(391)  评论(0编辑  收藏  举报