Centos7 搭建 RabbitMQ 集群(超详细)

前言

集群模式

RabbitMQ 是用 Erang 开发的,集群模式分为两种普通模式镜像模式,可以说镜像模式普通模式的升级版,其中 RabbitMQ 默认使用的是 普通模式

  • 普通模式:
    以两个节点(rabbit01、rabbit02)为例来进行说明,rabbit01 和 rabbit02 两个节点仅有相同的元数据,即队列的结构,但消息实体只存在于其中一个节点 rabbit01(或者 rabbit02)中。当消息进入 rabbit01 节点的 Queue 后,consumer 从 rabbit02 节点消费时,RabbitMQ 会临时在 rabbit01、rabbit02 间进行消息传输,把 A 中的消息实体取出并经过 B 发送给 consumer。所以 consumer 应尽量连接每一个节点,从中取消息。即对于同一个逻辑队列,要在多个节点建立物理 Queue。否则无论 consumer 连 rabbit01 或 rabbit02,出口总在 rabbit01,会产生瓶颈。当 rabbit01 节点故障后,rabbit02 节点无法取到 rabbit01 节点中还未消费的消息实体。如果做了消息持久化,那么得等 rabbit01 节点恢复,然后才可被消费;如果没有持久化的话,就会产生消息丢失的现象。

  • 镜像模式:
    在普通模式的基础上,把需要的队列做成镜像队列,存在于多个节点,消息实体会主动在镜像节点间同步,而不是在客户端取数据时临时拉取,也就是说多少节点消息就会备份多少份。该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉,所以在对业务可靠性要求较高的场合中适用。由于镜像队列之间消息自动同步,且内部有选举 Master 机制,即使 Master 节点宕机也不会影响整个集群的使用,达到去中心化的目的,从而有效的防止消息丢失及服务不可用等问题

集群节点的区别

RabbitMQ 的集群节点分为磁盘节点、内存节点。RabbitMQ 支持消息的持久化,也就是数据写在磁盘上。在 RabbitMQ 集群中,必须至少有一个磁盘节点,否则队列元数据无法写入到集群中。当磁盘节点宕掉时,集群将无法写入新的队列元数据信息。如果 RabbitMQ 集群全部宕机,必须先启动磁盘节点,然后再启动内存节点。最合适的方案就是既有磁盘节点,又有内存节点,推荐 1 个 磁盘节点 + 2 个内存节点的集群搭建方式。

准备工作

集群规划

名称 IP 端口 用途 RabbitMQ 节点名称
节点一 192.168.1.109 15672 磁盘节点 rabbit@rabbitmq1
节点二 192.168.1.201 15672 内存节点 rabbit@rabbitmq2
节点三 192.168.1.200 15672 内存节点 rabbit@rabbitmq3

注意,在生产环境搭建 RabbitMQ 集群时,所有集群节点要求都可以连接上互联网,另外 RabbitMQ 集群节点建议都在同一网段里,如果是跨广域网(外网),效果会变差。

系统初始化

创建用户和用户组

1
2
3
4
5
# 创建rabbitmq用户组
# groupadd rabbitmq

# 创建rabbitmq用户(不允许远程登录)
# useradd -g rabbitmq rabbitmq -s /bin/false

RabbitMQ 集群安装

Erlang 安装

在每个集群节点上分别编译安装 Erlang,这里使用的版本是 23.2,其他版本的 Erlang 可以从 Erlang 官网 下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 安装依赖
# yum install -y make autoconf gcc gcc-c++ glibc-devel kernel-devel m4 ncurses-devel openssl-devel unixODBC unixODBC-devel libtool libtool-ltdl-devel unzip

# 创建安装目录
# mkdir -p /usr/local/erlang-23.2

# 下载
# wget https://erlang.org/download/otp_src_23.2.tar.gz

# 解压
# tar -xvf otp_src_23.2.tar.gz

# 进入解压目录
# cd otp_src_23.2

# 配置
# ./otp_build autoconf
# ./configure --prefix=/usr/local/erlang-23.2 --without-javac

# 编译安装
# make && make install

# 创建软链接
# ln -sf /usr/local/erlang-23.2/bin/erl /usr/bin/erl

# 配置环境变量
# vim /etc/profile
export ERLANG_HOME=/usr/local/erlang-23.2
export PATH=$PATH:$ERLANG_HOME/bin

# 使环境变量生效
# source /etc/profile

RabbitMQ 安装

在每个集群节点上分别使用二进制包的方式安装 RabbitMQ,使用的版本是 3.8.6,其他版本的 RabbitMQ 可以从 RabbitMQ Github 下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 安装依赖
# yum install -y xmlto python-simplejson

# 下载
# wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.6/rabbitmq-server-generic-unix-3.8.6.tar.xz

# 解压
# xz -d rabbitmq-server-generic-unix-3.8.6.tar.xz
# tar -xvf rabbitmq-server-generic-unix-3.8.6.tar

# 拷贝安装文件
# cp -r rabbitmq_server-3.8.6 /usr/local/rabbitmq

# 文件授权
# chown -R rabbitmq:rabbitmq /usr/local/rabbitmq

RabbitMQ 配置

在每个集群节点上分别配置 RabbitMQ,包括创建默认的日志目录与数据目录、启用 Web 控制台管理插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 创建默认的日志目录与数据目录
# mkdir -p /usr/local/rabbitmq/var/log/rabbitmq
# mkdir -p /usr/local/rabbitmq/var/lib/rabbitmq/mnesia

# 创建默认的配置文件
# touch /usr/local/rabbitmq/etc/rabbitmq/rabbitmq.config
# echo "[]." > /usr/local/rabbitmq/etc/rabbitmq/rabbitmq.config

# 创建默认的环境变量文件
# touch /usr/local/rabbitmq/etc/rabbitmq/rabbitmq-env.conf
# echo "CONF_ENV_FILE=/usr/local/rabbitmq/etc/rabbitmq/rabbitmq-env.conf" >> /usr/local/rabbitmq/sbin/rabbitmq-defaults

# 启用Web控制台管理插件
# cd /usr/local/rabbitmq/sbin
# ./rabbitmq-plugins enable rabbitmq_management

# 查看所有插件的安装信息
# ./rabbitmq-plugins list

# 文件授权
# chown -R rabbitmq:rabbitmq /usr/local/rabbitmq

在每个集群节点上分别配置 RabbitMQ,包括创建虚拟主机、超级管理员用户、设置角色权限。由于出于系统安全考虑,RabbitMQ 默认限制了 guest 用户只能通过 localhost 登录使用,因此需要手动创建管理员帐号,并更改 guest 用户默认的密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 进入安装目录
# cd /usr/local/rabbitmq/sbin

# 前台启动RabbitMQ服务(默认会打印出日志文件和配置文件的路径)
# ./rabbitmq-server

# 或者后台启动RabbitMQ服务
# ./rabbitmq-server -detached

# 创建虚拟主机(相当于MySQL的数据库概念)
# ./rabbitmqctl add_vhost /

# 更改guest用户默认的密码
# ./rabbitmqctl change_password guest yourPassword

# 创建超级管理员用户
# ./rabbitmqctl add_user admin yourPassword

# 赋予administrator角色给超级管理员用户
# ./rabbitmqctl set_user_tags admin administrator

# 赋予超级管理员用户权限
# ./rabbitmqctl set_permissions -p / admin '.*' '.*' '.*'

# 彻底关闭后台启动的RabbitMQ服务
# ./rabbitmqctl stop

RabbitMQ 普通集群搭建

添加节点主机名

在每个集群节点上分别编辑 /etc/hosts 配置文件,指定各个节点的主机名

1
2
3
4
# vim /etc/hosts
192.168.1.109 rabbitmq1
192.168.1.201 rabbitmq2
192.168.1.200 rabbitmq3

配置节点的名称

RabbitMQ 节点由节点名称(RABBITMQ_NODENAME)标识,节点名称由两部分组成,前缀(默认是 rabbit)和主机名,例如:rabbit@rabbit1 是一个包含前缀 rabbit 和主机名 rabbit1 的节点名称。可以在同一台主机上运行多个 RabbitMQ 节点,但集群中每个节点必须有一个唯一的 RABBITMQ_NODENAME。若在同一台主机上运行多个节点(开发和 QA 环境中通常是这种情况),每个节点还必须使用不同的前缀,例如:rabbit1@hostname1rabbit2@hostname2在集群中,节点使用节点名称标识和联系彼此,这意味着必须解析每个节点名的主机名部分。当节点启动时,它会检查是否已为其分配了节点名,这是通过配置文件 rabbitmq-env.conf 里的 RABBITMQ_NODENAME 环境变量指定,如果环境变量没有配置,则节点将解析其主机名并在其前面添加 rabbit 来计算其节点名。


在每个集群节点上分别配置节点名称,只需将下面 rabbit@xxx 中的 xxx 替换为该节点的主机名即可,例如节点一的节点名称为: rabbit@rabbitmq1

1
2
# 配置节点名称
# echo "NODENAME=rabbit@xxx" >> /usr/local/rabbitmq/etc/rabbitmq/rabbitmq-env.conf

RabbitMQ 的集群是依附于 Erlang 的集群来工作的,所以必须先构建起 Erlang 的集群。Erlang 的集群中各节点是经由过程一个 cookie 来实现的,当使用解压缩的方式来安装 RabbitMQ 时,那么这个 cookie 存放在 ${home}/.erlang.cookie 中,文件是 400 的权限。必须保证集群各节点的 cookie 一致,不然节点之间就无法通信。

1
2
3
# 拷贝节点一的Cookie到其他节点
# scp /root/.erlang.cookie root@rabbitmq2:/root/
# scp /root/.erlang.cookie root@rabbitmq3:/root/

构建集群节点

在每个集群节点上分别启动 RabbitMQ 的服务,这里默认使用的用户为 root当使用解压缩的方式来安装 RabbitMQ 时,cookie 是存放在 ${home}/.erlang.cookie,因此这里必须注意各个节点的 RabbitMQ 是使用哪个用户启动,否则后续很可能由于各节点的 .erlang.cookie 不一致而导致节点无法加入集群。

1
2
3
4
5
# 进入安装目录
# cd /usr/local/rabbitmq/sbin

# 后台启动RabbitMQ服务
# ./rabbitmq-server -detached

在节点二执行以下操作,将节点二(rabbit@rabbitmq2)加入到 RabbitMQ 集群

1
2
3
4
5
6
7
8
9
10
11
# 进入节点二的安装目录
# cd /usr/local/rabbitmq/sbin

# 停止节点二的RabbitMQ的服务
# ./rabbitmqctl -n rabbit@rabbitmq2 stop_app

# 将节点二加入到集群中,"--ram" 表示节点二为内存节点
# ./rabbitmqctl -n rabbit@rabbitmq2 join_cluster rabbit@rabbitmq1 --ram

# 启动节点二的RabbitMQ服务
# ./rabbitmqctl -n rabbit@rabbitmq2 start_app

在节点三执行以下操作,将节点三(rabbit@rabbitmq3)加入到 RabbitMQ 集群

1
2
3
4
5
6
7
8
9
10
11
# 进入节点三的安装目录
# cd /usr/local/rabbitmq/sbin

# 停止节点三的RabbitMQ的服务
# ./rabbitmqctl -n rabbit@rabbitmq3 stop_app

# 将节点三加入到集群中,"--ram" 表示节点三为内存节点
# ./rabbitmqctl -n rabbit@rabbitmq3 join_cluster rabbit@rabbitmq1 --ram

# 启动节点三的RabbitMQ的服务
# ./rabbitmqctl -n rabbit@rabbitmq3 start_app

在任意节点上查看集群的状态

1
2
3
4
5
# 进入安装目录
# cd /usr/local/rabbitmq/sbin

# 查看集群状态
# ./rabbitmqctl cluster_status
  • 搭建集群时,停止 RabbitMQ 服务必须使用 stop_app 命令,而不是 stop 命令,否则无法将节点加入到集群中
  • 默认情况下,RabbitMQ 启动后是磁盘节点,在上面的 join_cluster 命令下,rabbitmq2rabbitmq3 是内存节点,rabbitmq1 是磁盘节点
  • 若要使 rabbitmq2rabbitmq3 都成为磁盘节点,去掉 --ram 参数即可,或者使用 --disc 参数替代
  • 如果想要更改节点类型,可以使用命令 rabbitmqctl change_cluster_node_type disc(ram),前提是必须停掉 RabbitMQ 服务

测试集群节点

若集群节点构建成功,通过浏览器访问任意节点的 Web 控制台,例如 http://192.168.1.109:15672,会看到如下的内容。最后可以在节点一创建队列 test,如果在节点二、节点三的 Web 控制台,也可以看到对应的 test 队列,则说明各集群节点的元数据(队列的结构)同步正常。至此,RabbitMQ 的普通集群搭建完成。

rabbitmq-cluster-1

RabbitMQ 镜像集群搭建

上面已经完成 RabbitMQ 普通集群的搭建,但并不能保证队列的高可用性,尽管交换机、队列、绑定这些可以复制到集群里的任何一个节点,但是队列内容(消息)不会复制。虽然普通集群解决可以一项目组的节点压力,但队列节点(磁盘节点)宕机会直接导致其他节点的队列(内存节点)无法使用,只能等待队列节点(磁盘节点)重启,所以要想在队列节点(磁盘节点)宕机或故障也能正常应用,就要复制队列内容(消息)到集群里的每个节点,因此必须要创建镜像队列。镜像队列是基于普通的集群模式的,然后再添加一些策略,所以还是得先配置普通集群,然后才能设置镜像队列。设置镜像队列可以在 RabbitMQ 的 Web 控制台进行,也可以通过命令,这里介绍是其中的 Web 控制台设置方式。

创建策略

在节点一的 Web 控制台上创建策略:

    1. 点击 Admin 菜单 –> 右侧的 Policies 选项
    1. 按照图中的内容根据自己的需求填写

rabbitmq-cluster-2

  • Name:策略名称
  • Pattern:匹配的规则,^a 表示匹配 a 开头的队列,如果是匹配所有的队列,那就是 ^.
  • Definition:使用 ha-mode 模式中的 all,也就是同步所有匹配的队列
    1. 点击左侧最下边的 Add/update a policy 按钮新增策略

rabbitmq-cluster-3

    1. 此时分别登录节点二、节点三的 Web 控制台,同样可以看到刚添加的这个策略

rabbitmq-cluster-4

rabbitmq-cluster-5

创建队列

在节点一的 Web 控制台上创建队列:

    1. 点击 Queues 菜单
    1. 输入 Name 和 Arguments` 参数的值,别的参数默认即可

rabbitmq-cluster-6

Name:队列名称
Durability:队列是否持久化
Node:消息队列的节点
Auto delete:是否自动删除
Arguments:使用的策略类型

    1. 点击左侧下边的 Add a new queue 按钮新增队列,将鼠标指向 +2 可以显示出另外两台节点

rabbitmq-cluster-7

创建消息

    1. 点击 ab 队列按钮,拖动滚动条
    1. 填写相关内容

rabbitmq-cluster-8

2-Persistent:表示持久化
Headers:随便填写即可
Properties:点击问号,选择一个消息 ID 号
Payload:消息内容

    1. 点击 Publish message 按钮新增消息,可发现 ab 队列的 ReadyTotal 中多了一条消息记录

rabbitmq-cluster-9

验证高可用性

    1. 将节点一的 RabbitMQ 服务关闭,再通过节点一和节点二,查看消息记录是否还存在,结果可以看到在其他节点的消息记录是存在的

rabbitmq-cluster-10

rabbitmq-cluster-11

    1. 再将节点二的 RabbitMQ 服务关闭,通过节点三查看消息记录是否还存在,结果可以看到 ab 队列和消息记录还是存在的,只是变成了只有一个节点

rabbitmq-cluster-12

    1. 将节点一和节点二的 RabbitMQ 服务重启,从中可以看到 ab 队列后面 +2 变成了红色,鼠标指上去显示镜像无法同步

rabbitmq-cluster-13

    1. 采取的解决办法是选择在节点二上执行同步命令
1
2
3
4
5
# 进入节点一的安装目录
# cd /usr/local/rabbitmq/sbin

# 同步特定的队列
# ./rabbitmqctl sync_queue ab

rabbitmq-cluster-14

同步完成后,+2 标识又变成了蓝色,这样就测试了 RabbitMQ 集群的高可用性,说明镜像集群配置成功。

FAQ

.erlang.cookie 是 Erlang 实现分布式集群的必要文件,Erlang 分布式集群要求每个节点上都要有相同的 .erlang.cookie 文件,同时保证文件的权限是 400。在搭建 RabbitMQ 集群的时候往往会因为 .erlang.cookie 而报各种错误,官方在介绍集群的文档中提到过 .erlang.cookie 一般会存在这两个路径:第一个是 ${home}/.erlang.cookie,第二个就是 /var/lib/rabbitmq/.erlang.cookie,具体说明如下:

  • 如果 RPM 等安装包方式进行安装的,那么这个文件会在 /var/lib/rabbitmq 目录下,完整路径为 /var/lib/rabbitmq/.erlang.cookie
  • 如果使用解压缩方式安装部署 RabbitMQ,那么这个文件会在 ${home} 目录下,也就是用户的 Home 目录下,完整路径为 ${home}/.erlang.cookie

通过 RabbitMQ 的启动日志,可以查看其 Home 目录是哪里,就可以知道 .erlang.cookie 存放在哪里,以及 mnesia 数据库信息存在哪里

1
2
3
4
5
6
7
8
# RPM包安装方式
node : rabbit@he10
home dir : /var/lib/rabbitmq
config file(s) : /etc/rabbitmq/rabbitmq.config (not found)
cookie hash : qhOGp9TtH4Rn+BekiYXxIg==
log : /var/log/rabbitmq/rabbit@he07.log
sasl log : /var/log/rabbitmq/rabbit@he07-sasl.log
database dir : /var/lib/rabbitmq/mnesia/rabbit@he07
1
2
3
4
5
6
7
8
# 解压缩安装方式(这里使用Root用户启动)
node : rabbit@he10
home dir : /root
config file(s) : /usr/local/rabbitmq/etc/rabbitmq/rabbitmq.config (not found)
cookie hash : 063Gh+RyPjHRzyuSPf9wWA==
log : /usr/local/rabbitmq/var/log/rabbitmq/rabbit@he10.log
sasl log : /usr/local/rabbitmq/var/log/rabbitmq/rabbit@he10-sasl.log
database dir : /usr/local/rabbitmq/var/lib/rabbitmq/mnesia/rabbit@he10

重新将节点加入集群

这里假设由于各种原因(例如断电重启、节点宕机重启),节点二无法成功加入到集群,那么可以执行以下操作来解决。特别注意,以下操作会删除节点二的元数据(虚拟机、用户、角色、权限、已持久化的消息等),因此当节点二成功加入集群后,必须重新配置节点二的虚拟机、用户、角色、权限等,否则 RabbitMQ 客户端将无法连接节点二。


首先在节点一里,将节点二移出集群

1
2
3
4
5
# 进入节点一的安装目录
# cd /usr/local/rabbitmq/sbin

# 将节点二移出集群
# ./rabbitmqctl -n rabbit@rabbitmq1 forget_cluster_node rabbit@rabbitmq2

然后重置节点二的元数据、集群配置等信息,其中会删除虚拟机、用户、角色、权限、已持久化的消息等元数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 进入节点二的安装目录
# cd /usr/local/rabbitmq/sbin

# 后台启动节点二的RabbitMQ服务
# ./rabbitmq-server -detached

# 停止节点二的RabbitMQ的服务
# ./rabbitmqctl -n rabbit@rabbitmq2 stop_app

# 重置节点二的元数据、集群配置等信息
# ./rabbitmqctl -n rabbit@rabbitmq2 reset

# 重新将节点二加入到集群
# ./rabbitmqctl -n rabbit@rabbitmq2 join_cluster rabbit@rabbitmq1 --ram

# 启动节点二的RabbitMQ服务
# ./rabbitmqctl -n rabbit@rabbitmq2 start_app

如果节点二仍然无法加入集群,可以直接删除节点二的所有数据库文件,然后重启节点二的 RabbitMQ 服务,最后重新将节点二加入到集群

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 进入节点二的安装目录
# cd /usr/local/rabbitmq/sbin

# 彻底关闭节点二的RabbitMQ服务
# ./rabbitmqctl -n rabbit@rabbitmq2 stop

# 删除节点二的数据文件
# rm -rf /usr/local/rabbitmq/var/lib/rabbitmq/mnesia/*

# 后台启动节点二的RabbitMQ服务
# ./rabbitmq-server -detached

# 停止节点二的RabbitMQ的服务
# ./rabbitmqctl -n rabbit@rabbitmq2 stop_app

# 重置节点二的元数据、集群配置等信息
# ./rabbitmqctl -n rabbit@rabbitmq2 reset

# 重新将节点二加入到集群
# ./rabbitmqctl -n rabbit@rabbitmq2 join_cluster rabbit@rabbitmq1 --ram

# 启动节点二的RabbitMQ服务
# ./rabbitmqctl -n rabbit@rabbitmq2 start_app

强制重置节点,force_reset 命令和 reset 的区别是无条件重置节点,不管当前管理数据库状态以及集群的配置,如果数据库或者集群配置发生错误才使用这个最后的手段

1
# ./rabbitmqctl force_reset

RabbitMQ 更改默认端口

RabbitMQ 默认占用 4369、5672、15672、25672 默认端口号,更改默认端口的方法如下:

  • 更改 15672 端口,配置文件路径:/usr/local/rabbitmq/etc/rabbitmq/rabbitmq.config
1
2
3
4
5
6
7
8
9
10
11
12
13
[
{rabbitmq_management,
[
{listener,
[
{port, 15672},
{ip, "0.0.0.0"},
{ssl, false}
]
}
]
}
].
  • 更改 5672、25672 端口,配置文件路径:/usr/local/rabbitmq/etc/rabbitmq/rabbitmq-env.conf
1
2
NODE_PORT=5673
DIST_PORT=25673
  • 更改 4369 端口,配置文件路径:/etc/profile,单机可以多个 RabbitMQ 节点共用同一个 ERL_EPMD_PORT 端口
1
export ERL_EPMD_PORT=4363

RabbitMQ 集群开机自启动 - 方案一

将集群各节点的 RabbitMQ 服务托管给 Systemd 管理,这里以节点一为例子,若其他节点的端口号不相同,默认情况下只需更改服务自启动脚本中对应的端口号即可,该脚本支持单机搭建 RabbitMQ 集群。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建服务自启动脚本
# touch /etc/init.d/rabbitmq-cluster-15672

# 更改服务自启动脚本,写入后面给出的脚本内容
# vim /etc/init.d/rabbitmq-cluster-15672

# 服务自启动脚本授权
# chmod u+x /etc/init.d/rabbitmq-cluster-15672

# 开机自启动
# chkconfig rabbitmq-cluster-15672 on

# 查看开机自启动列表
# chkconfig --list

# 关闭开机自启动
# chkconfig rabbitmq-cluster-15672 off

RabbitMQ 集群各节点的服务管理

1
2
3
4
5
6
7
8
9
10
11
# 关闭服务
# systemctl stop rabbitmq-cluster-15672

# 启动服务
# systemctl start rabbitmq-cluster-15672

# 查看服务状态
# systemctl status rabbitmq-cluster-15672

# 重启服务
# systemctl restart rabbitmq-cluster-15672
★展开服务自启动脚本的完整内容★
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/bin/sh
#
# rabbitmq-cluster-15672 RabbitMQ broker
#
# chkconfig: - 80 05
# description: Enable AMQP service provided by RabbitMQ
#

### BEGIN INIT INFO
# Provides: rabbitmq-cluster-15672
# Required-Start: $remote_fs $network
# Required-Stop: $remote_fs $network
# Description: RabbitMQ broker
# Short-Description: Enable AMQP service provided by RabbitMQ broker
### END INIT INFO

# Source function library.
. /etc/init.d/functions
export HOME=/home/rabbitmq
PATH=/sbin:/usr/sbin:/bin:/usr/bin

USER=rabbitmq
NAME=rabbitmq-server
MQ_HOME=/usr/local/rabbitmq
DAEMON=${MQ_HOME}/sbin/${NAME}
CONTROL=${MQ_HOME}/sbin/rabbitmqctl

ROTATE_SUFFIX=
DESC=rabbitmq-cluster-15672

MAX_OPEN_FILES=1048576
ulimit -n $MAX_OPEN_FILES

START_PROG="daemon"
PID_FILE=/var/run/rabbitmq/pid-15672
LOCK_FILE=/var/lock/subsys/$NAME-15672
INIT_LOG_DIR=${MQ_HOME}/var/log/rabbitmq

test -x $DAEMON || exit 0
test -x $CONTROL || exit 0

RETVAL=0
set -e

[ -f /etc/default/${NAME} ] && . /etc/default/${NAME}

ensure_pid_dir () {
PID_DIR=`dirname ${PID_FILE}`
if [ ! -d ${PID_DIR} ] ; then
mkdir -p ${PID_DIR}
chown -R ${USER}:${USER} ${PID_DIR}
chmod 755 ${PID_DIR}
fi
}

remove_pid () {
rm -f ${PID_FILE}
rmdir `dirname ${PID_FILE}` || :
}

start_rabbitmq () {
status_rabbitmq quiet
if [ $RETVAL = 0 ] ; then
echo RabbitMQ is currently running
else
RETVAL=0
ensure_pid_dir
set +e
RABBITMQ_PID_FILE=$PID_FILE $START_PROG $DAEMON \
> "${INIT_LOG_DIR}/startup_log" \
2> "${INIT_LOG_DIR}/startup_err" \
0<&- &
$CONTROL wait $PID_FILE >/dev/null 2>&1
RETVAL=$?
set -e
case "$RETVAL" in
0)
echo SUCCESS
if [ -n "$LOCK_FILE" ] ; then
touch $LOCK_FILE
fi
;;
*)
remove_pid
echo FAILED - check ${INIT_LOG_DIR}/startup_\{log, _err\}
RETVAL=1
;;
esac
fi
}

stop_rabbitmq () {
status_rabbitmq quiet
if [ $RETVAL = 0 ] ; then
set +e
$CONTROL stop ${PID_FILE} > ${INIT_LOG_DIR}/shutdown_log 2> ${INIT_LOG_DIR}/shutdown_err
RETVAL=$?
set -e
if [ $RETVAL = 0 ] ; then
remove_pid
if [ -n "$LOCK_FILE" ] ; then
rm -f $LOCK_FILE
fi
else
echo FAILED - check ${INIT_LOG_DIR}/shutdown_log, _err
fi
else
echo RabbitMQ is not running
RETVAL=0
fi
}

status_rabbitmq() {
set +e
if [ "$1" != "quiet" ] ; then
$CONTROL status 2>&1
else
$CONTROL status > /dev/null 2>&1
fi
if [ $? != 0 ] ; then
RETVAL=3
fi
set -e
}

rotate_logs_rabbitmq() {
set +e
$CONTROL rotate_logs ${ROTATE_SUFFIX}
if [ $? != 0 ] ; then
RETVAL=1
fi
set -e
}

restart_running_rabbitmq () {
status_rabbitmq quiet
if [ $RETVAL = 0 ] ; then
restart_rabbitmq
else
echo RabbitMQ is not runnning
RETVAL=0
fi
}

restart_rabbitmq() {
stop_rabbitmq
start_rabbitmq
}

case "$1" in
start)
echo -n "Starting $DESC: "
start_rabbitmq
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
stop_rabbitmq
echo "$NAME."
;;
status)
status_rabbitmq
;;
rotate-logs)
echo -n "Rotating log files for $DESC: "
rotate_logs_rabbitmq
;;
force-reload|reload|restart)
echo -n "Restarting $DESC: "
restart_rabbitmq
echo "$NAME."
;;
try-restart)
echo -n "Restarting $DESC: "
restart_running_rabbitmq
echo "$NAME."
;;
*)
echo "Usage: $0 {start|stop|status|rotate-logs|restart|condrestart|try-restart|reload|force-reload}" >&2
RETVAL=1
;;
esac

exit $RETVAL

RabbitMQ 集群开机自启动-方案二

严格来说 RabbitMQ 并不适用使用 Supervior 来管理服务,因为当手动 Kill 掉 RabbitMQ 的进程时,Supervior 无法正常重启 RabbitMQ 的进程,具体原因可以看这里,但若只是简单实现 RabbitMQ 开机自启动,Supervior 无疑是可以胜任的。


使用 Supervior 托管管理 RabbitMQ 的服务,以节点一为例给出下述配置示例,其他节点只需更改对应的端口号即可。值得一提的是,这里必须指定 environment=HOME=/home/rabbitmq,否则 RabbitMQ 会找不到 .erlang.cookie 而导致启动失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[program:rabbitmq]
environment=HOME=/home/rabbitmq
directory=/usr/local/rabbitmq
command=/usr/local/rabbitmq/sbin/rabbitmq-server
user=rabbitmq
numprocs=1
autostart=true
autorestart=true
startretries=10
process_name=%(program_name)s
stdout_logfile_backups=5
stdout_logfile_maxbytes=10MB
stdout_logfile=/var/log/supervisor/rabbitmq.log
stderr_logfile_backups=5
stderr_logfile_maxbytes=10MB
stderr_logfile=/var/log/supervisor/rabbitmq-error.log

以节点一为例通过 Supervisor 管理 RabbitMQ 服务,其他节点不再累述

1
2
3
4
5
6
7
8
9
10
11
# 关闭服务
# supervisorctl stop rabbitmq

# 启动服务
# supervisorctl start rabbitmq

# 查看服务状态
# supervisorctl status rabbitmq

# 重启服务
# supervisorctl restart rabbitmq

RabbitMQ 无法操作集群节点

若执行以下命令出现下述的错误,一般是当前执行操作的用户的家目录下的 .erlang.cookie 与 集群节点的 .erlang.cookie 不一致导致。解决办法是集群节点是以哪个用户启动的,就切换到对应的用户,例如 su rabbitmq ,然后再执行集群操作命令。

1
2
# 查看集群状态
# ./rabbitmqctl cluster_status

执行集群状态查看命令,出现以下错误信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Error: unable to perform an operation on node 'rabbit2@rabbitmq2'. Please see diagnostics information and suggestions below.

Most common reasons for this are:

* Target node is unreachable (e.g. due to hostname resolution, TCP connection or firewall issues)
* CLI tool fails to authenticate with the server (e.g. due to CLI tool's Erlang cookie not matching that of the server)
* Target node is not running

In addition to the diagnostics info below:

* See the CLI, clustering and networking guides on https://rabbitmq.com/documentation.html to learn more
* Consult server logs on node rabbit2@rabbitmq2
* If target node is configured to use long node names, don't forget to use --longnames with CLI tools

DIAGNOSTICS
===========

attempted to contact: [rabbit2@rabbitmq2]

rabbit2@rabbitmq2:
* connected to epmd (port 4369) on rabbitmq2
* epmd reports node 'rabbit2' uses port 25674 for inter-node and CLI tool traffic
* TCP connection succeeded but Erlang distribution failed

* Authentication failed (rejected by the remote node), please check the Erlang cookie


Current node details:
* node name: 'rabbitmqcli-7936-rabbit2@rabbitmq2'
* effective user's home directory: /home/centos
* Erlang cookie hash: 5hmDFFQNoU5sdfrafENxAg==

RabbitMQ 集群配置文件概述

这里以节点一为例,各个配置文件的路径如下,其他节点不再累述

1
2
3
4
5
6
安装目录:/usr/local/rabbitmq
日志目录:/usr/local/rabbitmq/var/log/rabbitmq
数据目录:/usr/local/rabbitmq/var/lib/rabbitmq/mnesia
配置文件:/usr/local/rabbitmq/etc/rabbitmq/rabbitmq.config
环境变量配置文件:/usr/local/rabbitmq/etc/rabbitmq/rabbitmq-env.conf
服务自启动脚本:/etc/init.d/rabbitmq-cluster-15672

参考博客