zoukankan      html  css  js  c++  java
  • RabbitMQ高可用-镜像模式部署使用

    RabbitMQ高可用-镜像模式部署使用

    一、概述

    RabbitMQ的Cluster集群模式一般分为两种,普通模式和镜像模式。消息队列通过rabbitmq HA镜像队列进行消息队列实体复制。
    RabbitMQ集群普通模式考虑到性能和存储空间,仅采用元数据同步的方式。即其他节点不会实际存储消息数据。
    生产者和消费者连接在哪个节点上,则消息就存储在哪个节点上,其他节点仅会存储元数据,如果消费者获取的数据不在当前节点上,则内部会路由到实际存储消息的节点上。
    我们在实际集群部署时,考虑到高可用性,一般都会使用镜像模式。
    
    在RabbitMQ集群中的节点只有两种类型:内存节点/磁盘节点,单节点系统只运行磁盘类型的节点。而在集群中,可以选择配置部分节点为内存节点。内存节点,就是将元数据(metadata)都放在内存里,磁盘节点就是放在磁盘上。如果RabbitMQ是单节点运行,默认就是磁盘节点。在RabbitMQ集群里,至少有一个磁盘节点,它用来持久保存元数据。新的节点加入集群后,会从磁盘节点上拷贝数据。但是,集群里也不必要每个节点都是磁盘节点,这主要是性能问题。例如,压力大的RPC服务,每秒都要创建和销毁数百个队列,如果集群里都是磁盘节点,意味着队列定义在每个节点上,都要写入磁盘才算成功,这样就会非常慢。
    
    如果集群里只有一个磁盘节点,这个节点挂了,会发生什么?此时消息路由机制仍可正常进行(可以正常投递和消费消息),但是不能做如下事:create queues,create exchanges,create bindings,add users,change permissions,add or remove cluster nodes
    所以,考虑到高可用性,推荐在集群里保持2个磁盘节点,这样一个挂了,另一个还可正常工作。但上述最后一点,往集群里增加或删除节点,要求2个磁盘节点同时在线。
    
    如果2个节点,则建议都设为磁盘节点,如果3个节点,则可2个磁盘节点+1个内存节点。
    
    RabbitMQ安装可参考上一篇博文:https://www.cnblogs.com/huligong1234/p/13548573.html
    
    
    部署方案:
    CentOS7.8 x64
    mq01:192.168.100
    mq02:192.168.101
    

    二、配置普通集群模式

    
    把mq01的cookie值复制到mq02服务器
    
    配置cookie
    vi /var/lib/rabbitmq/.erlang.cookie
    
    确保rabbitMQ服务处于停止状态:service rabbitmq-server stop
    
    确保2个节点的coolie文件使用相同的值
    cookie文件默认路径为/var/lib/rabbitmq/.erlang.cookie(RPM安装) 
    或者$home/.erlang.cookie(解压方式安装)
    
    .erlang.cookie设置可写
    chmod u+w /var/lib/rabbitmq/.erlang.cookie
    
    加入集群(默认加入的为磁盘节点)
    rabbitmqctl join_cluster rabbit@mq02
    
    如果要使用内存节点,则可以使用
    rabbitmqctl join_cluster --ram rabbit@mq02
    
    查看集群状态
    rabbitmqctl cluster_status
    

    三、配置镜像集群模式

    3.1.通过命令行配置

    
    rabbitmqctl set_policy [ha-all] "^" '{"ha-mode":"all"}' //策略正则表达式为 “^” 表示所有匹配所有队列名称
    rabbitmqctl set_policy -p [虚拟主机名称] [策略名称如ha-all ] "^" '{"ha-mode":"all" , "ha-sync-mode":"automatic"}'
    
    在任意一个节点上执行:
    rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all" , "ha-sync-mode":"automatic"}'
    
    或者指定vhost:
    rabbitmqctl set_policy -p demo ha-all "^" '{"ha-mode":"all" , "ha-sync-mode":"automatic"}'
    将所有队列设置为镜像队列,即队列会被复制到各个节点,各个节点状态保持一直。
    到这里,RabbitMQ 高可用集群就已经搭建好了,最后一个步骤就是搭建均衡器。
    
    策略名称:自定义
    “^”:匹配所有队列
    ha-sync-mode: 默认为手动,可以配置为自动,区别在于,如果是自动做镜像回复,则该队列会处于不可用状态直到同步完成
    

    3.2.通过管理界面配置

    界面配置示例(http://ip:15672/):

    配置完后:

    四、配置高可用

    可以采用HAProxy+Keepalived 或 Nginx+Keepalived方式来部署实现。

    我这里根据实际使用场采用另一种简洁方案,如下图所示,仅使用Nginx,配置backup路由策略,减轻部署复杂性。

    五、集群常用命令

    1、加入集群[--ram添加内存模式 默认disk模式]
    rabbitmqctl join_cluster --ram rabbit@mq01
    
    2、查看集群状态
    rabbitmqctl cluster_status
    
    3、更改节点模式[顺序 关闭运用-〉更改类型->开启运用]
    rabbitmqctl stop_app  –停止运用服务
    rabbitmqctl change_cluster_node_type disc/ram –更改节点为磁盘或内存节点
    rabbitmqctl start_app –开启运用服务
    
    4、创建策略(集群同步策略……)
    set_policy [-p vhostpath] {name} {pattern} {definition} [priority]
    
    5、查看策略
    rabbitmqctl list_policies
    
    6、移除远程offline的节点
    1.节点2停掉应用
    rabbitmqctl stop_app 
    2.节点1执行删除
    rabbitmqctl forget_cluster_node rabbit@mq02
    
    7、设置集群名称
    rabbitmqctl set_cluster_name cluster_name
    
    8、设置镜像模式
    Rabbit提供镜像功能,需要基于rabbitmq策略来实现,政策是用来控制和修改群集范围的某个vhost队列行为和Exchange行为 
    set_policy [-p vhostpath] {name} {pattern} {definition} [priority]
    rabbitmqctl set_policy ha-all "^ha." "{""ha-mode"":""all""}"
    rabbitmqctl set_policy ha-all "^" "{""ha-mode"":""all"",""ha-sync-mode"":""automatic""}"
    rabbitmqctl set_policy -p demo  ha-all "^" "{""ha-mode"":""all"",""ha-sync-mode"":""automatic""}"
    
    9、手动同步queue
    rabbitmqctl sync_queue name
    
    10、取消queue同步
    rabbitmqctl cancel_sync_queue name
    
    11、查看所有队列信息
    rabbitmqctl list_queues
    
    12、获取队列信息
    rabbitmqctl list_queues[-p vhostpath] [queueinfoitem ...]
    Queueinfoitem可以为:name,durable,auto_delete,arguments,messages_ready,messages_unacknowledged,messages,consumers,memory。
    
    13、获取Exchange信息
    rabbitmqctl list_exchanges[-p vhostpath] [exchangeinfoitem ...]
    Exchangeinfoitem有:name,type,durable,auto_delete,internal,arguments。
    
    14、获取Binding信息
    rabbitmqctl list_bindings[-p vhostpath] [bindinginfoitem ...] 
    Bindinginfoitem有:source_name,source_kind,destination_name,destination_kind,routing_key,arguments。
    
    15、获取Connection信息
    rabbitmqctl list_connections [connectioninfoitem ...]
    Connectioninfoitem有:recv_oct,recv_cnt,send_oct,send_cnt,send_pend等。
    
    16、获取Channel信息
    rabbitmqctl list_channels[channelinfoitem ...]
    Channelinfoitem有consumer_count,messages_unacknowledged,messages_uncommitted,acks_uncommitted,messages_unconfirmed,prefetch_count,client_flow_blocked。
    

    六、rabbitmq中镜像队列注意点

    RabbitMQ的mirror queue(镜像队列)机制是最简单的队列HA方案,它通过在cluster的基础上增加ha-mode、ha-param等policy选项,可以根据需求将cluster中的队列镜像到多个节点上,从而实现高可用,消除cluster模式中队列内容单点带来的风险。

    在使用镜像队列之前,有几点注意事项:
    1.镜像队列不能作为负载均衡使用,因为每个操作在所有节点都要做一遍。
    2.ha-mode参数和durable declare对exclusive队列都不生效,因为exclusive队列是连接独占的,当连接断开,队列自动删除。所以实际上这两个参数对exclusive队列没有意义。
    3.将新节点加入已存在的镜像队列时,默认情况下ha-sync-mode=manual,镜像队列中的消息不会主动同步到新节点,除非显式调用同步命令。当调用同步命令(via rabbitmqctl or web-based ui)后,队列开始阻塞,无法对其进行操作,直到同步完毕。当ha-sync-mode=automatic时,新加入节点时会默认同步已知的镜像队列。由于同步过程的限制,所以不建议在生产环境的active队列(有生产消费消息)中操作。
    4.每当一个节点加入或者重新加入(例如从网络分区中恢复回来)镜像队列,之前保存的队列内容会被清空。
    5.镜像队列有主从之分,一个主节点(master),0个或多个从节点(slave)。当master宕掉后,会在slave中选举新的master。选举算法为最早启动的节点。
    6.当所有slave都处在(与master)未同步状态时,并且ha-promote-on-shutdown policy设置为when-syned(默认)时,如果master因为主动的原因停掉,比如是通过rabbitmqctl stop命令停止或者优雅关闭OS,那么slave不会接管master,也就是说此时镜像队列不可用;但是如果master因为被动原因停掉,比如VM或者OS crash了,那么slave会接管master。这个配置项隐含的价值取向是优先保证消息可靠不丢失,放弃可用性。如果ha-promote-on-shutdown policy设置为alway,那么不论master因为何种原因停止,slave都会接管master,优先保证可用性。
    7.镜像队列中最后一个停止的节点会是master,启动顺序必须是master先起,如果slave先起,它会有30秒的等待时间,等待master启动,然后加入cluster。当所有节点因故(断电等)同时离线时,每个节点都认为自己不是最后一个停止的节点。要恢复镜像队列,可以尝试在30秒之内同时启动所有节点。
    8.对于镜像队列,客户端basic.publish操作会同步到所有节点;而其他操作则是通过master中转,再由master将操作作用于salve。比如一个basic.get操作,假如客户端与slave建立了TCP连接,首先是slave将basic.get请求发送至master,由master备好数据,返回至slave,投递给消费者。
    9.由8可知,当slave宕掉时,除了与slave相连的客户端连接全部断开之外,没有其他影响。当master宕掉时,会有以下连锁反应: 1)与master相连的客户端连接全部断开。 2)选举最老的slave为master。若此时所有slave处于未同步状态,则未同步部分消息丢失。 3)新的master节点requeue所有unack消息,因为这个新节点无法区分这些unack消息是否已经到达客户端,亦或是ack消息丢失在到老master的通路上,亦或是丢在老master组播ack消息到所有slave的通路上。所以处于消息可靠性的考虑,requeue所有unack的消息。此时客户端可能受到重复消息。 4)如果客户端连着slave,并且basic.consume消息时指定了x-cancel-on-ha-failover参数,那么客户端会收到一个Consumer Cancellation Notification通知,Java SDK中会回调Consumer接口的handleCancel()方法,故需覆盖此方法。如果不指定x-cancel-on-ha-failover参数,那么消费者就无法感知master宕机,会一直等待下去。 上面列出的注意事项整理自官方的HA文档。

    七、rabbitmq中镜像队列的故障恢复

    前提:两个节点(A和B)组成一个镜像队列。

    1.场景1:A先停,B后停。 该场景下B是master(disk,A是ram),只要先启动B,再启动A即可。或者先启动A,再在30秒之内启动B即可恢复镜像队列。
    2.场景2: A, B同时停。 该场景可能是由掉电等原因造成,只需在30秒之内连续启动A和B即可恢复镜像队列。
    3.场景3:A先停,B后停,且A无法恢复。 该场景是场景1的加强版,因为B是master,所以等B起来后,在B节点上调用rabbitmqctl forget_cluster_node A,解除与A的cluster关系,再将新的slave节点加入B即可重新恢复镜像队列。
    4.场景4:A先停,B后停,且B无法恢复。 该场景是场景3的加强版,比较难处理,早在3.1.x时代之前貌似都没什么好的解决方法,但是现在已经有解决方法了,在3.4.2版本亲测有效(我们当前使用的是3.3.5)。因为B是master,所以直接启动A是不行的,当A无法启动时,也就没办法在A节点上调用rabbitmqctl forget_cluster_node B了。新版本中,forget_cluster_node支持–offline参数,offline参数允许rabbitmqctl在离线节点上执行forget_cluster_node命令,迫使RabbitMQ在未启动的slave节点中选择一个作为master。当在A节点执行rabbitmqctl forget_cluster_node –offline B时,RabbitMQ会mock一个节点代表A,执行forget_cluster_node命令将B剔出cluster,然后A就能正常启动了。最后将新的slave节点加入A即可重新恢复镜像队列。
    5.场景5: A先停,B后停,且A、B均无法恢复,但是能得到A或B的磁盘文件。 该场景是场景4的加强版,更加难处理。将A或B的数据库文件(默认在$RABBIT_HOME/var/lib目录中)拷贝至新节点C的目录下,再将C的hostname改成A或B的hostname。如果拷过来的是A节点磁盘文件,按场景4处理方式;如果拷过来的是B节点磁盘文件,按场景3处理方式。最后将新的slave节点加入C即可重新恢复镜像队列。
    6.场景6:A先停,B后停,且A、B均无法恢复,且无法得到A或B的磁盘文件。 洗洗睡吧,该场景下已无法恢复A、B队列中的内容了。

    八、更多参考资料

  • 相关阅读:
    C语言面试题分类->宏定义
    c语言位运算
    C语言一个程序的存储空间
    收藏的链接-English
    侧滑关闭Activity的解决方案——SwipeBackLayout
    实现ViewPager的联动效果
    由Toolbar造成的ListView最后一项显示不全
    收藏的链接-Stub
    收藏的链接-Git
    收藏的链接
  • 原文地址:https://www.cnblogs.com/huligong1234/p/13549450.html
Copyright © 2011-2022 走看看