RabbitMQ集群会始终同步四种类型的内部元数据(相似索引):
a.队列元数据:队列名称和它的属性;
b.交换器元数据:交换器名称、类型和属性;
c.绑定元数据:一张简单的表格展现了如何将消息路由到队列;
d.vhost元数据:为vhost内的队列、交换器和绑定提供命名空间和安全属性;
所以,当用户访问其中任何一个RabbitMQ节点时,经过rabbitmqctl查询到的queue/user/exchange/vhost等信息都是相同的。html
我想确定有很多同窗会问,想要实现HA方案,那将RabbitMQ集群中的全部Queue的完整数据在全部节点上都保存一份不就能够了么?(能够相似MySQL的主主模式嘛)这样子,任何一个节点出现故障或者宕机不可用时,那么使用者的客户端只要能链接至其余节点可以照常完成消息的发布和订阅嘛。
我想RabbitMQ的做者这么设计主要仍是基于集群自己的性能和存储空间上来考虑。第一,存储空间,若是每一个集群节点都拥有全部Queue的彻底数据拷贝,那么每一个节点的存储空间会很是大,集群的消息积压能力会很是弱(没法经过集群节点的扩容提升消息积压能力);第二,性能,消息的发布者须要将消息复制到每个集群节点,对于持久化消息,网络和磁盘同步复制的开销都会明显增长。node
若是有一个消息生产者或者消息消费者经过amqp-client的客户端链接至节点1进行消息的发布或者订阅,那么此时的集群中的消息收发只与节点1相关,这个没有任何问题;若是客户端相连的是节点2或者节点3(队列1数据不在该节点上),那么状况又会是怎么样呢?数据库
若是消息生产者所链接的是节点2或者节点3,此时队列1的完整数据不在该两个节点上,那么在发送消息过程当中这两个节点主要起了一个路由转发做用,根据这两个节点上的元数据(也就是上文提到的:指向queue的owner node的指针)转发至节点1上,最终发送的消息仍是会存储至节点1的队列1上。
一样,若是消息消费者所链接的节点2或者节点3,那这两个节点也会做为路由节点起到转发做用,将会从节点1的队列1中拉取消息进行消费。安全
主机名解析bash
RabbitMQ节点使用域名相互寻址,所以全部集群成员的主机名必须可以从全部集群节点解析,能够修改hosts文件或者使用DNS解析cookie
若是要使用节点名称的完整主机名(RabbitMQ默认为短名称),而且可使用DNS解析完整的主机名,则可能须要调查设置环境变量 RABBITMQ_USE_LONGNAME = true网络
建立集群的方法用多种app
经过配置文件负载均衡
rabbitmqctl手动配置工具
经过插件(如:AWS(EC2)实例发现,Kubernetes发现,基于Consul的发现,基于etcd的发现)
一个集群的组成能够动态改变,全部的RabbitMQ开始做为单个节点运行,这些节点能够加入到集群,而后也能够再次脱离集群转回单节点
RabbitMQ集群能够容忍单个节点的故障。节点能够随意启动和通知,只要它们能够与在关闭时已知的集群成员节点联系
集群意味着在局域网使用,不建议运行跨广域网的集群
节点能够是disk节点或RAM节点
RAM节点将内部数据库表存储在RAM中。这不包括消息,消息存储索引,队列索引和其余节点状态
在90%以上的状况下,您但愿全部节点都是磁盘节点; RAM节点是一种特殊状况,可用于改善高排队,交换或绑定流失的性能集群。RAM节点不提供有意义的更高的消息速率。若有疑问,请仅使用磁盘节点。
因为RAM节点仅将内部数据库表存储在RAM中,所以它们必须在启动时从对等节点同步它们。这意味着群集必须至少包含一个磁盘节点。所以没法手动删除集群中剩余的最后一个磁盘节点
hostname | ip | system | RabbitMQ |
rabbit1 | 192.168.88.1 | CentOS7.2.1511 | 3.7.0 |
rabbit2 | 192.168.88.2 | ||
rabbit3 | 192.168.88.3 |
绑定hosts文件
192.168.88.1 rabbit1
192.168.88.2 rabbit2
192.168.88.3 rabbit3
在三台机器安装RabbitMQ
设置节点互相验证:Erlang Cookie
RabbitMQ节点和CLI工具(例如rabbitmqctl)使用cookie来肯定它们是否被容许相互通讯,要使两个节点可以通讯,它们必须具备相同的共享密钥,称为Erlang Cookie.
Cookie只是一个字符串,最多能够有255个字符。它一般存储在本地文件中。该文件必须只能由全部者访问(400权限)。每一个集群节点必须具备相同的 cookie,文件位置/var/lib/rabbitmq/.erlang.cookie, 把rabbit二、rabbit3设置成和rabbit2同样的便可,权限是400
正常方式启动全部节点
rabbitmq-server -detached rabbitmq-server -detached rabbitmq-server -detached
如今启动了三个独立的RabbitMQ,咱们用cluster_status命令查看集群状态
[root@rabbit1 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1]}]}, {running_nodes,[rabbit@rabbit1]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit1,[]}]}] [root@rabbit2 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit2]}, {cluster_name,<<"rabbit@rabbit2">>}, {partitions,[]}, {alarms,[{rabbit@rabbit2,[]}]}] [root@rabbit3 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit3 ... [{nodes,[{disc,[rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit3]}, {cluster_name,<<"rabbit@rabbit3">>}, {partitions,[]}, {alarms,[{rabbit@rabbit3,[]}]}]
为了链接集群中的三个节点,咱们把rabbit@c2和rabbit@c3节点加入到rabbit@c1节点集群
首先,在rabbit@c1的簇中加入rabbit@c2
一、中止rabbir@c2的rabbitmq应用程序,
二、加入rabbit@c1集群
三、而后启动RabbitMQ程序
注意:加入集群会隐式重置节点,从而删除此节点上之前存在的全部资源和数据
[root@rabbit2 ~]# rabbitmqctl stop_app Stopping rabbit application on node rabbit@rabbit2 ... [root@rabbit2 ~]# rabbitmqctl join_cluster rabbit@rabbit1 Clustering node rabbit@rabbit2 with rabbit@rabbit1 [root@rabbit2 ~]# rabbitmqctl start_app Starting node rabbit@rabbit2 ... completed with 0 plugins.
如今咱们在rabbit一、rabbit2任意一个节点上查看集群状态,咱们能够看到这两个节点加入了一个集群
[root@rabbit1 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit2,[]},{rabbit@rabbit1,[]}]}]
咱们再把rabbit3节点加入到这个集群
[root@rabbit3 ~]# rabbitmqctl stop_app Stopping rabbit application on node rabbit@rabbit3 ... [root@rabbit3 ~]# rabbitmqctl join_cluster rabbit@rabbit1 Clustering node rabbit@rabbit3 with rabbit@rabbit1 [root@rabbit3 ~]# rabbitmqctl start_app Starting node rabbit@rabbit3 ... completed with 0 plugins.
经过任何节点上的cluster_status命令,咱们能够看到这三个节点加入了一个集群
[root@rabbit1 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit3,[]},{rabbit@rabbit2,[]},{rabbit@rabbit1,[]}]}] [root@rabbit2 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit3,rabbit@rabbit1,rabbit@rabbit2]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit3,[]},{rabbit@rabbit1,[]},{rabbit@rabbit2,[]}]}] [root@rabbit3 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit3 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit1,[]},{rabbit@rabbit2,[]},{rabbit@rabbit3,[]}]}]
经过遵循上述步骤,咱们能够在集群正在运行的同时随时向集群添加新节点
已加入群集的节点可随时中止。他们也能够崩溃。在这两种状况下,群集的其他部分都会继续运行,而且节点在再次启动时会自动“跟上”(同步)其余群集节点。
咱们关闭rabbit@rabbit1和rabbit@rabbit3,并检查每一步中的集群状态
[root@rabbit1 ~]# rabbitmqctl stop Stopping and halting node rabbit@rabbit1 ... [root@rabbit2 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit3,rabbit@rabbit2]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit3,[]},{rabbit@rabbit2,[]}]}] [root@rabbit3 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit3 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit3]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit2,[]},{rabbit@rabbit3,[]}]}] [root@rabbit3 ~]# rabbitmqctl stop Stopping and halting node rabbit@rabbit3 ... [root@rabbit2 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit2]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit2,[]}]}]
如今咱们再次启动节点,在咱们继续检查集群状态时
[root@rabbit3 ~]# rabbitmq-server -detached [root@rabbit3 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit3 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit3]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit2,[]},{rabbit@rabbit3,[]}]}] [root@rabbit2 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit3,rabbit@rabbit2]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit3,[]},{rabbit@rabbit2,[]}]}] [root@rabbit1 ~]# rabbitmq-server -detached [root@rabbit1 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit3,rabbit@rabbit1]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit2,[]},{rabbit@rabbit3,[]},{rabbit@rabbit1,[]}]}] [root@rabbit2 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit1,rabbit@rabbit3,rabbit@rabbit2]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit1,[]},{rabbit@rabbit3,[]},{rabbit@rabbit2,[]}]}] [root@rabbit3 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit3 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit1,[]},{rabbit@rabbit2,[]},{rabbit@rabbit3,[]}]}]
一些重要的警告:
当整个集群关闭时,最后一个关闭的节点必须是第一个要联机的节点。
若是要脱机的最后一个节点没法恢复,可使用forget_cluster_node命令将其从群集中删除
若是全部集群节点同时中止而且不受控制(例如断电),则可能会留下全部节点都认为其余节点在其后中止的状况。在这种状况下,您能够在一个节点上使用force_boot命令使其再次可引导
当节点再也不是节点的一部分时,须要从集群中明确地删除节点。咱们首先从集群中删除rabbit@rabbit3,并将其返回到独立操做
在rabbit@rabbit3上:
一、咱们中止RabbitMQ应用程序,
二、重置节点
三、从新启动RabbitMQ应用程序
[root@rabbit3 ~]# rabbitmqctl stop_app Stopping rabbit application on node rabbit@rabbit3 ... [root@rabbit3 ~]# rabbitmqctl reset Resetting node rabbit@rabbit3 ... [root@rabbit3 ~]# rabbitmqctl start_app Starting node rabbit@rabbit3 ... completed with 0 plugins.
在节点上 运行cluster_status命令确认rabbit@rabbit3如今再也不是集群的一部分并独立运行
[root@rabbit3 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit3 ... [{nodes,[{disc,[rabbit@rabbit3]}]}, {running_nodes,[rabbit@rabbit3]}, {cluster_name,<<"rabbit@rabbit3">>}, {partitions,[]}, {alarms,[{rabbit@rabbit3,[]}]}] [root@rabbit1 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit2,[]},{rabbit@rabbit1,[]}]}] [root@rabbit2 ~]# rabbitmqctl cluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}, {cluster_name,<<"rabbit@rabbit1">>}, {partitions,[]}, {alarms,[{rabbit@rabbit1,[]},{rabbit@rabbit2,[]}]}]
咱们也能够远程删除节点,例如,在处理无响应的节点时,这颇有用
好比:咱们在节点rabbit@rabbit2上把rabbit@rabbit1从集群中移除
[root@rabbit1 ~]# rabbitmqctl stop_app Stopping rabbit application on node rabbit@rabbit1 ... [root@rabbit2 ~]# rabbitmqctl forget_cluster_node rabbit@rabbit1 Removing node rabbit@rabbit1 from the cluster
请注意,rabbit1仍然认为它与rabbit2集群 ,并试图启动它将致使错误。咱们须要从新设置才能从新启动。
[root@rabbit1 ~]# rabbitmqctl reset #必需要重置 Resetting node rabbit@rabbit1 ... [root@rabbit1 ~]# rabbitmqctl start_app Starting node rabbit@rabbit1 ... completed with 0 plugins.
如今查看集群状态,三个节点都时做为独立的节点
请注意,rabbit@rabbit2保留了簇的剩余状态,而rabbit@rabbit1 和rabbit@rabbit3是刚刚初始化的RabbitMQ。若是咱们想从新初始化rabbit@rabbit2,咱们按照与其余节点相同的步骤进行:
[root@rabbit2 ~]# rabbitmqctl stop_app Stopping rabbit application on node rabbit@rabbit2 ... [root@rabbit2 ~]# rabbitmqctl reset Resetting node rabbit@rabbit2 ... [root@rabbit2 ~]# rabbitmqctl start_app Starting node rabbit@rabbit2 ... completed with 0 plugins.
RabbitMQ节点使用主机名相互通讯。所以,全部节点名称必须可以解析全部集群对等的名称。像rabbitmqctl这样的工具也是如此
除此以外,默认状况下RabbitMQ使用系统的当前主机名来命名数据库目录。若是主机名更改,则会建立一个新的空数据库。为了不数据丢失,创建一个固定和可解析的主机名相当重要。每当主机名更改时,您应该从新启动RabbitMQ
若是要使用节点名称的完整主机名(RabbitMQ默认为短名称),而且可使用DNS解析完整的主机名,则可能须要调查设置环境变量 RABBITMQ_USE_LONGNAME = true
客户端能够正常链接到群集中的任何节点。若是该节点出现故障,而且集群的其他部分仍然存在,那么客户端应该注意到已关闭的链接,而且应该可以从新链接到群集的一些幸存的成员。一般,将节点主机名或IP地址烧入客户端应用程序是不可取的:这会引入不灵活性,而且若是集群配置发生更改或集群中节点数发生更改,则须要编辑,从新编译和从新部署客户端应用程序。相反,咱们推荐一个更抽象的方法:这多是一个动态的DNS服务,它具备很是短的TTL配置,或者一个普通的TCP负载均衡器,或者用起搏器或相似技术实现的某种移动IP。通常来讲
RAM节点只将其元数据保存在内存中。因为RAM节点没必要像光盘节点那样写入光盘,它们能够更好地执行。可是请注意,因为永久队列数据老是存储在磁盘上,所以性能改进将仅影响资源管理(例如添加/删除队列,交换或虚拟主机),但不会影响发布速度或消耗速度
RAM节点是高级用例; 设置你的第一个群集时,你应该不使用它们。您应该有足够的光盘节点来处理您的冗余要求,而后在须要时添加额外的RAM节点进行缩放
只包含RAM节点的集群是脆弱的; 若是群集中止,您将没法再次启动, 并将丢失全部数据。RabbitMQ将阻止在许多状况下建立RAM节点的群集,可是它不能彻底阻止它
这里的例子仅仅为了简单起见,显示了具备一个光盘和一个RAM节点的集群; 这样的集群是一个糟糕的设计选择
建立RAM节点
咱们能够在首次加入集群时将节点声明为RAM节点。像以前同样,咱们使用rabbitmqctl join_cluster来完成此 操做,但传递 --ram标志
rabbit2$ rabbitmqctl stop_app Stopping node rabbit@rabbit2 ...done . rabbit2$ rabbitmqctl join_cluster --ram rabbit@rabbit1 Clustering node rabbit@rabbit2 with [rabbit@rabbit1] ...done. rabbit2$ rabbitmqctl start_app Starting node rabbit@rabbit2 ...done. RAM节点在集群状态中显示为: rabbit1$ rabbitmqctl cluster_status Cluster status of node rabbit@rabbit1 ... [{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}] ...done. rabbit2$ rabbitmqctl cluster_status Cluster status of node rabbit@rabbit2 ... [{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2]}]}, {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}] ...done.
更改节点类型
咱们能够将节点的类型从ram更改成disc,反之亦然。假设咱们想要颠倒rabbit @ rabbit2和rabbit @ rabbit1的类型 ,将前者从ram节点转换为disc节点,将后者从disc节点转换为ram节点。要作到这一点,咱们可使用 change_cluster_node_type命令。该节点必须先中止
rabbit2$ rabbitmqctl stop_app Stopping node rabbit@rabbit2 ...done. rabbit2$ rabbitmqctl change_cluster_node_type disc Turning rabbit@rabbit2 into a disc node ... ...done. Starting node rabbit@rabbit2 ...done. rabbit1$ rabbitmqctl stop_app Stopping node rabbit@rabbit1 ...done. rabbit1$ rabbitmqctl change_cluster_node_type ram Turning rabbit@rabbit1 into a ram node ... rabbit1$ rabbitmqctl start_app Starting node rabbit@rabbit1 ...done.