[32] RabbitMQ-集群与运维

1. 集群方案原理#

对于无状态应用(如普通的微服务)很容易实现负载均衡、高可用集群。而对于有状态的系统(如数据库等)就比较复杂。

1.1 业界实践#

  • 主备模式:单活,容量对等,可以实现故障转移。使用独立存储时需要借助复制、镜像同步等技术,数据会有延迟、不一致等问题(CAP 定律),使用共享存储时就不会有状态同步这个问题。
  • 主从模式:一定程度的双活,容量对等,最常见的是读写分离。通常也需要借助复制技术,或者要求上游实现双写来保证节点数据一致。
  • 主主模式:两边都可以读写,互为主备。如果两边同时写入很容易冲突,所以通常实现的都是“伪主主模式”,或者说就是主从模式的升级版,只是新增了主从节点的选举和切换。
  • 分片集群:不同节点保存不同的数据,上游应用或者代理节点做路由,突破存储容量限制,分摊读写负载;典型的如 MongoDB 的分片、MySQL 的分库分表、Redis 集群。
  • 异地多活:“两地三中心”是金融行业经典的容灾模式(有资源闲置的问题),“异地多活”才是王道。

1.2 常用负载均衡算法#

  1. 随机
  2. 轮询
  3. 加权轮询
  4. 最少活跃连接
  5. 原地址/目标地址 hash(一致性 hash)

1.3 集群经典问题#

  1. 脑裂(可以通过协调器选举算法、仲裁节点等方式来解决)
  2. 网络分区、一致性、可用性(CAP)

相关景技术和工具:LVS、HAProxy、Nginx、KeepAlived、Heartbeat、DRBD、Corosync、Pacemaker、MMM/MHA、Galera、MGR 等,感兴趣的同学可以研究。

现在太多公司选择直接购买公有云服务,基本不用太关心很多基础设施和中间件的部署、运维细节。但是这些技术以及背后的原理是非常重要的。

1.4 分布式架构模式#

a. 主备模式#

也叫 Warren(兔子窝)模式,同一时刻只有一个节点在工作(备份节点不能读写),当主节点发生故障后会将请求切换到备份节点上(主恢复后成为备份节点)。需要借助 HAProxy 之类的(VIP 模式)负载均衡器来做健康检查和主备切换,底层需要借助共享存储(如 SAN 设备)。

这不是 RabbitMQ 官方或者开源社区推荐方案,适用于访问压力不是特别大但是又有高可用架构需求(故障切换)的中小规模的系统来使用。首先有一个节点闲置,本身就是资源浪费,其次共享存储往往需要借助硬件存储,或者分布式文件系统。

b. 铲子模式#

Shovel 是一个插件,用于实现跨机房数据复制,或者数据迁移,故障转移与恢复等。

如下图,用户下单的消费先是投递在 Goleta Broker 实例中,当 Goleta 实例达到触发条件后(例如:消息堆积数达到阈值)会将消息放到 Goleta 实例的 backup_orders 备份队列中,并通过 Shovel 插件从 Goleta 的 backup_orders 队列中将消息拉取到 Carpinteria 实例存储。

使用 Shovel 插件后,模型变成了近端同步确认、远端异步确认的方式。

此模式支持 WAN 传输,并且 Broker 实例的 RabbitMQ、Erlang 版本不要求完全一致。

Shovel 的配置分静态模式(修改 RabbitMQ 配置)和动态模式(在控制台直接部署,重启后失效)。

c. RabbitMQ 集群#

RabbitMQ 集群允许消费者和生产者在 RabbitMQ 单个节点崩溃的情况下继续运行,并可以通过添加更多的节点来线性扩展消息通信的吞吐量。当失去一个 RabbitMQ 节点时,客户端能够重新连接到集群中的任何其他节点并继续生产和消费。

RabbitMQ 集群中的所有节点都会备份所有的元数据信息,包括:

  1. 队列元数据:队列的名称及属性
  2. 交换器:交换器的名称及属性
  3. 绑定关系元数据:交换器与队列或者交换器与交换器之间的绑定关系
  4. vhost 元数据:为 vhost 内的队列、交换器和绑定提供命名空间及安全属性

基于存储空间和性能的考虑,RabbitMQ 集群中的各节点存储的消息是不同的(有点儿类似分片集群,各节点数据并不是全量对等的),各节点之间同步备份的仅仅是上述元数据以及 Queue Owner(队列所有者,就是实际创建 Queue 并保存消息数据的节点)的指针

当集群中某个节点崩溃后,该节点的队列进程和关联的绑定都会消失,关联的消费者也会丢失订阅信息,节点恢复后(前提是消息有持久化)消息可以重新被消费。虽然消息本身也会持久化,但如果节点磁盘存储设备发生故障那同样会导致消息丢失。

这是 RabbitMQ 内置的集群模式,Erlang 语言天生具备分布式特性,所以不需要借助类似 Zookeeper 之类的组件来实现集群(集群节点间使用 cookie 来进行通信验证,所有节点都必须使用相同的 .erlang.cookie 文件内容),不同节点的 Erlang、RabbitMQ 版本必须一致。

d. 镜像队列模式#

前面我们讲了 RabbitMQ 内置的集群模式有丢失消息的风险,“镜像队列”可以看成是对内置默认集群模式的一种高可用架构的补充。

可以将队列镜像(同步)到集群中的其他 Broker 上,相当于是多副本冗余。如果集群中的一个节点失效,队列能自动地切换到集群中的另一个镜像节点上以保证服务的可用性,而且消息不丢失。

在 RabbitMQ 镜像队列中所谓的 Master 和 Slave 都仅仅是针对某个 Queue 而言的,而不是 Node。一个 Queue 第一次创建所在的节点是它的 Master 节点,其他节点为 Slave 节点。如果 Master 由于某种原因失效,最先加入的 Slave 会被提升为新的 Master。

无论客户端请求到达 Master 还是 Slave,最终数据都是从 Master 节点获取。当请求到达 Master 节点时,Master 节点直接将消息返回给 Client,同时 Master 节点会通过 GM(Guaranteed Multicast)协议将 Queue 的最新状态广播到 Slave 节点。GM 保证了广播消息的原子性,即要么都更新要么都不更新。当请求到达 Slave 节点时,Slave 节点需要将请求先重定向到 Master 节点,Master 节点将消息返回给 Client,同时 Master 节点会通过 GM 协议将 Queue 的最新状态广播到 Slave 节点。

很多同学可能就会疑惑,这样设计太傻叉了,Slave 完全是闲置的啊!干嘛不学习 MySQL 主从复制,起码可以搞个读写分离啊!其实业界很多 HA 架构实践中冗余资源都是闲置的。前面我们讲了 RabbitMQ 镜像队列中的 Master、Slave 是 Queue 维度而并非 Node 维度,所以我们可以交叉减少资源限制,如下图所示:

e. 联邦模式#

Federation 和 Shovel 类似,也是一个实现跨集群、节点消息同步的插件。支持联邦交换器、联邦队列(作用在不同级别)。

Federation 插件允许你配置一个 Exchanges Federation 或 Queues Federation。一个 Exchange/Queues Federation 允许你从一个或者多个 upstream 接收信息,就是远程的 Exchange/Queues。

无论是 Federation 还是 Shovel 都只是解决消息数据传输的问题(当然插件自身可能会一些应用层的优化),跨机房跨城市的这种网络延迟问题是客观存在的,不是简单的通过什么插件可以解决的,一般需要借助昂贵的专线。

很多书籍和文章中存在误导大家的,可能会说 Federation/Shovel 可以解决延迟的问题,可以实现异地多活等等,其实这都是错误的。而且我可以负责人的告诉大家,他们所谓的“异地多活”并非大厂最佳实践。

例如:使用 Shovel 构建集群,RabbitMQ 和应用程序都选择双机房部署时,当杭州机房发生了消息积压后超出阈值部分的消息就会被转发到上海机房中,此时上海机房的应用程序直接消费掉上海机房 RabbitMQ 的消息,这样看起来上海机房是可以分摊负载,而且一定程度上实现“双机房多活”的。但是数据库呢?选择两边都部署还是仅部署在某个机房呢?两边同时写入是很容易造成冲突的,如果数据库仅仅部署在杭州机房,那么数据库也可能成为瓶颈导致消费速度依然上不去,只不过是多了上海机房中的消费者实例节点而已。

而使用 Federation 模式呢?如果要真正要实现“双机房多活”那么应用程序也是多机房的,那某些 Exchange/Queue 中的消息会在两边机房都有,两边机房的应用程序都会同时消息,那必然会造成重复消息!

方案 容量 容灾 成本
异地多活 [优] 基于逻辑机房,容量可伸缩的云微架构
[优] 容量可异地伸缩
[优] 日常运行,容灾时可用性高。
[劣] 受城际网络故障影响,影响度取决于横向依赖程度。
[优] IDC、应用等成本在日常得到有效利用。
两地三中心 [劣] 仅可部署在一个城市,容量伸缩有城市级瓶颈 [劣] 灾备设施冷备等待,容灾时可用性低。 [劣] 容灾设施等成本仅在容灾时才使用,且受限于可用性。

// TODO

posted @   tree6x7  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示
主题色彩