当一个集群发生网络分区时,集群会分为两个部分或者更多,它们各自为政,互相都认为对方分区内的节点已经挂了,包括队列、交换器及绑定等元数据的创建和销毁都处于自身分区内,与其他分区无关。分区的引入是为了配合RabbitMQ的数据一致性复制原理。一般情况下,网络分区都是由于单个节点的故障引起的,且通常会形成一个大分区和一个单节点分区,如果配置了镜像,那么可以在不影响服务可用性的情况下从网络分区的情形下得以恢复。
RabbitMQ集群内两两节点之间会有信息交互,如果某节点出现网络故障或是端口不同,则会使与此节点的交互出现中断,经过超时判定后,会判定网络分区。网络分区的判断与net_ticktime参数息息相关,其默认值为60。集群内部每个节点间会每隔四分之一net_ticktime记一次应答,如果任何数据被写入节点中,则此节点会被认为已经被应答了,如果连续4此没有应答,则会判定此节点已下线,其余节点可以将此节点剥离出当前分区。
网络分区的一些细节会存储在Mnesia数据库中,当Mnesia认定发生了网络分区,则会被记录在RabbitMQ服务日志中。除了服务日志外还有以下三种方法可以判定是否出现了网络分区:
对于未配置镜像的集群,网络分区发生之后,队列也会随着宿主节点而分散在各自的分区中。对于消息发送方而言,可以成功发送消息,但是会有路由失败的情况,需要配合mandatory等机制保障消息的可靠性,对于消息消费方而言,可能会出现不可预知的现象,比如已消费消息ack会失效。网络分区发生后,客户端与某分区重新建立通信链路,其分区中如果没有相应的队列进程,则会有异常报出。如果从网络分区中恢复,数据不会丢失,但是客户端会重复消费
网络分区的发生会引起消息的丢失,解决办法为消息发送端需要具备Basic.Return的能力,其次在检测到网络分区之后,需要迅速地挂起所有生产者进程,之后连接分区中的每个节点消费分区中所有的队列数据,在消费完之后再处理网络分区,最后从网络分区中恢复之后再恢复生产者进程。整个过程可以最大程度上保证网络分区之后的消息的可靠性。需要注意的是整个过程中会有大量的消息重复,消费客户端需要做好相应的幂等性处理。也可以将所有旧集群资源迁移到新集群来解决这个问题。
为了从网络分区中恢复,需要挑选一个信任分区,该分区具有决定Mnesia内容的权限,发生在其它分区的改变将不会被记录到Mnesia中而被丢弃。挑选完后重启非信任分区中的节点,如果还有网络分区的告警,紧接着重启信任分区中的节点。这个有三个要点需要注意:
RabbitMQ提供了三种自动处理网络分区的方法:pause-minority模式,pause-if-all-down模式和autoheal模式,默认是ignore,即不自动处理网络分区,其修改对应config文件中的cluster_partition_handling属性
在此模式下,当发生网络分区时,集群中的节点在观察到某些节点下线时,会自动检测其是否属于少数派(分区中的节点小于等于集群中一般的节点数),RabbitMQ会自动关闭这些节点的运作,以满足可用性,当分区结束时又会启动。处于关闭的节点会每秒检测一次是否可以连通到剩余集群中,如果可以则启动自身的应用。
RabbitMQ也会关闭不是严格意义上的大多数,如果集群中只有两个节点,其中任一节点关闭,另一个节点都会被关闭,所以pause-minority模式适用于集群节点数大于2个的时候。但如果出现2v2、3v3类的对等节点数的分区情况,所有节点上的RabbitMQ应用都会被关闭
该模式下,RabbitMQ集群中的节点在所配置的列表中的任何节点都不能交互时则会关闭。该模式下有两种配置,ignore和autoheal,当出现对等节点数分区的情况,ignore不会自动处理,autoheal则可以处理这种情形
在该模式下,RabbitMQ会自动决定一个获胜的分区,然后重启不在这个分区中的节点来从网络分区中恢复。获胜分区是指客户端连接最多的分区,如果连接客户端相同则选择节点数较多的一个分区,如果节点数相同,则以节点名称的字典顺序来判断。该模式在判定出节点下线时不做动作,要等到网络分区恢复时,在判定出网络分区之后才会有相应的动作,即重启非获胜分区中的节点。
需要注意,在此模式下,如果集群中有节点处于非运行状态,那么当发生网络分区时,将不会有任何自动处理的动作
ignore:网络分区发生时,不做任何动作,需要人工处理;
pause-minority:对等分区的处理不够优雅,一般用于非跨机架、奇数节点数的集群中;
pause-if-all-down模式:对于受信任节点的选择较为考究
autoheal:可以应对各个情形下的网络分区,但是如果集群中有节点处于非运行状态,则此模式会失效
原文:https://www.cnblogs.com/Jscroop/p/14382790.html