RabbitMQ集群
RabbitMQ集群允许消费者和生产者在RabbitMQ单个节点崩溃的情况下继续运行,他可以通过添加更多的节点线性的扩展消息通信的吞吐量。当失去一个RabbitMQ节点时,客户端能够重新到集群中的任何其他其他节点并继续生产和消费。
不过RabbitMQ集群不能保证消息的万无一失,即将消息、队列、交换机等都设置为可持久化,生产者和消费者都正确地使用了确认方式。当集群中一个RabbitMQ节点崩溃时,该节点上的所有队列中的消息也会失去,RabbitMQ集群所有节点都会备份所有的元数据信息,包括以下内容:
- 队列元数据:队列的名称及属性
- 交换机:交换机的名称及属性
- 绑定关系元数据:交换器及队列或者交换机与交换机之间的绑定关系
- vhost元数据:为vhost内的队列、交换器和绑定提供命名空间及安全属性
但是不会备份消息(当然通过特殊的配置比如镜像队列可以解决这个问题)。基于存储空间和性能的考虑,在RabbitMQ集群中创建队列,集群只会在单个节点而不是在所有节点上创建队列的进程并包含完整的队列信息(元数据、状态、内容)。这样只有队列的宿主节点知道所以信息,所以其他非所有者节点只知道队列的元数据和指向该队列存在的那个节点的指针。因此当集群节点崩溃时,该节点的队列进程和关联的绑定都会消失。附加在队列上的消费者也会丢失所订阅的信息。
不同于队列那样拥有自己的进程,交换机其实只是一个名称和绑定列表。当消息发布到交换器时,实际是由所连接的信道将消息上的路由键同交换器的绑定列表进行比较,然后路由消息。当创建一个新的交换机时,RabbitMQ所要做的就是将绑定列表添加到集群中的所有节点上。
多机多节点配置
RabbitMQ集群对延迟非常敏感,应当只在本地局域网内使用,在广域网不应该使用集群。
- 集群准备:
- 添加主机名与ip的映射关系
- 时间同步
编辑RabbitMQ的cookie文件,已确认各个节点的cookie文件使用的是同一个值,可以将一个节点的cookie的值,然后将其复制到node2和node3节点中。cookie文件默认路径为/var/lib/rabbitmq/.erlang.cookie,cookie相当于密令令牌,集群中的RabbitMQ节点需要通过交换密令获得相互认证。
配置集群,启动各个节点的rabbitmq服务,通过
rabbitmqctl cluster_status
可以得知现在节点都还是单独运行的,并没有组成集群,接下来组成集群:
如存在node1,node2,node3节点的rabbitmq,将node2和node3加入node1的集群中,1
node2 ~]# rabbitmqctl stop_app
2
node2 ~]# rabbitmqctl reset
3
node2 ~]# rabbitmqctl join_cluster rabbit@node1
4
node2 ~]# rabbitmqctl start_app
5
6
node3 ~]# rabbitmqctl stop_app
7
node3 ~]# rabbitmqctl reset
8
node3 ~]# rabbitmqctl join_cluster rabbit@node1
9
node3 ~]# rabbitmqctl start_app
如果关闭集群中的所有节点,则需要确保启动的时候最后关闭的那个节点是第一个启动的,如果第一个启动的不是最后关闭的节点,那么这个节点会等待最后关闭的节点启动,这个等待时间是30秒。
如果最后一个关闭的节点最终由于某些异常而无法启动,则可以通过rabbitmqctl forget_cluster_node命令来剔除当前集群。
如果集群中的所有节点由于某些非正常因素,比如断电而关闭,那么集群中的都不会认为还是其他节点在它后面关闭,此时需要调用rabbitmqctl force_boot
命令来启动一个节点,之后集群才可以正常启动。
集群节点类型
RabbitMQ中的每一个节点,不管是单一节点系统或者是集群中的一部分,要么是内存节点,要么是磁盘节点。内存节点将所有的队列、交换机、绑定关系、用户、权限和vhost的元数据定义都存储在内存中,而磁盘节点则将这些信息存储在磁盘中。
加入内存节点:
1 | node2 ~]# rabbitmqctl join_cluster rabbit@node1 --ram |
如果集群已经搭好,更改节点类型:
1 | node2 ~]# rabbitmqctl stop_app |
2 | node2 ~]# rabbitmqctl change_cluster_node_type ram |
3 | node2 ~]# rabbitmqctl start_app |
内存节点可以提供出色的性能,磁盘节点能够保证集群配置信息的高可靠性,如何在这两者之间进行抉择?
RabbitMQ集群只是要求至少一个磁盘节点,所有节点都可以是内存节点。当节点加入或者离开集群时,它们必须将变更通知到至少一个磁盘节点,如果只要一个磁盘节点,刚好宕机,那么集群仍可以发送和接受消息,但是无法执行创建队列、交换器、绑定关系、用户,以及更改权限、添加或删除集群节点等操作了,所以唯一磁盘节点崩溃,集群仍然可以保持运行,但是直到该节点恢复到集群前,你无法更改任何东西。所以在建立集群的时候应该保证有两个或者多个磁盘节点的存在。
在内存节点重启后,它们会连接到预先配置的磁盘节点,下载当前集群元数据的副本,当在集群中添加内存节点时,确保告诉所以磁盘节点,只要内存节点可以找到至少一个磁盘节点,那么它就能在重启后加入集群中。
剔除集群节点
如何将一个节点从集群中剔除,或者需要换一台性能更加好的节点。
以node1,node2,node3集群为例,将node2从集群中剔除。
第一种方法:
首先在node2节点上执行rabbitmqctl stop_app或者rabbitmqctl stop命令来关闭RabbitMQ服务,之后在node1或者node3节点上执行rabbitmqctl forget_cluster_node rabbit@node2命令将node2节点剔除出去。
1 | node1 ~]# rabbitmqctl stop_app |
2 | node1 ~]# rabbitmqctl forget_cluster_node rabbit@node2 |
关闭集群中的每个节点之后,如果最后一个关闭的节点最终由于某些异常而无法启动,则可以将该节点剔除集群,如:
1 | node3 ~]# rabbitmqctl stop |
2 | node2 ~]# rabbitmqctl stop |
3 | node1 ~]# rabbitmqctl stop |
4 | |
5 | #将最后关闭异常的节点剔除集群 |
6 | node2 ~]# rabbitmqctl forget_cluster_node rabbit@node1 -offline |
7 | node2 ~]# rabbitmq-server -detached |
8 | node3 ~]# rabbitmq-server -datached |
第二种方法:
在node2上执行rabbitmqctl reset,如果不需要由于启动顺序缘故而不得不删除一个集群节点。
1 | node2 ~]# rabbitmqctl stop_app |
2 | node2 ~]# rabbitmqctl reset |
3 | node2 ~]# rabbitmqctl start_app |
rabbitmqctl reset命令将清空节点的状态,并将其恢复到空白状态。当重设的节点是集群中的一部分时,该命令也会和集群中的磁盘节点进行通信,告诉它们该节点正在离开集群。
集群节点升级
如果RabbitMQ集群只是由单独一个节点组成,那么升级版本很容易,只需要关闭原来的服务,然后解压新的版本再运行即可。不过要确保原来的Mnesia中的数据不被变更,且新节点中的Mnesia路径的指向要与原节点中的相同,或者新版本的Mnesia路径指向保留的Mnesia数据的路径,最后启动新版本的服务即可。
如果RabbitMQ集群由多个节点组成,那么也可以参照单个节点的情形:
- 关闭所有节点的服务,注意采用rabbitmqctl stop命令关闭
- 保存各个节点的Mnesia数据
- 解压新版本的RabbitMQ到指定的目录
- 指定新版本的Mnesia路径为步骤2保存的Mnesia数据路径
- 启动新版本的服务,注意先启动原版本中最后的那个节点
步骤4和步骤5,可以参考export RABBITMQ_MNESIA_BASE=/var/lib/rabbitmq/mnesia && rabbitmq-server -detached
,其中/var/lib/rabbitmq/mnesia为原版本保存Mnesia数据的路径。
查看服务日志
RabbitMQ的日志默认存放在$RABBITMQ_HOME/var/log/rabbitmq文件夹。在这个文件夹内RabbitMQ会创建两个日志文件: RABBITMQ_NODENAME-sasl.log和RABBITMQ_NODENAME.log
- RABBITMQ_NODENAME-sasl.log: 记录Erlang相关信息时,可以在这个文件中找到Erlang的崩溃报告。
- RABBITMQ_NODENAME.log: 查看RabbitMQ应用服务日志。