当前位置: 首页 > 科技观察

这样做RabbitMQ高可用,业务流量猛增10倍也不怂

时间:2023-03-20 20:07:33 科技观察

这样RabbitMQ高可用,业务流量暴涨10倍。一、背景描述vivo于2016年推出RabbitMQ,基于开源的RabbitMQ进行扩展,为业务提供消息中间件服务。从2016年到2018年,所有业务都使用一个集群。随着业务规模的增长,集群负载越来越重,集群故障频发。2019年,RabbitMQ进入高可用建设阶段,完成了RabbitMQ集群的高可用组件MQ名称服务和同城双活建设。同时,对业务用集群进行物理拆分,严格按照集群负载和业务流量进行业务用集群的分配和动态调整。自2019年高可用建设以来,业务流量增长十倍,集群未出现严重故障。RabbitMQ是一款开源的消息代理软件,实现了AMQP协议,起源于金融系统。具有丰富的特性:消息可靠性保证,RabbitMQ通过发送确认保证消息可靠传递,通过集群、消息持久化、镜像队列保证集群内的消息可靠性,通过消费确认保证消息消费可靠性;RabbitMQ提供了多种语言的Client;提供各种类型的exchange,消息发送到集群后,通过exchange路由到特定的队列;RabbitMQ提供了完善的管理后台和管理API,通过它可以快速和自建监控系统集成。RabbitMQ在实践中发现的问题:为了保证业务的高可用,采用多套集群进行物理隔离,多套集群没有统一的管理平台;原生RabbitMQ客户端使用集群地址连接,业务在使用多套集群时需要关心集群地址,使用容易混淆;原生RabbitMQ只有简单的用户名/密码认证,不对使用的业务应用端进行认证,容易将不同业务的exchange/queue信息混在一起,导致业务应用使用异常;平台维护消息发送方和消费方的关联信息,多次版本迭代后无法确定接收方;客户端无限流量,业务突发异常流量冲击甚至毁坏集群;客户端无异常消息重发策略,用户需要执行;当集群内存溢出阻塞时,无法快速自动转移到其他可用集群;使用镜像队列,队列的主节点会落在一个特定的节点上。当集群队列数量较多时,容易出现节点负载不均衡的情况;RabbitMQ不具备自动均衡队列的能力,在队列较多的情况下容易出现集群节点负载不均的问题。二、总体结构1、MQ-Portal--支持应用程序应用。业务团队以往在使用RabbitMQ时,应用流量和对接应用等信息都是以离线形式记录的,比较分散,更新不及时,无法准确了解业务现状。真正的使用,因此通过访问应用程序流程来可视化和平台化应用程序使用的元数据信息。通过MQ-Portal的应用流程(如上图),确定消息发送应用、消费应用、交换/队列使用、发送流量等信息使用应用提交,将进入vivo内部工单审批流程。工单流程审核通过后,通过工单接口回调分配应用使用的具体集群,并在集群上创建exchange/queue绑定关系。由于在正式环境中使用多集群物理隔离来保证业务的高可用,因此无法简单地定位到一个exchange/queue名称所使用的集群。每个exchange/queue通过一对唯一的rmq.topic.key和rmq.secret.key与集群相关联,这样在SDK启动过程中就可以定位到具体使用的集群。rmq.topic.key和rmq.secret.key会在ticket的回调接口中赋值。2.客户端SDK能力概述客户端SDK基于spring-message和spring-rabbit进行封装,在此基础上提供应用鉴权、集群寻址、客户端限流、生产消费重置、阻塞传输等能力。1)应用使用开源的RabbitMQ进行认证,仅通过用户名和密码来判断是否允许连接集群,应用是否允许使用exchange/queue则不做验证。为了避免不同服务混用exchange/queue,需要对应用进行鉴权。应用认证由SDK和MQ-NameServer共同完成。应用启动时,会先将应用配置的rmq.topic.key信息上报给MQ-NameServer,MQ-NameServer会判断使用的应用与申请的应用是否一致,并进行二次校验在SDK的消息发送过程中执行。/***发送前检查,获取真正的发送工厂,这样业务可以声明多个,*但是使用其中一个bean发送所有消息,不会导致任何异常*@paramexchange验证参数*@returnsendingfactory*/publicAbstractMessageProducerFactorybeforeSend(Stringexchange){if(closed||stopped){//上下文已经关闭并抛出异常,防止进一步发送,减少临界状态数据的发送thrownewRmqRuntimeException(String.format("producersendingmessagetoexchange%shasclosed,can'tsendmessage",this.getExchange()));}if(exchange.equals(this.exchange)){returnthis;}if(!VIVO_RMQ_AUTH.isAuth(exchange)){thrownewVivoRmqUnAuthException(String.format("发送topic验证异常,请勿私自向exchange%s发送数据,发送失败",exchange)));}//获取真正的发送bean,避免发送错误returnPRODUCERS.get(exchange);}2)ClusteraddressingAs上面提到,应用程序使用RabbitMQ严格按照集群负载和业务流量来分配集群,所以一个特定应用使用的不同exchange/queue可能会分配到不同的集群上。为了提高业务开发效率,需要屏蔽多个集群对业务的影响,所以根据应用中配置的rmq.topic.key信息进行集群的自动寻址。3)客户端限流原生SDK客户端不限制发送流量,当某些应用出现异常继续向MQ发送消息时,可能会导致MQ集群不堪重负。而且,一个集群被多个应用使用,单个应用影响到集群,就会影响到所有使用该异常集群的应用。因此,需要在SDK中提供客户端限流能力,在必要时限制应用向集群发送消息,以保证集群的稳定性。4)生产消费重置①随着业务规模的增长,集群负载不断增加。这时候就需要进行集群业务拆分。为了避免拆分过程中业务重启,需要一个生产消费重置功能。②集群异常可能导致消费者下线。此时,可以通过重置生产和消费来快速拉动企业消费。为了重置生产和消费,需要执行以下过程:重置连接工厂连接参数;重置连接;建立新连接;重启生产和消费。CachingConnectionFactoryconnectionFactory=newCachingConnectionFactory();connectionFactory.setAddresses(地址);connectionFactory.resetConnection();rabbitAdmin=newRabbitAdmin(connectionFactory);rabbitTemplate=newRabbitTemplate(connectionFactory);同时MQ-SDK有异常消息重发策略,可以避免配置时导致消息发送异常。5)阻塞传输当内存使用率超过40%,或者磁盘使用率超过限制时,RabbitMQ会阻塞消息发送。由于vivo中间件团队已经完成了RabbitMQ同城双活的建设,当发生集群传输块时,可以将生产和消费重置到双活集群中,完成块的快速传输。6)多集群调度随着应用的发展,单个集群将无法满足应用的流量需求,集群队列都是镜像队列。业务支撑流量无法通过简单的增加集群节点来实现单个集群的水平扩展。因此需要SDK支持多集群调度能力,通过将流量分发到多个集群来满足业务的大流量需求。3.MQ-NameServer--支持MQ-SDK实现快速故障转移。MQ-NameServer是一个无状态的服务,通过集群部署可以保证其高可用。主要用于解决以下问题:MQ-SDK启动鉴权、应用使用集群定位;处理MQ-SDK的时序指标(发送消息数、消费消息数)上报,返回当前可用的集群地址,保证集群异常时SDK根据正确地址重连;控制MQ-SDK重置生产和消费。4.MQ-Server高可用部署实践RabbitMQ集群采用同城双活部署架构,依靠MQ-SDK和MQ-NameServer提供的集群寻址和快速故障转移能力保证集群的可用性。1)集群脑裂问题处理RabbitMQ官方提供了三种集群脑裂恢复策略。①ignore忽略脑裂问题,不处理。当发生裂脑时,需要人为干预来恢复它。由于需要人为干预,部分消息可能会丢失,在网络非常可靠的情况下可以使用。②pause_minority节点在与超过半数集群节点失去连接时会自动暂停,直到检测到与超过半数集群节点的通信恢复。在极端情况下,集群中的所有节点都被挂起,导致集群不可用。③Autoheal少数节点会自动重启。该策略主要用于优先考虑服务的可用性而不是数据的可靠性,因为重启节点上的消息会丢失。由于RabbitMQ集群都是同城双活部署,即使单个集群业务异常流量也能自动迁移到双活机房集群,所以选择pause_minority策略,避免脑裂问题.2018年多次因为网络抖动导致集群脑裂。修改集群裂脑恢复策略后,不再出现裂脑问题。2)集群高可用方案RabbitMQ采用集群部署,由于集群脑裂恢复策略采用pause_minority模式,每个集群至少需要3个节点。建议部署5个或7个节点的高可用集群,并控制集群队列数。集群队列都是镜像队列,保证消息有备份,避免节点异常导致消息丢失。exchange、queue、消息都设置为持久化,避免节点异常重启消息丢失。队列都设置为惰性队列,以减少节点内存使用的波动。3)同城双机房部署对等集群,双集群通过Federation插件组成联邦集群。机房内的应用机器优先接入机房内的MQ集群,避免专线抖动导致应用使用异常。通过MQ-NameServer的心跳获取最新的可用集群信息,异常时重新连接双活集群,实现应用功能的快速恢复。3、未来的挑战和展望目前,RabbitMQ的使用主要在MQ-SDK和MQ-NameServer端增强。SDK的实现比较复杂。后期希望可以搭建消息中间件的代理层,可以简化SDK,让业务流量更细化。管理。