前言
消息中间件已经成为分布式工程项目中不可或缺的一部分,市场上也呈现出各种各样的开源 MQ,而 RabbitMQ 以其优秀的性能和易用的特点赢得了较好的口碑,本文将介绍 RabbitMQ 集群的搭建与应用。
1.虚拟机的准备
本文将介绍集群安装 RabbitMQ,所以需要 3 台 Linux 服务器,操作系统选择 CentOS7.4,在滴滴云上创建 3 台 2CPU,4MEM 的虚拟机,并且在同一个 VPC 下。准备的 3 台 VM 分别为 rabbitmq-1,rabbitmq-2,rabbitmq-3,确保 3 台 VM 在内网下可以互通。
2.Erlang与RabbitMQ的准备
在 RabbitMQ-1 上首先安装一套环境,步骤如下:
1. 安装 RabbitMQ 依赖的 Erlang 语言:
dc2-user@10.254.124.34:~$yum install erlang
...
Complete!
当出现以上完成回显时表明 Erlang 已经安装完毕。
2. 安装 RabbitMQ 服务器
dc2-user@10.254.124.34:~$yum install rabbitmq-server
Installed:
rabbitmq-server.noarch 0:3.3.5-34.el7
Dependency Installed:
erlang-sd_notify.x86_64 0:0.1-1.el7
Complete!
出现上述回显表示已经成功安装了 rabbitmq-server,注意版本信息可能会有所不同。
3. 创建日志文件夹
为了更好地维护 RabbitMQ 集群,需要显示地指定日志的目录和数据目录,并改变文件夹的拥有者:
dc2-user@10.254.124.34:~$mkdir -p /home/rabbitmq/log
dc2-user@10.254.124.34:~$mkdir -p /home/rabbitmq/mnesia
dc2-user@10.254.124.34:~$chown -R rabbitmq:rabbitmq /home/rabbitmq
4. 修改 RabbitMQ 的配置文件
安装完 RabbitMQ 后,其默认的配置文件在 /etc/rabbitmq 下的 rabbitmq.config 中,我们拷贝一份作为本节点的配置文件:
dc2-user@10.254.124.34:~$cp rabbitmq.config rabbitmq-env.conf
然后用文本编辑器打开 rabbitmq-env.conf,并添加如下内容:
RABBITMQ_MNESIA_BASE=/home/rabbitmq/mnesia
RABBITMQ_LOG_BASE=/home/rabbitmq/log
然后保存并退出。
5. 添加 web 插件
RabbitMQ 提供了很多实用的插件,其中的 web 插件用于在浏览器上观察集群的整体状态和监控,首先在 /etc/rabbitmq 目录下创建插件配置文件:
dc2-user@10.254.124.34:~$touch enabled_plugins
然后用文本编辑器打开 enabled_plugins 文件,输入以下内容:
[rabbitmq_management].
6. 在单节点上启动RabbitMQ
运行下面的命令启动:
dc2-user@10.254.124.34:~$service rabbitmq-server start
如果没有报错则表示启动成功,可以使用一下命令验证:
dc2-user@10-254-208-128:~$rabbitmqctl status
...
{alarms,[]},
{listeners,[{clustering,25672,"::"},{amqp,5672,"::"}]},
{vm_memory_high_watermark,0.4},
{vm_memory_limit,1589903360},
{disk_free_limit,50000000},
{disk_free,40955396096},
{file_descriptors,
[{total_limit,924},{total_used,3},{sockets_limit,829},{sockets_used,1}]},
{processes,[{limit,1048576},{used,178}]},
{run_queue,0},
{uptime,133}]
...done.
当出现上面的内容时表明在单个节点上已经安装成功了,当然也可以通过 web 界面来查看集群,在任意一个联网的浏览器上输入:rabbitmq-1 的外网 IP:15672 即可看到相应内容,注意这时 /home/rabbitmq 下面的 log 文件夹和 mnesia 中已经可以看到内容了,mnesia 中保存的是 RabbitMQ 的数据。
7. 配置集群
- 修改主机名
3 台 VM 的主机名依次为 rabbitmq-1,rabbitmq-2,rabbitmq-3,在各自机器上执行下列语句:
dc2-user@10.254.124.34:~$hostnamectl set-hostname rabbitmq-1
上述只是临时生效,如需永久生效请看下面的方法。
- 让集群识别别的机器
3 台 VM 需要互相通信就需要对方的 IP 等信息,用文本编辑器编辑 /etc/hosts 文件,注意左边的 IP 改成自己机器的内网 IP。
127.0.0.1 rabbitmq-1
10.254.124.34 rabbitmq-1
10.254.181.10 rabbitmq-2
10.254.19.61 rabbitmq-3
如果是 CentOS7.4 则还要修改 /etc/hostname 文件,输入以下内容:
10.254.124.34 rabbitmq-1
10.254.181.10 rabbitmq-2
10.254.19.61 rabbitmq-3
- 拷贝拷贝 erlang.cookie
需要将 3 台 VM 上的拷贝 erlang.cookie 内容保持一致,因为 RabbitMQ 是基于 erlang 工作的,erlang 要相互通信依靠 erlang.cookie。
查看第一台机器上的 erlang.cookie:
dc2-user@rabbitmq-1:~$cat /var/lib/rabbitmq/.erlang.cookie
ZWZAVHWGJQJJTOLXLDGC
上面的字符串就是对应的 cookie,可以使用 scp 命令拷贝到其他 2 台 VM 上使用,命令如下:
dc2-user@rabbitmq-1:~$scp /var/lib/rabbitmq/.erlang.cookie
dc2-user@10.254.181.10:/var/lib/rabbitmq/.erlang.cookie
dc2-user@rabbitmq-1:~$scp /var/lib/rabbitmq/.erlang.cookie
dc2-user@10.254.19.61:/var/lib/rabbitmq/.erlang.cookie
- 加入集群
剩下的 2 台 VM 需要使用如下命令加入到第一个所在的集群中:
dc2-user@rabbitmq-2:~$rabbitmqctl stop_app
dc2-user@rabbitmq-2:~$rabbitmqctl join_cluster
rabbit@rabbitmq-1
dc2-user@rabbitmq-2:~$rabbitmqctl start_app
到此为止集群的创建就完成了,3 个 RabbitMQ 节点组成了一个集群工作。
浏览器插件显示如下图效果:
- 用户相关命令
RabbitMQ 提供了强大的客户端命令,可以实现复杂的用户权限管理,默认安装完成后已经拥有 guest 账号。需要添加新用户可以使用如下命令:
dc2-user@rabbitmq-1:~$rabbitmqctl add_user test test123
add_user 后面的 2 个参数分别是用户名和对应的密码。
角色设置可以限制用户的访问权限,可以用一下命令设置:
dc2-user@rabbitmq-1:~$rabbitmqctl set_user_tags test administrator
以上命令设置了账号 test 为管理员权限。
vhost 设置命令如下:
dc2-user@rabbitmq-1:~$rabbitmqctl set_permissions -p / test ".*" ".*" ".*"
以上命令设置了 test 用户在虚拟空间/拥有所有权限。
3.RabbitMQ 构建镜像实现冗余
如果 RabbitMQ 集群只有一个 broker 节点,那么该节点的失效将导致整个服务临时性的不可用,并且可能会导致 message 的丢失(尤其是在非持久化 message 存储于非持久化 queue 中的时候)。当然可以将所有的 publish 的 message 都设置为持久化的,并且使用持久化的 queue,但是这样仍然无法避免由于缓存导致的问题:因为 message 在发送之后和被写入磁盘并执行 fsync 之间存在一个虽然短暂但是会产生问题的时间窗。通过 publisher 的 confirm 机制能够确保客户端知道哪些 message 已经存入磁盘,尽管如此,一般不希望遇到因单点故障导致的服务不可用
如果 RabbitMQ 集群是由多个 broker 节点构成的,那么从服务的整体可用性上来讲,该集群对于单点失效是有弹性的,但是同时也需要注意:尽管 exchange 和 binding 能够在单点失效问题上幸免于难,但是 queue 和其上持有的 message 却不行,这是因为 queue 及其内容仅仅存储于单个节点之上,所以一个节点的失效表现为其对应的 queue 不可用。
引入 RabbitMQ 的镜像队列机制,将 queue 镜像到 cluster 中其他的节点之上。在该实现下,如果集群中的一个节点失效了,queue 能自动地切换到镜像中的另一个节点以保证服务的可用性。在通常的用法中,针对每一个镜像队列都包含一个 master 和多个 slave,分别对应于不同的节点。Slave 会准确地按照 master 执行命令的顺序进行命令执行,故 slave 与 master 上维护的状态应该是相同的。除了 publish 外所有动作都只会向 master 发送,然后由 master 将命令执行的结果广播给 slave 们,故看似从镜像队列中的消费操作实际上是在 master 上执行的。
一旦完成了选中的 slave 被提升为 master 的动作,发送到镜像队列的 message 将不会再丢失:publish 到镜像队列的所有消息总是被直接 publish 到 master 和所有的 slave 之上。这样一旦 master 失效了,message 仍然可以继续发送到其他 slave 上。
可以使用如下的命令来创建队列:
dc2-user@rabbitmq-1:~$sudo rabbitmqctl eval 'rabbit_amqqueue:declare({resource, <<"/">>, queue, <<"test-queue">>}, false, false, [], none).'
上述命令创建在/下创建了名字为 test-queue 的非持久化的队列。但是这样如果队列所在的 broker 出现了问题,那么这个队列就会丢失,对业务产生影响,所以可以使用以下命令创建镜像:
dc2-user@rabbitmq-1:~$rabbitmqctl set_policy ha-test-two "^" '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
上述命令设置了名字为 ha-test-two 的镜像策略,"^"表示通配所有的队列,每个符合镜像条件的队列一共有 2 份,即一共有 1 个备份,一个主队列,同步模式采用自动备份,无需手工通过命令进行备份。同步后的队列如下图所示:
4.总结
本文介绍了如何在在滴滴云服务器上构建高可用集群的 RabbitMQ 集群,并阐述了一些通用的命令,可以看到使用滴滴云的 VPC 等产品可以轻松地搭建分布式的高可用环境。