注:最近项目上与MQ相关的问题层出不断,而组内人员缺乏对RabbitMQ本身及OpenStack如何使用RabbitMQ的了解,在遇到问题时一筹莫展,只能使用运维三大法宝:重启、清空、重装。本着追根求源的问题分析态度,需要对MQ本身及相关用法做深入分析,以避免此类问题发生。以下是基于RabbitMQ官网介绍的MQ 集群相关的介绍,下一章会详细介绍集群的高可用机制。
参考:https://www.rabbitmq.com/clustering.html#cluster-formation
一、集群构建
1. 构建集群的方法
(1)通过在配置文件中配置集群节点
(2)使用DNS发现方式
(3)使用AWS EC2实例发现方式
(4)使用K8S发现方式
(5)使用consul发现方式
(6)使用ETCD方式
(7)手动方式:rabbitmqctl
可以动态更改集群的运行方式。所有RabbitMQ节点开始时都在单个节点上运行。 这些节点可以连接成集群,然后再次回归到单个节点。
2. 构建集群的要求
RabbitMQ节点使用域名进行寻址交互,因此所有节点的域名必须在所有RabbitMQ节点及使用rabbitmqctl命令的节点可以被正常解析。
域名解析可以使用操作系统支持的标准方式:
(1)DNS
(2)hosts文件
在一些限制比较强的环境,不允许修改hosts文件及DNS。可以配置erlang vm使用其他方式进行域名解析,比如其他的DNS Server,本地文件(非标准hosts位置),或一些组合方法。
3. 端口访问
RabbitMQ使用到的端口
5672, 5671: AMQP规范0.9.1及1.0的客户端使用的端口,5672为非TLS场景,5671为TLS场景
4369:empd,RabbitMQ节点及其他cli工具使用到的对等发现服务
25672: 用于RabbitMQ节点间和CLI工具通信(Erlang分布式服务端端口),在一个动态范围内分配(默认单端口是AMQP端口+20000)。一般不公开这个端口。
35672-35682: 由CLI工具(Erlang分布式客户端端口)用于节点通信,在范围内动态分配(Erlang分布式服务端端口+10000 - 10010)
15672:HTTP API, 用于管理页面及rabbitmqadmin命令,仅当启用管理插件时才启用
61613, 61614:没有和使用TLS的STOMP客户端(仅当启用了STOMP插件时,该插件支持浏览器通过websocket协议访问mq)
1883,8883 : 没有和使用TLS的MQTT客户端 (仅当启用了MQTT插件, 该插件提供消息队列遥测传输协议支持)
15674:STOMP-over-WebSockets客户端(仅当启用了Web STOMP插件时)
15675:MQTT-over-WebSockets客户端(仅当启用了Web MQTT插件时)
可以将RabbitMQ配置为使用不同的端口和特定的网络接口。
二、集群中的节点
1. 什么是复制
RabbitMQ broker操作所需的数据及状态在所有rabbitmq节点同步。尽管消息队列是对所有节点可见及可达,但本质上是绑定在一个节点上。也可以在集群中配置跨节点同步的队列。
2. 节点对等关系
大多数分布式系统,都具备master/slave角色。但对RabbitMQ集群来说,没有特殊角色的节点存在,各个节点是对等的关系。在考虑镜像队列及插件时,可能会有部分不同,但大部分都是对等关系。
任意节点都可以执行CLI操作,HTTP API客户端可以连接集群任意一个节点。
各个插件可以选择或指定某一时间段在指定节点上运行,例如:federation连接位于集群指定节点上,当该节点发送故障后,连接会在集群其他节点上重新启动。
在3.6.7之前的版本中,管理插件使用专用节点进行信息收集。
3. 集群鉴权,Erlang Cookie
鉴权包含2部分:CLI工具到集群内节点;集群节点之间
RabbitMQ集群及CLI工具使用cookie来识别相互之间是否有访问权限。两个节点之间要通信,他们必须拥有相同的erlang cookie。cookie是一段最大255个字符的字符串,通常在本地存储,只允许owner访问。集群中所有节点必须有相同的cookie
当erlang cookie不存在的时候,RabbitMQ会在启动时自动创建一个包含随机字符串的cookie,只有在开发环境中才适合使用这种自动生成的cookie。因为每个节点都会生成自己的cookie,因此该策略在集群环境中不适用。erlang cookie应该在部署阶段完成,理想情况下用自动化和编排工具,如Chef,Puppet,BOSH,Docker或类似工具。
在UNIX系统上,cookie通常位于/var/lib/rabbitmq/.erlang.cookie(由服务器使用)或$HOME / .erlang.cookie(由CLI工具使用)中。 请注意,由于$HOME
的值因用户而异,因此需要为使用CLI工具的每个用户放置cookie文件副本。 适用于所有非特权用户和root。
当cookie漏配的时候,rabbitmq会记录类似”Connection attempt from disallowed node“及”Could not auto-cluster“的日志。当CLI工具,如rabbitmqctl与RabbitMQ集群之间认证错误时,通常报如下错误:
* epmd reports node 'rabbit' running on port 25672
* TCP connection succeeded but Erlang distribution failed
* suggestion: hostname mismatch?
* suggestion: is the cookie set correctly?
* suggestion: is the Erlang distribution using TLS?
cookie位置错误或cookie值不一致,是比较常见的错误。当使用最近的Erlang或OTP版本时,认证失败错误包含的信息更详细,可以方便的识别cookie不匹配错误。
* connected to epmd (port 4369) on warp10
* epmd reports node 'rabbit' running on port 25672
* TCP connection succeeded but Erlang distribution failed
* Authentication failed (rejected by the remote node), please check the Erlang cookie
4. 集群和客户端
假设集群所有节点都可用,客户端可以连接集群任意一个节点并进行任意操作。节点会将操作路由到队列主节点。不管是哪一种消息协议,每一个客户端都只连接一个节点。
为了避免单点问题,客户端应具备重连机制,重连到正常节点、更新客户端的集群拓扑并继续进行操作。基于这个原因,大多数客户端都支持配置多个endpoint。
在某些特殊情况下,客户端重连后,无法透明的继续其他操作。
5. 集群和监控
客户端连接,channel以及queue会分散到集群的不同节点上。管理员需要有能够检查和监控所有此类资源。
RabbitMQ工具,如rabbitmq-diagnostics及rabbitmqctl 提供了检查和监控这些资源的命令。有些命令侧重于单节点状态,如rabbitmq-diagnostics environment and rabbitmq-diagnostics status,其他的关注在集群层面的状态。例如:rabbitmqctl list_connections, rabbitmqctl list_mqtt_connections, rabbitmqctl list_stomp_connections, rabbitmqctl list_users, rabbitmqctl list_vhosts
这些集群层面的命令,一般会首先与集群中一个节点交互,发现集群成员并进行连接来获取所有成员状态。如rabbitmqctl list_connections会连接所有节点,检索所有amqp 0.9.0级1.0版本的连接,并展示给用户。用户不需要单独同每个节点交互。在不同节点执行命令获取到的结果是一致的。如果是只获取当前节点状态的命令,则不会产生同样的结果。管理页面也是类似原理。
6. 节点错误处理
RabbitMQ集群节点具备容错机制。只要这个节点同集群中其他节点的联系正常,这个节点可以被随意启停。
队列镜像机制允许队列内容在集群不同节点之间同步。非镜像队列的表现,依赖于queue持久化配置。
RabbitMQ集群有几种模式来处理出现网络分区的情况,以保障集群的一致性。不建议跨WAN运行。
7. 度量和统计
每个节点都会存储当前节点的度量和统计,并提供API来供其他节点调用获取度量及统计。当客户端获取集群度量及统计时,会通过调用节点汇总各个节点的度量及统计,并返回给客户端。
8. 磁盘和内存节点
一个节点可以是磁盘类型或是内存类型。内存类型的节点只会在RAM中存储内部数据库,不包括消息、消息存储索引、队列索引及其他节点状态。在大多数情况下,所有节点均是磁盘类型,内存类型只是个别情况,可用于改进具有高队列,交换或绑定流失的性能群集。内存类型节点并不能提高消息速率。
由于内存类型节点只存储内部数据库,因此启动时必须从其他节点同步。这就要求集群中至少有一个磁盘类型节点。
三、操作命令实例
1. 集群部署
以三个节点为例,三个节点分别为rabbit1, rabbit2, rabbit3
(1)分别在三个节点执行
rabbitmq-server -detached
(2)在rabbit2,rabbit3上分别执行
rabbitmqctl stop_app
rabbitmqctl reset #会清理当前节点所有资源及数据
rabbitmqctl join_cluster rabbit@rabbit1
rabbitmqctl start_app
rabbitmqctl cluster_status
2. 集群重启
(1) 关闭节点
rabbitmqctl stop
(2) 启动节点,在3个节点分别执行
rabbitmq-server -detached
当集群中一个节点重新启动时,会自动连接集群中其他节点,默认情况下会重连10次,30s超时时间(最多5分钟)。如果在重试及超时时间内,集群中有存活节点,那么是可以启动成功并进行数据或状态同步的;如果没有存活节点,则启动失败。
以上说明意味着在5分钟内,可以以任意顺序重启集群内节点。
3. 拆分集群
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
4. 单物理节点集群
确保每个rabbitmq server独立的hostname,日志,配置等
以下命令可以在一个物理节点上启动2个rabbitmq-server
RABBITMQ_NODE_PORT=5672 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15672}]" RABBITMQ_NODENAME=rabbit rabbitmq-server -detached
RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15673}]" RABBITMQ_NODENAME=hare rabbitmq-server -detached
5. 主机名变更
RabbitMQ的数据库路径是基于hostname的,如果hostname发生了变化,那么会重新创建一个新的database。为了避免数据丢失,在变更houstname后,需要重启rabbitmq服务。
6. 防火墙节点
在集群被防火墙隔开的情况下,需要打开第一节中对应端口。
7. Erlang 版本
集群内所有节点Erlang版本必须一致。19.3.4和19.3.6可以混合部署,19.0.1和19.3.6(或17.5和19.3.6)不能混部。
8.从客户端连接集群
以下摘自官网描述:
A client can connect as normal to any node within a cluster. If that node should fail, and the rest of the cluster survives, then the client should notice the closed connection, and should be able to reconnect to some surviving member of the cluster. Generally, it's not advisable to bake in node hostnames or IP addresses into client applications: this introduces inflexibility and will require client applications to be edited, recompiled and redeployed should the configuration of the cluster change or the number of nodes in the cluster change. Instead, we recommend a more abstracted approach: this could be a dynamic DNS service which has a very short TTL configuration, or a plain TCP load balancer, or some sort of mobile IP achieved with pacemaker or similar technologies. In general, this aspect of managing the connection to nodes within a cluster is beyond the scope of RabbitMQ itself, and we recommend the use of other technologies designed specifically to solve these problems.
客户端可以配置多个rabbitmq server,当某个fail时,客户端发现并自动连其他节点。一般不建议配置多个ip的情况,这样当mq集群发生变化时,需要修改所有客户端配置。建议通过动态DNS,或负载均衡的方式实现mq节点的访问高可用。
9. 包含RAM节点的集群
集群中的内存节点,会将metadata放到内存中,不用像disk节点一样向disk中写入很多数据,所以性能会更好。但这种性能体现在资源管理上,在消息发布及消费上没有提升。
仅包含RAM节点的集群是极其脆弱的,如果所有节点Down掉,则无法再次拉起。所以RabbitMQ会阻止所有节点全是RAM节点的情况发生。
(1)向集群添加RAM节点
rabbitmqctl stop_app
rabbitmqctl join_cluster --ram rabbit@rabbit1
rabbitmqctl start_app
(2)更换节点类型
rabbitmqctl stop_app
rabbitmqctl change_cluster_node_type disc
rabbitmqctl start_app