RabbitMQ高可用集群搭建

RabbitMQ + HAProxy 高可用镜像模式集群部署

  • 为什么搭建 RabbitMQ 集群?
  • RabbitMQ 集群模式有哪些?
  • 如何搭建 RabbitMQ 集群?
  • RabbitMQ 镜像模式的策略如何配置?

RabbitMQ高可用集群搭建

部署说明

下面将以一个示例说明多机部署一个高可用 RabbitMQ 集群的流程。

前提

在部署集群前,必须在将成为集群成员的每个节点上安装 RabbitMQ,并确保每个节点之间能相互访问。

RabbitMQ 集群模式

  • 普通模式(默认的集群模式)

    以两个节点 node1、node2 为例来进行说明。

    对于 queue 来说,消息实体只存在于其中一个节点(node1 或 node2),node1 和 node2 两个节点仅有相同的元数据,即队列的结构。当消息进入 node1 节点的 queue 后,consumer 从 node2 节点消费时,RabbitMQ 会临时在 node1、node2 间进行消息传输,把 node1 中的消息实体取出并经过 node2 发送给 consumer。所以 consumer 应尽量连接每一个节点,从中取消息。即对于同一个逻辑队列,要在多个节点建立物理 queue。否则无论 consumer 连接 node1 或 node2,出口总在 node1,会产生瓶颈。

    当 node1 节点故障后,node2 节点无法取到 node1 节点中还未消费的消息实体。如果做了消息持久化,那么得等 node1 节点恢复,然后才可被消费;如果没有持久化的话,就会产生消息丢失的现象。

  • 镜像模式

    把需要的队列做成镜像队列,存在于多个节点,属于 RabbiMQ 的 HA 方案,在对业务可靠性要求较高的场合中比较适用。

    该模式解决了普通模式中的问题,其实质和普通模式不同之处在于,消息实体会主动在镜像节点间同步,而不是在客户端取数据时临时拉取。该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。所以在对可靠性要求较高的场合中适用。

    要实现镜像模式,需要先搭建一个普通集群模式,在这个模式的基础上再配置策略以实现高可用。

集群节点类型

  • 内存(ram)节点
  • 磁盘(disk)节点

RAM 节点仅将内部数据库表存储在 RAM 中。这不包括消息,消息存储索引,队列索引和其他节点状态。

在大多数情况下,您希望所有节点都是磁盘节点。 RAM 节点是一种特殊情况,可用于提高 queue、exchange 或 binding 流失率较高的群集的性能。 RAM 节点不提供更高的消息速率。如有疑问,请仅使用磁盘节点。

由于 RAM 节点仅将内部数据库表存储在RAM中,因此它们必须在启动时从对等节点同步它们。这意味着一个群集必须至少包含一个磁盘节点。因此,不可能手动删除集群中最后剩余的磁盘节点。

在 RabbitMQ 集群中,当磁盘节点宕掉且集群中无其他可用的磁盘节点时,集群将无法写入新的队列元数据信息。

环境准备

  • 系统系统:CentOS7 64位

  • 三台服务器:192.168.0.231/232/233

  • 服务器规划

    服务器 用途 主机名 节点类型
    192.168.0.231 RabbitMQ 集群节点 1 node231 磁盘节点
    192.168.0.232 RabbitMQ 集群节点 2 node232 磁盘节点
    192.168.0.233 RabbitMQ 集群节点 3 node233 磁盘节点

集群搭建

普通模式

配置 hosts

vim /etc/hosts 编辑三个节点的 hosts 文件,在文件末尾添加如下内容:

1
2
3
192.168.0.231 node231
192.168.0.232 node232
192.168.0.233 node233

配置 hostname

  • node231

    vim /etc/hostname 编辑主机名如下:

    1
    node231

    vim /etc/sysconfig/network 编辑网络配置文件,添加如下内容:

    1
    2
    NETWORKING=yes
    HOSTNAME=node231

    重启 network

    1
    systemctl restart network
  • node232

    vim /etc/hostname 编辑主机名如下:

    1
    node232

    vim /etc/sysconfig/network 编辑网络配置文件,添加如下内容:

    1
    2
    NETWORKING=yes
    HOSTNAME=node232

    重启 network

    1
    systemctl restart network
  • node233

    vim /etc/hostname 编辑主机名如下:

    1
    node233

    vim /etc/sysconfig/network 编辑网络配置文件,添加如下内容:

    1
    2
    NETWORKING=yes
    HOSTNAME=node233

    重启 network

    1
    systemctl restart network

RabbitMQ 集群是基于 erlang 进行同步的,在 erlang 的集群中各节点同步需要一个相同的 cookie,所以必须保证各节点 cookie 一致,不然节点之间就无法通信。这个 cookie 默认存放在 /var/lib/rabbitmq/.erlang.cookie 中。

在任意一个节点中 copy .erlang.cookie 文件到其它所有节点,如在 node1 上进行 copy :

1
2
[root@node231 ~]# scp /var/lib/rabbitmq/.erlang.cookie root@192.168.0.232:/var/lib/rabbitmq/
[root@node231 ~]# scp /var/lib/rabbitmq/.erlang.cookie root@192.168.0.233:/var/lib/rabbitmq/

重启节点

如果后面执行 rabbitmqctl stop_app 失败,需要重启 node231、node232、node233 使配置生效。

启动 rabbitmq-server

分别启动 node231、node232、node233 的 rabbitmq-server:

1
2
3
[root@node231 ~]# systemctl start rabbitmq-server
[root@node232 ~]# systemctl start rabbitmq-server
[root@node233 ~]# systemctl start rabbitmq-server

将节点加入集群

将 node232、node233 节点加入 node231 节点集群中,在 node232、node233 中分别执行以下命令:

1
2
3
# rabbitmqctl stop_app
# rabbitmqctl join_cluster rabbit@node231
# rabbitmqctl start_app
  • 默认 RabbitMQ 启动后是磁盘节点,在这个 cluster 下,node231、node232 和 node233 都是是磁盘节点。

  • 如果要使 node232、node233 都是内存节点,加上 --ram 参数即可,如 rabbitmqctl join_cluster --ram rabbit@node232

  • 如果想要更改节点类型,可以使用命令 rabbitmqctl change_cluster_node_type disc(ram),修改节点类型前需要先 rabbitmqctl stop_app

    (Note: disk and disc are used interchangeably)

查看集群状态

任意节点执行:

1
# rabbitmqctl cluster_status

创建管理用户

如果在主机名变更前就已经创建过用户的,仍需要重新重新创建,因为主机名的变更,之前创建的用户无法登录 web 管理系统。

以下操作在 node231 下执行:

  • 创建 vhost(可选,默认使用 "/" vhost)

    这里创建一个 vhost 用于测试:

    1
    [root@node231 ~]# rabbitmqctl add_vhost testvhost
  • 创建用户

    1
    [root@node231 ~]# add_user admin password
  • 设置用户角色

    1
    [root@node231 ~]# set_user_tags admin administrator
  • 设置用户权限

    1
    [root@node231 ~]# set_permissions -p testvhost admin ".*" ".*" ".*"

启用 rabbitmq management

在 node231 上启用 rabbitmq management

1
[root@node231 ~]# rabbitmq-plugins enable rabbitmq_management

在浏览器中访问 http://192.168.0.231:15672,使用 "admin/password" 即可登录。

web-management

镜像模式

上面已经完成 RabbitMQ 默认集群模式,但并不保证队列的高可用性,尽管 Exchanges、Bindings 这些可以复制到集群里的任何一个节点,但是队列内容不会复制。所以集群中的节点宕机后将直接导致队列无法应用或消息丢失,要想队列在节点宕机或故障时也能正常应用,需要复制队列内容到集群中的每个节点,这就要使用镜像队列了。

镜像队列

默认情况下,RabbitMQ 集群中 queue 的内容位于单个节点(声明该 queue 的节点)上。这与 exchanges 和 bindings 相反,exchanges 和 bindings 始终可以被视为在所有节点上。可以选择使 queue 跨多个节点进行镜像。

每个镜像队列由一个 master 和一个或多个镜像(mirrors)组成。master 托管在一个通常称为主节点的节点上。每个队列都有其自己的主节点。给定队列的所有操作都首先应用于队列的主节点,然后传播到镜像节点。这涉及排队发布,向消费者传递消息,跟踪来自消费者的确认等。

队列镜像意味着节点的集群。发布到队列的消息将复制到所有镜像。无论消费者连接到哪个节点,最终都会被连接到主节点,镜像节点都会丢弃已在主节点上确认的消息。因此,队列镜像可提高可用性,但不会在节点之间分配负载(所有参与的节点均完成所有工作)。

如果承载队列主节点发生故障,则最早的镜像将在同步后提升为新的主节点。根据队列镜像参数,也可以升级不同步的镜像。

配置镜像策略

使用策略(policiy)配置镜像参数。 一个策略按名称(使用正则表达式模式)匹配一个或多个队列,并且包含一个定义(可选参数的映射),该定义被添加到匹配队列的全部属性中。

通过控制台添加策略

add-policy

如果其他节点也启用了 rabbitmq_management,此时其他节点的控制台,可以看到上面添加的这个策略,如图所示:

show-policies

参数说明:

  • Virtual host:策略应用的 vhost。

  • Name:为策略名称,可以是任何东西,但建议使用不带空格的基于ASCII的名称。

  • Pattern:与一个或多个 queue(exchange) 名称匹配的正则表达式,可以使用任何正则表达式。只有一个 ^ 代表匹配所有,^test 为匹配名称为 "test" 的 exchanges 或者 queue。

  • Apply to:Pattern 应用对象。

  • Priority:配置了多个策略时候的优先级,值越大,优先级越高。

    (没有指定优先级的消息会以0优先级对待。对于超过队列所定最大优先级的消息,优先级以最大优先级对待)

  • Definition:一组键/值对(例如 JSON 文档),将被插入匹配 queues and exchanges 的可选参数映射中

    ha-mode:策略键,分为3种模式

    • all - 所有(所有的 queue)
    • exctly - 部分(需配置 ha-params 参数,此参数为 int 类型。比如 3,众多集群中的随机 3 台机器)
    • nodes - 指定(需配置 ha-params 参数,此参数为数组类型。比如 ["rabbit@node2", "rabbit@node3"] 这样指定为 node2 与 node3 这两台机器)

    ha-sync-mode:队列同步

    • manual:手动(默认模式)。新的队列镜像将不会收到现有的消息,它只会接收新的消息
    • automatic:自动同步。当一个新镜像加入时,队列会自动同步。队列同步是一个阻塞操作。
通过命令行添加策略
  • 设置

    1
    rabbitmqctl set_policy [-p <vhost>] [--priority <priority>] [--apply-to <apply-to>] <name> <pattern>  <definition>
  • 清除

    1
    rabbitmqctl clear_policy [-p <vhost>] <name>
  • 查看

    1
    rabbitmqctl list_policies [-p <vhost>]

例如:

1
2
# rabbitmqctl set_policy -p testvhost testha "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
Setting policy "testha" for pattern "^" to "{"ha-mode":"all","ha-sync-mode":"automatic"}" with priority "0" for vhost "testvhost" ...
1
2
3
4
# rabbitmqctl list_policies -p testvhost
Listing policies for vhost "testvhost" ...
vhost name pattern apply-to definition priority
testvhost testha ^ all {"ha-mode":"all","ha-sync-mode":"automatic"} 0
1
2
# rabbitmqctl clear_policy -p testvhost testha
Clearing policy "testha" on vhost "testvhost" ...

测试策略是否生效

在控制台的队列页面上,镜像队列将展示策略名称和其他副本(镜像)数量。以下是一个名为 three_replicas 的队列的示例,该队列具有一个 master(主节点)和两个镜像节点。

添加队列:

add-queue

查看队列:

show-queues

queue-detail

HAProxy 负载均衡

HAProxy 是由 C 语言编写的免费的开源软件,它快速而高效,可为基于TCP和HTTP的应用程序提供高可用、高性能的负载平衡器和代理服务器。

环境准备

服务器 用途 系统 版本
192.168.0.235 负载均衡服务器 Ubuntu 20.04 haproxy 2.0

安装

1
$ sudo apt install haproxy

配置

vim /etc/haproxy/haproxy.cfg 编辑 haproxy 配置文件,修改如下:

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
global
maxconn 512
stats socket /tmp/haproxy

defaults
log global
mode http
option abortonclose
compression algo gzip
compression type text/html text/plain application/json
timeout connect 5000ms

listen stats
bind *:8888
mode http
log 127.0.0.1 local3 err
stats refresh 60s
stats uri /stats
stats realm Haproxy\ Manager
stats auth admin:password
stats hide-version
stats admin if TRUE

listen rabbitmq_cluster
bind *:5672
mode tcp
option tcpka
balance roundrobin
server rabbit1 192.168.0.231:5672 check inter 1000 rise 2 fall 3 weight 1
server rabbit2 192.168.0.232:5672 check inter 1000 rise 2 fall 3 weight 1
server rabbit3 192.168.0.233:5672 check inter 1000 rise 2 fall 3 weight 1

验证 HAProxy 配置

修改配置后,在启动 HAProxy 前,应先运行以下命令验证配置文件语法:

1
2
$ haproxy -f /etc/haproxy/haproxy.cfg -c -V
Configuration file is valid

如果收到错误消,请务必先修复,然后再继续。

运行 HAProxy

1
$ systemctl restart haproxy

查看状态:

1
$ systemctl status haproxy

HAProxy Statistics

浏览器访问 http://192.168.0.235:8888/stats,输入配置中的用户名和密码登录

haproxy-statistics

References

https://www.rabbitmq.com/clustering.html

https://www.rabbitmq.com/ha.html

https://www.rabbitmq.com/parameters.html#policies

http://www.haproxy.org/