在本章节中我们将覆盖:
- 创建一个本地服务器集群
- 创建一个简单集群
- 自动添加一个RabbitMQ 集群
- 引入消息负载均衡器
- 创建集群客户端
介绍
RabbitMQ提供了各种各样的特性以及集群功能.
通过使用集群,一组适当配置的主机的行为与单个broker实例一样,但集群带有下面的目的:
- 高可用性: 如果一个节点宕机了,分布式broker仍然能接受和处理消息.这方面内容会在Chapter 7,Developing High-availability Applications中深入探讨.
- 可伸缩的消息负载:消息路由在集群节点间是分布式的.
- 连接客户端的伸缩性:每个节点都可以用来处理可用客户端的某部分子集.
TIP
RabbitMQ集群节点应该在LAN内. RabbitMQ 集群不能很好的容忍网络分化,因为网络分化期间,每个节点都是单独工作的,目前还没有机制来防止所谓的"split-brain syndrome" (可在 http://en.wikipedia.org/wiki/Consensus_(computer_science)找到解决这个问题的参数).在本地网络中, RabbitMQ使用短超时来确定兄弟节点的可用性。更多信息,可参考http://www.rabbitmq.com/nettick.html.
RabbitMQ集群中所有节点除了队列外,可以共享虚拟主机,用户,交换器的定义,物理上它们只存在于创建的节点内. 另一方面,它们是全局定义的,可通过集群的任何节点进行连接。
如果一个客户端从还没有创建队列的节点上生产或消费消息,其性能会降低,因为客户端需要多个跳跃才能到达物理上持有队列的节点。
TIP
默认情况下,队列不会在集群节点间复制,因此当出现节点故障时,你可能会丢失数据.可参考Chapter 7, Developing High-availability Applications 来解决这个问题。
在章节中,我们将展示RabbitMQ集群伸缩性方面的例子,同时也会展示设计集群AMQP客户端的正确方法.
创建一个本地服务器集群
本地集群指的是在同一台主机上将多个RabbitMQ实例以单一集群的方式进行配置。
实际上,在产品环境中,通常来说,这没有多大用处,因为它不提供任何可伸缩性和可靠性改进. 然而,创建本地集群来测试配置是相当用的,如:开发PC。
准备
为了付诸实施,我们需要安装有RabbitMQ,并能正常运行。即使严格上意义来不是必要的,我们也会安装管理插件,就如Chapter 3, Managing RabbitMQ中展示的一样.在这种情况下,我们需要为每个broker指定不同的web控制台TCP端口.
如何做
要创建一个本地集群,我们需要执行下面的步骤:
1. 从root shell (Linux)中,使用下面的命令来启动另一个RbbitMQ实例:
env RABBITMQ_NODENAME=node01 RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15673}]"
rabbitmq-server -detached
2. 使用下面的命令将此实例加入到默认那个节点:
rabbitmqctl -n node01 stop_app
rabbitmqctl -n node01 join_cluster rabbit@$HOSTNAME
rabbitmqctl -n node01 start_app
3. 使用下面的命令来检查运行的集群状态:
rabbitmqctl cluster_status
4. 使用下面的命令退出集群,并回归到单独节点状态:
rabbitmqctl -n node01 stop_app
rabbitmqctl -n node01 reset
rabbitmqctl -n node01 start_app
如何工作
通过覆盖某些配置选项,可在同台机器上运行多个RabbitMQ 服务器实例. 尤其是在已经安装了broker的情况下,新安装的broker必须强制指定不同的broker端口(默认为5672)以及管理插件端口(默认为15672)。
这可以通过修改环境变量来完成(步骤1).目前,可用的RabbitMQ环境变量如下:
- RABBITMQ_MNESIA_BASE
- RABBITMQ_LOG_BASE
- RABBITMQ_NODENAME
- RABBITMQ_NODE_IP_ADDRESS
- RABBITMQ_NODE_PORT
你可以在shell提示窗口中,输入下面的命令来查看rabbitmqserver指南页面中所有前面变量的详细描述:
man rabbitmq-server
在这里,我们设置了node名称,TCP端口,以及一些自定义参数:
RABBITMQ_NODENAME=node01
RABBITMQ_NODE_PORT=5673
RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15673}]"
TIP 在Windows中,步骤1中的命令可通过在命令行中进行设置.你可以将下面的脚本拷贝到一个.bat文件中来达到此目的:
set RABBITMQ_NODENAME=%1
set RABBITMQ_NODE_PORT=%2
set RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,%3}]" "SBIN_FULL_PATH_HERE\rabbitmq-server" -detached
此时,第二台服务器相对于第一台服务器来说,仍然是独立运行的。我们必须运行步骤2中的三个命令,这样两个服务器才会绑定到同一个集群中。
TIP
在 Windows中,你需要使用 %COMPUTERNAME% 来代替$HOSTNAME.
通过在集群环境中运行提供的例子,你可以很容易地进行检查.节点上创建的以及代码中使用的交换器将会复制到新添加的节点上,反之亦然. 可单独访问http://localhost:15672 和 http://localhost:15673 管理插件来了解此种情况。
另外, 你也可以使用-n选项来指定特定节点来执行rabbitmqctl命令,就像步骤2和步骤4中展示的一样.
如果你不使用-n选项,命令会直接在默认rabbitmq节点上执行.
更多
两个节点神奇般地可以互相说话了,这要多亏它们共享了相同的Erlang cookie,因为它们在同一台机器上.
事实上,多个不同RabbitMQ实例与任何Erlang分布式程序一样,都会使用基本认证方案.两个应用程序实例要能相互通信,它们必须有相同的Erlang cookie(一个包含任意数据的文件).它可以是字符串,GUID,或者是你能想到的任何东西.
TIP
Erlang cookie事实是上一个密钥,如果没有适当的保护, Erlang则不允许使用,并且不会工作.你可以为其设置适当的权限,如下所示:
chmod 400 .erlang.cookie
你可在http://www.erlang.org/doc/getting_started/conc_prog.html#id67454找到更多关于Erlang cookies的信息.
在不同主机上搭建集群中,必须强制性在各个主机上使用相同的Erlang cookie,在后续食谱中我们将看到这方面的内容。
创建一个简单集群
设置一个RabbitMQ集群也就是几分钟的事情,只需要配置几个步骤,高可用集群就可以部署运行。
事实上,相对于本地集群,它更容易,可参考创建本地集群食谱,因为每个节点都会使用标准端口设置.
准备
要准备此食谱,你至少需要安装在两台主机上安装RabbitMQ实例,并将它们配置为独立broker.
TIP
要使集群能正常工作,必须确保所有主机上的RabbitMQ和Erlang是一致的.
主机可以是物理机,也可以是云实例,还可以是虚拟机器.
TIP
Amazon Web Services (AWS) 预先安装的RabbitMQ通常都是过期的,我们建议安装主流的Linux AMI,然后安装最新的RabbitMQ版本,可参考(http://www.rabbitmq.com/download.html).
如何做
在下面的步骤中,我们将节点命名为node01, node02, node03.作为一种选择,你可以使用你当前的主机名称,从而可以越过步骤1到4.
1. 如果节点已经运行,通过下面的命令来停止RabbitMQ server:
service rabbitmq-server stop
2. 在node01上, 在/etc/hosts中追加所有IP名称绑定,如下所示(将你的IP地址放在这里):
10.0.0.1 node01
10.0.0.2 node02
...
3. 从node1中拷贝下面的文件到所有其它节点上,以保证有相同的主机名称定义:
scp /etc/hosts 10.0.0.2:/etc/hosts
scp /etc/hosts 10.0.0.3:/etc/hosts
...
4. 在所有节点上设置本地主机名称(每个服务器都设为不同的主机名称):
echo node01 > /etc/hostname
hostname –F /etc/hostname
然后,我们配置RabbitMQ.
5. 从node01中拷贝RabbitMQ Erlang cookie到其它所有节点上:
scp /var/lib/rabbitmq/.erlang.cookie node02:/var/lib/rabbitmq/.erlang.cookie
scp /var/lib/rabbitmq/.erlang.cookie node03:/var/lib/rabbitmq/.erlang.cookie
...
6. 重启所有节点上的RabbitMQ server:
service rabbitmq-server start
7. 将所有节点加入到node01.在除node01的所有节点上,运行下面的命令:
rabbitmqctl stop_app
rabbitmqctl join_cluster --ram rabbit@node01
rabbitmqctl start_app
此时,集群已能正常运行了.我们可以执行一些更多的命令:
1. 检查集群状态:
rabbitmqctl cluster_status
2. 修改node02的节点类型:
rabbitmqctl stop_app
rabbitmqctl change_cluster_node_type disc
rabbitmqctl stat_app
3. 让集群忘了node02,它是从node01来执行的:
rabbitmqctl forget_cluster_node rabbit@node02
4. 让删除的node02忘记其集群状态.在node02上使用下面的命令:
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
如何工作
集群中的所有服务器必须通过主机名来互相感知,这是很重要的,因为IP地址对于RabbitMQ来说还不够。因此,你必须为所有服务器配置/etc/hosts (或者配置windows中类似的hosts文件。
TIP
所有节点都需要使用短主机(short hostnames)来寻址. 如果你试图使用全限定域名(FQDN), RabbitMQ可能不能正常工作。
使用外部名称服务器也是可以的,但在这种情况下,集群需要对它进行依赖. 通常的惯例是在集群的前端设置了一个名称服务器,并依赖静态主机信息来配置集群。
在所有节点配置之后,需要对它们进行同步,以在其它服务器上共享同一个cookie. cookie文件可包含任意的字符串,以在节点间实现简单地相互认证。(步骤5)
TIP
通过指定-n选项,也可以配置Erlang cookie来让rabbitmqctl与远程节点一同工作.
此时,可能过下面的命令来创建集群:
rabbitmqctl join_cluster --ram rabbit@node01
如果我们正确地执行了所有步骤,我们可以得到下面的输出:
Clustering node rabbit@node02 with rabbit@node01 ...
...done.
通过使用 --ram 选项,我们将RabbitMQ 节点类型声明为RAM.RAM类型的节点不会在磁盘上保存信息,它只是比磁盘节点(默认节点类型)更快速.
在RabbitMQ集群的介绍中,至少在集群中有一个节点的类型为磁盘节点.这样,在服务器重启或某个磁盘节点出现故障时,所有配置(交换器,队列等)才会安全。
TIP
实际上,由于你已在所有节点上拷贝了Erlang cookies,因此你可以通过-n选项,可在node01上来执行指定节点上的所有操作:
rabbitmqctl -n rabbit@node02 stop_app
rabbitmqctl -n rabbit@node02 join_cluster rabbit@node01
rabbitmqctl -n rabbit@node02 start_app
更多
为了大型集群上准备这种配置,通常最佳实践是使用分布式shell前端.
最初, dsh只能应用于IBM AIX,但在现代的Linux系统中,你可以找到pdsh和pdcp (通常在pdsh包中)命令来执行同样的操作。
你也可以在老的或window上使用相似的前端工作(如dancer工具:http://www.netfort.gr.jp/~dancer/software/dsh.html.en).
另外,你也可以使用你喜欢的集群管理软件来执行相同的操作.
也可参考
你可在http://www.rabbitmq.com/clustering.html找到更多关于集群指南的信息.
自动添加一个RabbitMQ集群通常情况下,当服务器启动时,我们需要准备加入集群的操作系统镜像.通过一些脚本,我们可以很容易地实现这种任务,但RabbitMQ提供了更简单的更优雅的方法。在本食谱中,我们将进行介绍。
准备
为了准备这个食谱,我们至少两台已经安装RabbitMQ的主机,并将它们配置为独立broker.同时,这些主机还应该有相同版本的RabbitMQ 和 Erlang.
如何做
在本食谱中,我们启动了两个高可用性RabbitMQ核心broker,它们满足集群正常工作,具备高可靠性的最小条件 。然后,我们将其它所有节点配置为自动配置(通过在rabbitmq.config文件中编写配置)。
1. 配置服务器主机名已在Creating a simple cluster 食谱的步骤1 到 4中看过了.
2. 为所有服务器配置相同的Erlang cookie.
3. 在节点node01和node02上启动RabbitMQ,并根据它们来创建一个集群,正如前面食谱中看过的.
4.如果节点已经被使用了,重设所有其它节点并停止,就像下面这样:
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
5. 在所有其它节点上,设置RabbitMQ配置文件rabbitmq.config内容(最后的圆点是配置文件的一部分):
[{rabbit,
[{cluster_nodes, {['rabbit@node01', 'rabbit@node02'],
ram}}]}].
6. 在所有节点上重启RabbitMQ,如下所示:
service rabbitmq-server start
如何工作
这种配置最适合在预先部署OS镜像和RabbitMQ. 这些镜像可以在不伤害现有集群的情况下进行部署和使用.
RabbitMQ在启动时,会读取 rabbitmq.config文件. RabbitMQ将忽略该节点的集群自动配置,除非它是一个完全干净的状态, 也就是要启用集群自动配置,需要满足下面两个条件中的任何一个:
- RabbitMQ实例已经被重置和停止,正如步骤4中所展示的一样
- RabbitMQ实例已经安装且从没有启动过
在后一种情况中,你不需要执行步骤6,只需要将RabbitMQ配置文件放置到正确的位置,然后再启动RabbitMQ (如果我们是以镜像方式来运行的,那么它将在服务器启动时,自动运行),随后游戏就结束了。
TIP
默认的RabbitMQ配置文件称为rabbitmq.config,其位置根据安装时配置是变化的.下面是典型的位置:
- /etc/rabbitmq/ (大多数分发是这样)
- Rabbitmqdir/etc/rabbimq/ (Mac OSX)
- %APPDATA%\RabbitMQ\ (Windows)
一旦节点启动了,它就会重复地尝试连接指定节点,作为步骤5中列表节点元组的第一个元素.只要它成功连上了节点列表中的一个,它就会以RAM节点加入到集群中,即作为元组的第二个元素 (或者你也可以指定一个磁盘节点).
介绍消费者负载均衡器
为了有一个多节点的RabbitMQ集群, clients必须知道所有的IP地址,如果集群配置是动态的,clients应该收到任何变化的通知。
此外, clients可以使用一些方法联系很少负载的clients.
通常的解决方案是在集群的前端增加一个负载均衡器.
负载均衡器可以硬件解决方案或软件包,可在一般器上进行安装和配置.而且,也有多种不同的负载均衡技术, 下面是最相关的:
- Round-robin name servers
- TCP load balancers
TCP 负载均衡器可作为:
- Proxy: 在这种情况下,所有连接的接受和执行都在负载均衡器自身上执行
- Direct Server Return (DSR): 只接受连接的初始化 (SYN),然后让客户端与服务器在后端直接连接,而不再进行干预(明显的优势在于伸缩性和性能)
这并不是全部,但更深入的细节已经超出了本书的范围. 针对我们的目的来说,我们会使用TCP 软件负载均衡器-Crossroads (http://crossroads.e-tunity.com/).
当其安装后,clients将会对它进行连接,它会将连接转发到后端RabbitMQ服务器,正如下图所示:
![](file:///C:/Users/Administrator/AppData/Local/YNote/data/huiwen_82132000@163.com/59e9532d96414c16a0a48d97159f4a81/clipboard.png)
准备
在本食谱中,我们需要三台机器;两台用于RabbitMQ集群,一个用于安装负载均衡器.
在这种情况下,如果clients是从Internet发起连接的话,前面两台服务器不需要公网IP地址,负载均衡器有公网IP就足够了.
如何做
在这个例子中,我们采用了两个Linux Debian (IP为 192.168.10.2、192.168.10.3) 来作 RabbitMQ服务器,一台Linux Centos作Crossroads (with IP 192.168.10.1).
使用两台机器来创建 RabbitMQ集群的例子,我们已经在创建简单集群食谱中看过了.
下面的步骤只针对Centos 机器(192.168.10.1):
1. 为了编译Crossroads, 你需要 C++ 编译器和GNU make. 同时你也需要系统标准C包;执行下面的命令来准备机器:
yum groupinstall "Development Tools"
2. 然后, 执行wget http://crossroads.e-tunity.com/downloads/crossroads-stable.tar.gz 下载最新的Crossroads的稳定版本到你的源码目录中(如: /usr/local/src/),
3. 使用下面的解压命令:
tar xvzf crossroads-stable.tar.gz
4. 进入解压目录,cd crossroads-2.74, 执行make install, 然后等待… 最后会出现下面的截屏:
![](file:///C:/Users/Administrator/AppData/Local/YNote/data/huiwen_82132000@163.com/690e6929362b47afb0c8989fdaa90cfe/clipboard.png)
5. 为RabbitMQ配置负载均衡器:
xr -v --server tcp:0:5672 --backend 192.168.10.2:5672 --backend 192.168.10.3:5672 2>&1 >> /var/log/xr-rmq.log &
6. 如果你启用了RabbitMQ web管理插件(参考 Chapter 3,Managing RabbitMQ),你也可以为其创建一个负载均衡器:
xr -v --server tcp:0:80 --backend 192.168.10.2:15672 --backend 192.168.10.3:15672 2>&1 >> /var/log/xr-web.log &
7. 在浏览器中打开http://192.168.10.1.
如何工作
在创建简单集群之后(步骤1),我们安装并配置了负载均衡器;对于Crossroads,我们假设你使用的是Centos Linux distribution.跟随步骤2到5,你可以编译和安装Crossroads, 然后你可以配置负载均衡器角色。最初,我们使用下面的命令来运行crossroads的负载均衡器(步骤6):
xr -v --server tcp:0:5672 --backend 192.168.10.2:5672
--backend 192.168.10.3:5672
2>&1 >> /var/log/xr-rmq.log &
xr –v server tcp:0:5672命令将5672端口(RabbitMQ默认TCP端口)与服务器TCP监听器进行了关联, --backend 参数 用于重定向连接,在我们这里,将会重定向到192.168.10.2:5672 and 192.168.10.3:5672.
我们使用>>/var/log/xr-rmq.log将输出写到日志文件中.
为了从负载均衡器访问web管理插件,在步骤7中,我们使用15672端口又执行另一个XR服务器.
Crossroads 可使用不同的分发算法,但默认情况下,它使用简单的 round-robin 算法,这就是说,连接会均匀地重定向到每个后端服务器上。
此时,你可以使用crossroadsIP代替服务器IP地址来运行任何的RabbitMQ client示例.连接会自动地转发到后端brokers上.
在下章节中,我们将看到负载均衡器的使用和其它高可用方案.
更多
Crossroads是简单地Linux负载均衡器,可参考http://crossroads.etunity.com/documentation.xr 来了解全部信息。 另一种广泛地负载均衡方案是HAProxy (http://haproxy.1wt.eu/)
也可参考
可阅读章节-从客户端来连接集群(http://www.rabbitmq.com/clustering.html)来了解详情.
创建集群客户端
当你创建了简单集群后,客户端需要指定多个broker地址来连接broker.在本食谱中,我们将看到在RabbitMQ Java client中,如何使用多个地址来连接broker.
注意:与集群前端负载均衡器不同,在那里并不需要指定多个地址,只需要指定前端均衡器地址即可。
准备
你需要Java 1.6+和Apache Maven.
如何做
首先,你需要一个简单集群.
然后,你可以使用下面的代码来进行连接:
Address[] addrArr = new Address[]{ new Address("node01",portnode1), new Address("node02", portnode2)};
connection = factory.newConnection(addrArr);
如何工作
在RabbitMQ包中,你可以传递多个host/IP给连接工厂.这种特性可用于小型和大型场景.例如,如果你有两个节点,并且你想避免使用DNS或负载均衡器,那么你可以直接在client中传递所有的broker地址. client会尝试连接第一个地址,如果第一个地址连接失败,client会在不抛出异常的情况下,继续尝试连接下一个地址。
同样的方案可应用于更为复杂的架构.如,假设你有下面两个主机:
- myrmqcluster_production.internal.com
- myrmqcluster_maintenance.internal.com
客户端总是会连接production系统,但如果production 系统正在维护,clients会连接第二个地址.
更多 实际上,在没有RabbitMQ集群的情况下,你也可以使用这种连接方法.在源码Chapter06/Recipe06中,我们创建了一种虚拟算法来负载连接。 在这种情况下,你完全可以构建两个独立的RabbitMQ实例.
也可参考
在下面的章节中,我们将看到如何通过混合集群,HA策略,客户端技术来创建一个HA client.
posted on 2016-06-15 20:54
胡小军 阅读(1930)
评论(2) 编辑 收藏 所属分类:
RabbitMQ