RabbitMQ源码解析前奏--集群

一、集群简介

      一个RabbitMQ broker是一个或者几个Erlang节点的一个逻辑分组,每个节点运行着RabbitMQ应用和共享用户、虚拟主机、队列、交换机等等。有时候我们把这些节点集合作为集群。

      为了做到可靠、可扩展和ACID属性,所有数据或者状态都需要RabbitMQ broker复制到所有节点。对此,一个例外是消息队列,默认驻留在创建它们的节点上,虽然它们对于所有节点都是可访问和可见的。通过节点在集群内复制队列,可以参见这里(需要注意的是首先必须是集群)。

      RabbitMQ不能很好的处理分区容忍,所以它不能用于WAN。shovel或者federation插件很能很好的解决这个问题。

    集群的配置能够被动态地改变。所有RabbitMQ broker在一个单独的节点上开始运行,这些节点能够被加入到集群中,随后又能转换成单独的broker。

      RabbitMQ broker容忍个别节点的失败。节点能够被随意的启动和停止。

一个节点可以是磁盘节点或者内存节点(注意:diskdisc在使用上是可交换的,配置语法或者状态消息通常使用disc)。内存节点保持它们的状态仅仅在内存中(队列内容除外,如果队列持久化或者太大不能放在内存中则驻留在磁盘上)。磁盘节点保持状态在内存中和磁盘上。内存节点不会像磁盘节点一样写入到磁盘,因此性能更佳。然而,需要注意的是队列数据一直存储在磁盘上,性能的提升仅仅影响到资源管理(例如:添加/删除队列、交换机、或者虚拟机),而不是发布和消费速度。因为状态复制到集群内所有节点,所以集群内可以只有一个磁盘节点(但是不推荐)用于存储集群状态。

 

二、集群副本

接下来是一个设置和操作RabbitMQ集群的副本,其通过三台机器构成一个集群,分别是rabbit1、rabbit2、rabbit3,其中两台机器复制数据到内存和磁盘,另外一台仅仅复制数据到内存中。

我们假设用户登录到所有的三台机器上,RabbitMQ已经安装到机器上了,rabbitmq-server和rabbitmqctl脚本已经在用户PATH里了。

1、初始化设置

Erlang节点使用cookie来检测它们之间是否允许通信,如果两个节点能够通信,那么它们必须拥有相同的cookie。

Cookie仅仅是一个含有文字与数字的字符串,如果你喜欢其可以是long或者short。

RabbitMQ服务器启动的时候,Erlang将自动创建随机cookie文件。这个文件一般位于/var/lib/rabbitmq/.erlang.cookie。最简单的方式是允许其中一个节点创建cookie文件,然后将其拷贝到集群内所有其它节点。

作为一种选择,你可以在调用rabbitmq-server和rabbitmqctl脚本时插入选项“-setcookie cookie”。

2、启动独立节点

      用正常的方式在所有节点上启动RabbitMQ,具体如下:

rabbit1$ rabbitmq-server -detached

rabbit2$ rabbitmq-server -detached

rabbit3$ rabbitmq-server -detached

      如此,则创建了三个独立的RabbitMQ broker,每个节点上一个,可以通过如下命令查看集群状态:

rabbitmqctl cluster_statusCluster status of node rabbit@rabbit1 ...
...done.
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit2 ...
...done.
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit3 ...
...done.

3、创建集群

      为了将三个节点连入集群中,我们将其中两个节点加入到第三个节点的集群中,比如:将rabbit@rabbit2和rabbit@rabbit3加入到rabbit@rabbit1。

      我们首先将rabbit@rabbit2作为内存节点加入到rabbit@rabbit1的集群中,如下操作即可达到目的:

rabbitmqctl stop_appStopping node rabbit@rabbit2 ...done.
rabbitmqctl join_cluster --ram rabbit@rabbit1Clustering node rabbit@rabbit2 with [rabbit@rabbit1] ...done.
rabbitmqctl start_appStarting node rabbit@rabbit2 ...done.

      需要注意的是加入一个集群会隐式重启该节点,因此节点上之前存在的资源和数据都会被删除。

      在任意节点上运行rabbitmqctl cluster_status都会看到这两个节点已经一个集群中了,具体如下:

rabbitmqctl cluster_statusCluster status of node rabbit@rabbit1 ...
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
rabbit2$ 
[{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2]}]},
...done.

      现在我们可以将rabbit@rabbit3以磁盘节点模式加入相同的集群。加入步骤跟上面类似,但不同的是要删除—ram标志,因为默认就是磁盘节点模式。实际上,在将节点加入集群时,可以选择集群内任何在线节点,而不一定是rabbit@rabbit1,这里选择的就是rabbit@rabbit2,具体请看如下:

rabbitmqctl stop_appStopping node rabbit@rabbit3 ...done.
rabbitmqctl join_cluster rabbit@rabbit2Clustering node rabbit@rabbit3 with rabbit@rabbit2 ...done.
rabbitmqctl start_appStarting node rabbit@rabbit3 ...done.

      同样,在任意节点上运行rabbitmqctl cluster_status都会看到这三个节点已经一个集群中了,具体如下:

rabbitmqctl cluster_statusCluster status of node rabbit@rabbit1 ...
 {running_nodes,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]}]
rabbit2$ 
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit3]},{ram,[rabbit@rabbit2]}]},
...done.
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit3 ...
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}]
rabbit2$ 
rabbit2$ 
...done.
rabbit3$ 
rabbit3$ 
rabbit3$ 
rabbit1$ 
rabbit2$ 
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]},{ram,[rabbit@rabbit3]}]},
...done.
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit3 ...
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit3]}]
rabbit3$ 
rabbit2$ 
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]},{ram,[rabbit@rabbit3]}]},
...done.

      现在我们再次启动这两个节点,然后检查集群状态,具体如下:

rabbitmq-server -detachedrabbit1$ 
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]},{ram,[rabbit@rabbit3]}]},
...done.
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit2 ...
 {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
rabbit3$ 
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit1 ...
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}]
rabbit2$ 
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]},{ram,[rabbit@rabbit3]}]},
...done.
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit3 ...
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}]
rabbit3$ 
rabbit3$ 
rabbit3$ 
rabbit1$ 
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
...done.
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit2 ...
 {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
rabbit3$ 
[{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}]
rabbit1$ 
rabbit2$ 
...done.

      需要注意的是此时rabbit@rabbit1任然认为其还在rabbit@rabbit2的集群中,但是当尝试着启动它时会返回错误。我们需要重置它已让其可以再次被启动:

rabbitmqctl start_appStarting node rabbit@rabbit1 ...
rabbit1$ 
rabbit1$ 
...done.

   再次运行分别在三个节点上运行rabbitmqctl cluster_status命令,会发现它们都各自独立运行着RabbitMQ broker

rabbitmqctl cluster_statusCluster status of node rabbit@rabbit1 ...
...done.
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit2 ...
...done.
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit3 ...
...done.

      需要注意的是rabbit@rabbit2此时仍保持着集群的残余状态,然而rabbit@rabbit1和rabbit@rabbit3的RabbitMQ broker则是清新的初始状态。如果我们想让rabbit@rabbit2也这样需要其执行相同的步骤:

rabbitmqctl stop_appStopping node rabbit@rabbit2 ...done.
rabbitmqctl resetResetting node rabbit@rabbit2 ...done.
rabbitmqctl start_appStarting node rabbit@rabbit2 ...done.

7、自动配置集群

      除了通过命令可以实现集群外,还可以通过配置文件来实现集群,配置文件可以参看这里,也可以参看这里。在该文件中设置cluster_nodes域,该域是一个tuple包含一个节点列表,一个atom指示是磁盘还是内存节点。

      如果cluster_nodes被指定,RabbitMQ将尝试着集群节点列表中所有拥有相同Erlang和RabbitMQ版本的在线节点。

      需要注意的是集群配置仅仅应用于新节点。新节点是指刚被重置或者第一次启动的节点。因此,自动集群不会在重启节点后立即生效。这意味着通过rabbitmqctl对集群发生的任何改变都优先于自动集群配置。

      下面我们将演示如何把三个独立的节点加入集群,其中rabbit@rabbit1和rabbit@rabbit2为磁盘节点,为了确保所有节点的干净,首先我们需要重置和停止所有节点,具体如下:

rabbitmqctl stop_appStopping node rabbit@rabbit1 ...done.
rabbitmqctl resetResetting node rabbit@rabbit1 ...done.
rabbitmqctl stopStopping and halting node rabbit@rabbit1 ...done.
rabbitmqctl stop_appStopping node rabbit@rabbit2 ...done.
rabbitmqctl resetResetting node rabbit@rabbit2 ...done.
rabbitmqctl stopStopping and halting node rabbit@rabbit2 ...done.
rabbitmqctl stop_appStopping node rabbit@rabbit3 ...done.
rabbitmqctl resetResetting node rabbit@rabbit3 ...done.
rabbitmqctl stopStopping and halting node rabbit@rabbit3 ...done.

      现在我们设置配置文件中重要的域,具体如下:

  ...
        ...
        ...
  ...
[
  {rabbit, [
        {cluster_nodes, {['rabbit@rabbit1', 'rabbit@rabbit2', 'rabbit@rabbit3'], ram}},
  ]},
].

      这是标准的Erlang配置文件,更多详细信息可以参看RabbitMQ配置向导和Erlang配置帮助

      如果修改了配置文件,可以简单地启动节点:

rabbitmq-server -detachedrabbit2$ 
rabbitmq-server -detachedrabbit1$ 
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]},{ram,[rabbit@rabbit3]}]},
...done.
rabbitmqctl cluster_statusCluster status of node rabbit@rabbit2 ...
 {running_nodes,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]
rabbit3$ 
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]},{ram,[rabbit@rabbit3]}]},
...done.

      需要注意的是:为了从自动集群配置中移除一个节点,首先需要从集群中别的节点的配置文件中将其移除。

 

三、升级集群

      当RabbitMQ升级major或者minor版本时(比如:从3.0.x到3.1.x或者从2.x.x到3.x.x),或者升级Erlang时,整个集群必须停掉。但是当升级RabbitMQ补丁(例如:从3.0.x到3.0.y)时就不需要这样。这些版本都能被固化到集群中(但是除了3.0.0及后来的3.0.x系列)。

      当RabbitMQ升级major或者minor版本时,如果必要RabbitMQ将自动更新其持久化的数据结构。在集群中,这项任务由第一个启动的磁盘节点来执行。因此,当升级RabbitMQ集群时,不应该尝试启动任何内存节点,任何内存节点启动时都会发出一个错误消息然后失败。

      提前停止即将升级的磁盘节点,并在升级完后首先启动它,这是个好主意,但这不是绝对必须的。否则,在升级节点停止和最后节点停止之间,集群配置的改变将会丢失。

      只有RabbitMQ 2.1.1及其以后版本可以自动升级,如果有更早版本的集群,你需要重建集群来完成升级。

 

四、单机器集群

      在某些环境下,在单机器上运行集群的所有节点是很有用的。非常典型的应用是:在桌面或便携式机器上通过启动几个虚拟机来实验集群。在单机器上运行多个节点有两个主要的要求:(1)每个节点有唯一的名称;(2)针对每种协议,每个节点绑定到唯一的IP和端口号对。

      可以通过手动重复调用rabbitmq-server来在同一主机上启动多个节点。必须确保每次调用时设置的环境变量RABBITMQ_NODENAME和RABBITMQ_NODE_PORT都有合适的值。例如:

$ RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=hare rabbitmq-server -detached
$ rabbitmqctl -n hare join_cluster rabbit@`hostname -s`
<span color:#333333;"="" style="word-wrap: break-word; font-size: 10pt;">$ rabbitmqctl -n hare start_app

      上面将建立两个节点的集群。

 

五、防火墙节点

      防火墙集群节点的经典案例是:节点在数据中心或者可信网络中,但是被防火墙分离。同样,不推荐在WAN或者不可信网络上架设集群。

      如果集群中不同节点在同一数据中心,但是在防火墙后面,这样就必须修改配置以确保是内部节点通信。Erlang使用empd(Erlang Port Mapper Daemon)来解析集群内节点名称。

      缺省epmd端口号是4369,但是可以通过环境变量ERL_EPMD_PORT改变其值。所有节点必须使用同样的端口号。防火墙必须允许集群内节点在这个端口上传输数据。更详细的信息请参看Erlang epmd

     一旦分布式Erlang节点地址通过epmd已经被解析了,其它节点将通过Erlang分布式节点协议尝试使用该地址直接通信。通信端口号范围可以通过Erlang内核APP的两个参数来配置:

      inet_dist_listen_min

      inet_dist_listen_max

      防火墙必须允许集群内节点通过这个范围内的端口号传递数据(假设所有节点使用同样的端口号范围)。缺省端口号范围不受限制。更详细的信息可以参看Erlang内核APP帮助

 

六、客户端连接集群

    客户端可以连接集群内任何节点。如果被连接节点崩溃但是集群内其它节点还健在,则客户端通知连接关闭,然后重连集群内其它节点。通常,将节点主机名或者IP地址写死在客户端APP中是不合适的,这会导致当集群配置改变或者集群节点数改变时,客户端需要重新编辑、编译、部署。相反,建议更抽象的方式:可以是动态DNS服务,其有简短的TTL配置,或者TCP负载均衡,或者移动IP排序或者相似技术。一般,管理集群内节点的连接已经非RabbitMQ本身能力所及,建议使用其它技术来解决此类问题。


七、注意事项
1、host问题
        集群节点之间能够互相访问,因此,每个集群节点的host文件中都需要配置集群内所有节点的信息,以保证能互相解析,具体如下:
        #vi /etc/hosts
        172.28.14.223 ZS-QA-01
        172.28.14.224 ZS-QA-02
        172.28.14.225 ZS-QA-03
        比如:3个节点的集群,172.28.14.223/224/225三台机器的hostname分别是ZS-QA-01/02/03。
        如果不知道本机hostname可以通过以下命令查看:
        #cat /proc/sys/kernel/hostname

2、cookie问题
        RabbitMQ的集群完全依赖于Erlang集群,而Erlang集群通过cookie来进行通信认证,因此,集群内所有节点的cookie必须相同,一般采用第一个节点的cookie即可。
        默认情况下,Erlang的cookie文件在$HOME目录下,且是隐藏文件名为.erlang.cookie,可以拷贝第一个节点的cookie文件到集群内其它节点。
        cookie文件仅仅能被own访问,因此,需要将此文件权限改为-r--------,否则启动RabbitMQ时,会出现如下错误:
        [dev@ZS-QA-02 sbin]$ (./rabbitmq-server &)
        [dev@ZS-QA-02 sbin]$ {error_logger,{{2013,5,3},{16,9,29}},"Cookie file /home/dev/.erlang.cookie must be accessible by owner only",[]}
        文件权限可以通过如下命令来修改:
        #chmod -w .erlang.cookie
        #chmod g-r .erlang.cookie
        #chmod o-r .erlang.cookie
3、端口问题
        RabbitMQ默认监听5672端口,如果需要更改端口号,只需要在etc/rabbitmq目录下新建rabbitmq.config文件,并在该文件输入如下内容即可:
        [
            {rabbit, [{tcp_listeners, [22221]}]}
        ].
        具体关于配置的详细信息请参看 这里
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值