RabbitMQ high availability

RabbitMQ 高可用方案

  1. Rabbit集群方案一: 普通模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    普通模式:
    exchange,buindling在所有的节点上都会保存一份,但queue只会存储在其中的一个节点上,同时所有的节点还都会存储一份queue的meta信息.
    因为这样有两个好处:
    1.存储空间
    如果每一个节点上都有全部的消息,有多少个节点就会有多少个消息总量的"拷贝".
    假如一个队列的消息占用的空间是1G,那么三个节点就是3G
    2.性能
    * 消息需要在节点之间传输会有很大的网络开销
    * 如果消息设置了durable(即,持久化),还会增加很大的磁盘负载

    队列存储的节点 取决于 创建队列的客户端当时所连接的节点.如果生产者连接的是另外一个节点,将会把消息转发到存储该队列的节点上.如果消费者连接了非存储队列的节点取数据,消费者从存储消息的节点拉去数据.
    所以:
    1.创建队列都连到了一个节点上,所有的队列都存储在一个节点上
    2.存消息的节点挂掉了,consumer只能等到节点恢复后才能读到消息
    3.设A,B节点,queue数据在A上,可以向A或B生产或消费消息.一旦往B生产消息时A挂了,client不会收到任何错误信息并继续发送,而实际上消息是被丢弃了.而此时如果client也挂了,再连接B时会报节点A不存在而失败.在B读也是类似的,在client批量取到的数据读完之前是不会知道A有没有挂掉,等到读取下一批数据时一旦A挂掉会报错
    所以这种集群方法的特点是:高吞吐量,非高可用
  2. Rabbit集群方案一: 镜像模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    镜像模式:

    镜像模式和普通模式的区别:
    队列的数据都镜像了一份到所有的节点上.这样任何一个节点失效,不会影响整个集群的使用

    在实现上,镜像队列内部有一套选举算法,会选出一个master和若干个slaver.master和slaver通过相互间不断发送心跳来检查是否连接断开.可以通过指定net_ticktime来控制心跳检查频率.注意一个单位时间net_ticktime实际上做了4次交互,故当超过net_ticktime(± 25%)秒没有响应的话则认为节点挂掉.另外注意修改net_ticktime时需要所有节点都一致:
    配置举例:
    [
    {rabbit, [{tcp_listeners, [5672]}]},
    {kernel, [{net_ticktime, 120}]}
    ].

    consumer,任意连接一个节点,若连上的不是master,请求会转发给master,为了保证消息的可靠性,consumer回复ack给master后,master删除消息并广播所有的slaver去删除.

    publisher,任意连接一个节点,若连上的不是master,则转发给master,由master存储并转发给其他的slaver存储.如果master挂掉,则从slaver中选择消息队列最长的为master,在这种情况下可以存在消息未同步,给ack消息未同步的情况,会造成消息重发(默认是异步同步的).
    总共有以下几件事情发生:
    1.1个最老的(队列最长的)的slaver提升为master,如果没有一个slaver是和master同步的则会造成消息丢失
    2.要提升为master的slaver会认为以前所有连接挂掉的master的消费者都断开了连接.那么存在clinet发送了ack的消息但还在路上是master挂掉的情况,或者master收到了ack但是在广播给slaver的时候master挂掉的情况,所以新的master别无选择,只能认为消息没有被确认.他会requeue他认为没有ack的消息.那么client可能就收到了重复的消息,并要再次发送ack
    3.从镜像队列中消费的client支持了consumer Cancellation通知的,将收到通知并订阅的mirrored-queue被取消了,这是因为该mirrored-queue升级成了master,这是client需要重现去找mirrored-queue上消费,这样就避免了client继续发送ack到老的挂掉的master上.避免收到新的master发送的相同的消息.
    4.如果noAck=true,且在mirrored-queue上消费,那么在切换时由于服务器是先ack然后发送到noAck=true的消费者,这时连接断开可能导致该数据丢失

    如果slaver挂掉,则集群的节点状态没有任何变化.只要client没有连到这个节点上,也不会给client发送失败的通知.在检测到slaver挂掉的期间publish消息会有延迟.如果配置了高可用策略是自动同步,当slaver起来后,队列中有大量的消息需要同步,将会整个集群阻塞长时间的不能读写直到同步结束.

    这两个挂掉的情况都需要客户端镜像容错,比如在连接断开的时候进行重连(官方的Java和.net 客户端提供了callback方法在监听到链接失败的时候调用.Java在Connection和channel类中提供了ShutdownListener 的callback方法,.net client在IConnecton中提供了ConnectionShuedown在Imodel中提供了ImodelShutdown事件供调用).也可以在client和server之间加入LoadBalancer.比如haproxy做负载均衡

    指定mirror策略:
    有三种策略:
    * all
    队列将mirrored到所有集群中的节点中,当新节点添加进来时也会mirrored到新的节点
    * exactly(需指定count)
    如果节点数小于count数,则队列将mirrored到所有的节点.如果节点数大于count,新的节点将不再创建队列的mirror(即使原来已创建mirror的节点挂掉也不会创建)
    * nodes
    对指定的节点进行mirror.如果没有一个指定的节点在运行中,那么只有client连接的那个节点才会声明queue(这里有个迁移策略:假如queue是在[A,B]上且A为master,若给定的新的策略为nodes[C,D],那么为了防止数据丢失,在迁移中会同时存在[A,C,D]直到C,D已经同步好以后,A才会关闭)

    配置举例:
    设置queue的名称为ha.的为高可用:
    linux:rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all"}'
    win: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:
    1:Navigate to Admin > Policies > Add / update a policy.
    2:Enter "ha-all" next to Name, "^ha\." next to Pattern, and "ha-mode" = "all" in the first line next to Policy.
    3:Click Add policy.

    举例2:
    rabbitmqctl set_policy ha-two "^two\." \
    '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'

    自动或手动同步:
    你可以查看哪些slave已经同步好了:
    rabbitmqctl list_queues name slave_pids synchronised_slave_pids

    你可以手动同步(默认手动同步):
    rabbitmqctl sync_queue name

    你可以取消自动同步:
    rabbitmqctl cancel_sync_queue name
    一个没有同步的mirror,它仍然会同步后续插入队列的数据,但是队列前面的数据却没有。但是随着队列的不断消费,导致空缺的部分的消息被消费掉了,此时mirror也可以是同步了的。
  3. Rabbit集群方案一: 主备模式

    1
    2
    主备模式:
    主备方式(active,passive)只有一个节点处于服务状态,可以结合pacemaker和ARBD,shovel简单从一个broker的一个队列中消费消息,且转发该消息到另一个broker的交换机.