关于rabbitmq:vivo-基于原生-RabbitMQ-的高可用架构实践

36次阅读

共计 4323 个字符,预计需要花费 11 分钟才能阅读完成。

一、背景阐明

vivo 在 2016 年引入 RabbitMQ,基于开源 RabbitMQ 进行扩大,向业务提供消息中间件服务。

2016~2018 年,所有业务均应用一个集群,随着业务规模的增长,集群负载越来越重,集群故障频发。

2019 年,RabbitMQ 进入高可用建设阶段,实现了高可用组件 MQ 名字服务以及 RabbitMQ 集群的同城双活建设。

同时进行业务应用集群的物理拆分,严格依照集群负载状况和业务流量进行业务应用集群的调配以及动静调整。

在 2019 年高可用建设后至今,业务流量减少了十倍,集群未呈现过重大故障。

RabbitMQ 是实现了 AMQP 协定的开源音讯代理软件,起源于金融零碎。

具备丰盛的个性:

  1. 音讯可靠性保障,RabbitMQ 通过发送确认保障音讯发送牢靠、通过集群化、音讯长久化、镜像队列的形式保障音讯在集群的牢靠、通过生产确认保障音讯生产的可靠性。
  2. RabbitMQ 提供了多种语言的客户端。
  3. 提供了多种类型的 exchange,音讯发送到集群后通过 exchange 路由到具体的 queue 中。
  4. RabbitMQ 提供了欠缺的治理后盾和治理 API,通过治理 API 能够疾速与自建监控零碎整合。

RabbitMQ 在具体实际中发现的问题:

  1. 为保障业务高可用应用多套集群进行物理隔离,多套集群无对立平台进行治理。
  2. 原生 RabbitMQ 客户端应用集群地址连贯,应用多套集群时业务须要关怀集群地址,应用凌乱。
  3. 原生 RabbitMQ 仅有简略的用户名 / 明码验证,不对应用的业务利用方进行鉴权,不同业务容易混用 exchange/queue 信息,造成业务利用应用异样。
  4. 应用的业务利用方较多,无平台保护音讯发送方、生产方的关联信息,多个版本迭代后无奈确定对接方。
  5. 客户端有限流,业务突发异样流量冲击甚至击垮集群。
  6. 客户端无异样音讯重发策略,须要应用方实现。
  7. 集群呈现内存溢出等造成集群阻塞时无奈疾速主动转移到其它可用集群。
  8. 应用镜像队列,队列的 master 节点会落在具体某个节点上,在集群队列数较多时,容易呈现节点负载不平衡的状况。
  9. RabbitMQ 无队列主动均衡能力,在队列较多时容易呈现集群节点负载不均问题。

二、整体架构

1、MQ-Portal– 反对利用应用申请

过往业务团队实用 RabbitMQ 时,利用申请的流量以及对接的利用等信息都在线下表格记录,较为零散,更新不及时,无奈精确理解业务以后实在的应用状况,因而通过一个接入申请的流程可视化、平台化建设利用应用的元数据信息。

通过 MQ-Portal 的申请流程(如上图),确定了音讯发送利用、生产利用、应用 exchange/queue、发送流量等信息应用申请提交后将进入 vivo 外部工单流程进行审批。

工单流程审批通过后,通过工单的接口回调,调配利用具体应用的集群,并在集群上创立 exchange/queue 曾经绑定关系。

因为采纳多集群物理隔离的形式保障业务在正式环境的高可用,无奈简略通过一个 exchange/queue 的名称定位到应用的集群。

每一个 exchange/queue 与集群之间通过惟一的一对 rmq.topic.key 与 rmq.secret.key 进行关联,这样 SDK 启动过程中即可定位到具体应用的集群。

rmq.topic.key 与 rmq.secret.key 将在工单的回调接口中进行调配。

2、客户端 SDK 能力概述

客户端 SDK 基于 spring-message 和 spring-rabbit 进行封装,并在此基础上提供了利用应用鉴权、集群寻址、客户端限流、生产生产重置、阻塞转移等能力。

2.1、利用应用鉴权

开源 RabbitMQ 仅通过用户名明码的形式判断是否容许连贯集群,然而利用是否容许应用 exchange/queue 是未进行校验的。

为了防止不同业务混用 exchange/queue,须要对利用进行应用鉴权。

利用鉴权由 SDK 和 MQ-NameServer 协同实现。

利用启动时首先会上报利用配置的 rmq.topic.key 信息到 MQ-NameServer,由 MQ-NameServer 判断应用利用与申请利用是否统一,并且在 SDK 发送音讯过程中还会进行二次校验。

/**
  * 发送前校验,并且获取真正的发送 factory,这样业务能够申明多个,* 然而用其中一个 bean 就能够发送所有的音讯,并且不会导致任何异样
  * @param exchange 校验参数
  * @return 发送工厂
*/
public AbstractMessageProducerFactory beforeSend(String exchange) {if(closed || stopped){
        // 上下文曾经敞开抛出异样,阻止持续发送,缩小发送临界状态数据
        throw new RmqRuntimeException(String.format("producer sending message to exchange %s has closed, can't send message", this.getExchange()));
    }
    if (exchange.equals(this.exchange)){return this;}
    if (!VIVO_RMQ_AUTH.isAuth(exchange)){throw new VivoRmqUnAuthException(String.format("发送 topic 校验异样,请勿向无权限 exchange %s 发送数据,发送失败", exchange));
    }
    // 获取真正的发送的 bean,防止发送谬误
    return PRODUCERS.get(exchange);
}

2.2、集群寻址

前文说过,利用应用 RabbitMQ 严格依照集群的负载状况和业务流量进行集群的调配,因而具体某个利用应用的的不同的 exchange/queue 可能是调配在不同的集群上的。

为了晋升业务的开发效率,须要屏蔽多集群对业务的影响,因而依照利用配置的 rmq.topic.key 信息进行集群的主动寻址。

2.3、客户端限流

原生 SDK 客户端不进行发送流量限流,在局部利用存在异样继续向 MQ 发送音讯时,可能会冲垮 MQ 集群。并且一个集群为多利用独特应用,繁多利用造成集群影响将会影响应用异样集群的所有利用。

因而须要在 SDK 中提供客户端限流的能力,必要时能够限度利用向集群发送音讯,保障集群的稳固。

2.4、生产生产重置

(1)随着业务规模增长,集群负载继续减少,此时须要进行集群的业务拆分。为了缩小在拆分过程中防止业务重启,须要有生产生产重置性能。

(2)集群出现异常,可能会造成消费者掉线,此时通过生产生产重置能够疾速拉起业务生产。

为了实现生产生产重置,须要实现一下流程:

  • 重置连贯工厂连贯参数
  • 重置连贯
  • 建设新的连贯
  • 重新启动生产生产
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(address);
connectionFactory.resetConnection();
rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitTemplate = new RabbitTemplate(connectionFactory);

同时 MQ-SDK 中有异样音讯重发策略,能够防止在生产重置过程中导致的音讯发送异样。

2.5、阻塞转移

RabbitMQ 在内存应用超过 40%,或是磁盘应用超限度时会阻塞音讯发送。

因为 vivo 中间件团队曾经实现了 RabbitMQ 同城双活的建设,因而在呈现一个集群发送阻塞时能够通过生产生产重置到双活集群实现阻塞的疾速转移。

2.6、多集群调度

随着利用的倒退,单集群将无奈满足利用的流量需要,并且集群队列均为镜像队列,无奈简略的通过减少集群节点的形式实现业务撑持流量单集群的程度扩容。

因而须要 SDK 反对多集群调度能力,通过将流量扩散到多个集群上满足业务大流量需要。

3、MQ-NameServer– 反对 MQ-SDK 实现故障疾速切换

MQ-NameServer 为无状态服务,通过集群部署即可保障本身高可用,次要用于解决以下问题:

  • MQ-SDK 启动鉴权以及利用应用集群定位。
  • 解决 MQ-SDK 的定时指标上报(音讯发送数量、音讯生产数量),并且返回以后可用集群地址,确保 SDK 在集群异样时依照正确地址进行重连。
  • 管制 MQ-SDK 进行生产生产重置。

4、MQ-Server 高可用部署实际

RabbitMQ 集群均采纳同城双活部署架构,依附 MQ-SDK 和 MQ-NameServer 提供的集群寻址、故障疾速切换等能力保障集群的可用性。

4.1、集群脑裂问题解决

RabbitMQ 官网提供了三种集群脑裂复原策略。

(1)ignore

疏忽脑裂问题不解决,在呈现脑裂时须要进行人为干涉才可复原。因为须要人为干涉,可能会造成局部音讯失落,在网络十分牢靠的状况能够应用。

(2)pause_minority

节点在与超过半数集群节点失联时将会主动暂停,直到检测到与集群超半数节点的通信复原。极其状况下集群内所有节点均暂停,造成集群不可用。

(3)autoheal

少数派节点将主动重启,此策略次要用于优先保障服务的可用性,而不是数据的可靠性,因为重启节点上的音讯会失落。

因为 RabbitMQ 集群均为同城双活部署,即便单集群异样业务流量也可主动迁徙到双活机房集群,因而抉择应用了 pause_minority 策略防止脑裂问题。

2018 年屡次因网络抖动造成集群脑裂,在批改集群脑裂复原策略后,已未再呈现脑裂问题。

4.2、集群高可用计划

RabbitMQ 采纳集群化部署,并且因为集群脑裂复原策略采纳 pause_minority 模式,每个集群要求至多 3 个节点。

举荐应用 5 或 7 节点部署高可用集群,并且管制集群队列数量。

集群队列均为镜像队列,确保音讯存在备份,防止节点异样导致音讯失落。

exchange、queue、音讯均设置为长久化,防止节点异样重启音讯失落。

队列均设置为 lazy queues,缩小节点内存应用的稳定。

4.3、同城双活建设

双机房部署等价集群,并且通过 Federation 插件将双集群组成联盟集群。

本机房利用机器优先连贯本机房 MQ 集群,防止因专线抖动造成利用应用异样。

通过 MQ-NameServer 心跳获取最新的可用集群信息,异样时重连到双活集群中,实现利用性能的疾速复原。

三、将来挑战与瞻望

目前对 RabbitMQ 的应用加强次要在 MQ-SDK 和 MQ-NameServer 侧,SDK 实现较为简单,前期心愿能够构建消息中间件的代理层,能够简化 SDK 并且对业务流量做更加粗疏化的治理。

作者:derek

正文完
 0