ESB架构,从支撑几千到百万订单的优化过程
背景说明
2016年左右是SAAS快速发展的阶段,公司转型SAAS业务,变成了风口的猪,业务快速发展,同时也避免不了互联网公司的发展初期的痛点,技术严重拖了业务发展的后腿,几千的订单数已经让系统不堪重负,三天一个小故障,五天一个大故障,系统基本处于不可用的状态。没有监控系统,基本都是客户发现问题电话打过来了,才知道系统出问题了。
公司的技术栈是JAVA,基于一个国外比较流行,国内比较冷门的开源ESB框架"Mule"来开发,当时公司决定做技术转型,将ESB往微服务转,同时选择GRPC作为微服务的开发框架。
当时面临的问题是该领域的SAAS服务业务逻辑非常复杂,整个老的代码已经沉淀了一段时间,往新的框架迁移的时间周期比较长,所以老的代码还需要继续支撑一段很长的时间,但是业务量又高速发展,所以需要针对老的代码做性能优化,能够支撑到新的框架能够上线
架构介绍
What is Mule ESB?
Mule, the runtime engine of Anypoint Platform, is a lightweight Java-based enterprise service bus (ESB) and integration platform that allows developers to connect Applications together quickly and easily, enabling them to exchange data. It enables easy integration of existing systems, regardless of the different technologies that the applications use, including JMS, Web Services, JDBC, HTTP, and more. The ESB can be deployed anywhere, can integrate and orchestrate events in real time or in batch, and has universal connectivity.
what-mule-esb.png
框架数据流转
数据流转.png
- xxx-api是api层的服务;xxx-service是service层的服务,ActiveMQ数据总线
- routeQ:服务注册topic(service层服务注册具体的能力-方法),此处实现了一个服务注册发现的能力
- API接收到请求:1、api从routeQ中获取请求具体实现的service层对应的队列名(xxx-service-{host1});2-api将消息(json格式)发送到对应的队列中;3-service层服务监听到队列的消息,进行业务处理;4、service层服务获取监听的消息中的target地址(xxx-api-{host}),将结果消息写会target地址;5、api服务监听到resp消息然后将结果返回给请求终端
- api的处理是一个同步过程
服务部署架构图
服务的部署主要是按照粗粒度的系统功能做了集群的划分:定时任务主要是
mule.png
- 对外服务
主要是面向对外的服务,流量较大,SLA要求比较高,出问题概率也比较高,系统故障的容忍度低
- 内部管理服务内部运营和管理人员使用的,服务的SLA要求不高,系统故障容忍度高,允许一定时间的服务异常
- 定时任务任务类的介于对外和对内的服务之间的SLA要求,由于也是服务于在线业务类的服务,部分服务实时性要求比较高,类似于准实时,比如延时的要求是秒级别的,所以这类的服务也需要有一定的可用性控制,系统故障荣任务略低
优化过程
监控报警
最开始没有监控报警,服务挂掉全是靠客户的电话才能知道,那么首先就是构建监控报警系统,基于小米开源的Open-Falcon构建了最早的监控报警系统,然后开始分析整个Mule开发框架的问题,通过分析部署图和Mule的框架可以得出,出问题最直接的表象,队列阻塞和队列的consumer数量异常,那么就是基于这个来实现一个报警。
Mule基于ActiveMQ来作为自己的数据总线,ActiveMQ是一个典型的AMQ,类似于RabbitMQ、RocketMQ,都有自己的admin管理页面或者api接口,那么就是基于admin-api来操作,由于ActiveMQ产品比较老,很多的协议还是基于XML,那么就是通过接口获取队列的stat信息,然后分析队列的消息数量和consumer数量来触发报警,Open-Falcon的开放性非常强,只需要写一个plugin就可以实现自己需要定制化报警
ActiveMQ.png
其中queues/queue/stats/size队列为消费的堆积消息数;queues/queue/stats/consumerCount是消费者的数量。消费者消息规定的数报警、消息对接数量过多报警;
故障分析
通过流量分析和故障检测,发现两个点是主要的瓶颈点
- 业务消息:由于业务需要,终端跟云端通过消息进行业务通信(有些业务场景需要云端主动给消息到终端),但是消息的通信采用了轮训拉取的方式,通过分析有85%的流量都来自于消息的轮训,但是由于业务量还不够大,这85%的消息轮训里边又80%以上的都是无效的空查询,绝大部分都是没有消息,这个时候就是空轮训,但是整个业务请求还需要过一遍ActiveMQ。同时消息的查询后端service直接查询MySQL,这样对于MySQL的压力也非常大
- 业务账单数据的处理:此业务出故障的次数也比较多,我来公司第一次碰到故障就是账单的队列堵了,这个的原因主要是账单的数据会引发一连串的数据计算,逻辑非常复杂,同步计算,严重依赖MySQL,consumer消费能力非常容易遇到瓶颈
- ActiveMQ集群:集群使用的是5.13.0,比较老的一个版本,对于cluster的支持不太友好,只能支持双主模式,双主模式经常出故障
处理措施
- 消息的轮训拉取的方式改造成推的模式,通过websocket实现长连接推送消息
- 账单数据的处理其实类似于一个离线业务,不算是在线业务,实时性要求不是很高,将账单处理的部分从在线的集群剥离,独立一个账单处理的集群,同时账单的堵塞经常是因为计算复杂,数据库瓶颈遇到队列堵塞,所以做了读写分离,对于账单报表的读取和计算在同一个Mule集群里边做了队列的读写分离,避免互相的影响
- 同时将服务再次做了细粒度的划分:在线/离线、读/写的服务的划分,将集群重新的规划,尽量保证实时在线业务的高可用
- 在线业务集群同时部署两个集群,做数据隔离,但是同时承载业务流量,做到服务级别的HA
- 接入层通过Nginx和openresty做了一些熔断和降级的处理,最大限度保证服务的可用性
- 通过分析可以发现基本服务的瓶颈都会出现在MySQL数据层,那么针对这个瓶颈也做了调整,读写分离和分库,读写分离比较好理解,分库的逻辑主要是针对SAAS的特点,SAAS系统主要是以客户为核心,每个客户都有自己的客户标识,那么可以天然的通过客户的标识来进行分库的操作
- ActiveMQ遇到过几次问题,首先是内存过高触发流控(AMQ都有类似的自我保护能力)、OOM这种主要是调整JVM的内存设置以及流控的配置
- ActiveMQ的集群问题,双主的模式经常会出现consumer都落到一个节点,导致另外一个节点产生的消息无法消费堵塞,高版本的ActiveMQ支持多节点的集群模式也解决了类似的BUG,由于所有的RD都集中在GRPC微服务改造,不准备提升版本支持高版本的开发,所以只能通过别的方案绕开,ActiveMQ不采用集群,只是用单点+haproxy来实现HA。
总结
- 通过上边的优化改造错误,Mule运行了一年多的时间,支撑了订单到百万的业务量,同时故障率非常的低,保证了SLA
- 一个完整的大型系统,在整个的生命周期内,开发交付以后,运维、优化、保证可用性阶段的重要性也非常的大,需要不停的去学习改造
- 监控报警系统需要首先部署好,避免系统的裸奔,只要好的监控报警才能够帮助我们更快速的定位问题、发现问题、优化和改造系统
- 服务的等级划分非常重要,需要最大粒度的去划分,然后根据等级做不同的处理
- 服务的治理、读写分离在系统的很多方面都能用到,需要根据实际情况更好的应用到特定的地方
题外话
MuleSoft公司成立于2006年,最初是实现一个ESB的开发框架并开源,框架的名字命名为"Mule",由于单纯通过框架和服务支持很难盈利;
2009年在新CEO加入以后,开始转型,并构建一个名为"Anypoint"的云平台,平台打通了数千个SAAS服务平台,并将服务通过API接口的方式暴露给用户,用户可以在Anypoint上边按照自己的业务场景和依赖的三方SAAS平台,组装自己的自定义业务,可以说Anypoint是对于ESB的上层抽象,将SAP、NetSuite、Salesforce等三方SAAS服务当做企业的应用,Anypoint就是连接SAAS服务的数据总线,同时用户可以基于Anypoint的编排能力用最低的代价实现自己的业务,大量的减少了IT的投入成本。用当前比较流行的概念来说,Anypoint就是一个通用的业务中台,也是一个有业务编排能力的低代码平台。
也正是得益于MuleSoft的转型和在云服务的优秀表现,2018年被Salesforce用65亿美元的价格收购,彻底逆袭。
从MuleSoft的成功我们也能看到,对于计算机和软件体系,任何概念和理论都是互通的,都是可以借鉴并发展的,同时需要有一个明确的可以落地的方向,沿着方向持续的走下去。