梁文刚:微服务中心运维平台的产品负责人。UC公司级的JAVA开发框架作者。在UC项目经历:负责LBS地盘聊吧、九游H5游戏大厅、九游社区IM、国际安卓发行9apps的整体架构设计。
本文根据msup和魅族联合举办的《第三期魅族技术开放日-架构设计与优化》录音整理原创首发,转载或节选内容前需获授权。
九游复杂庞大的系统带来的问题
2011年,整个九游平台就的创业阶段,系统非常复杂。整套是ASP.NET系统,所有的东西都是模块化,包括游戏接入、门户、论坛、支付、等级体系等全部在一起;存储层用Sql Server,把所有的应用都耦合在里面,带来的问题是:整个系统耦合非常严重,一个很小的模块出问题,导致整个系统崩溃,也没法做任何的追踪,不知道系统问题出在哪里,因为根本不知道整个模块之间的关联,是怎样导致整个系统崩溃的,还有就是扩展难,最后连机器都加不了,只能听天由命。
分布式系统的问题解决方案
一、拆分成分布式系统带来的问题
我们面对这样的问题时,要做的事情就很明显:必然要做拆分,拆分带来的好处显而易见,业务简单、权责清晰,专人专事。我们到现在拆分到了200多个独立系统,不知不觉整个平台变成了非常复杂的分布式系统,带来比较严重的问题:比如框架怎么去选择?基于版权问题,我们不可能再用回基于Windows的框架,毕竟也没法享受到开源社区的福利;存储和日志怎么处理?怎么保证我们这么多系统的状况下,统计数据是正确的?我们的日志量怎么做海量数据的处理?200多个系统每天会有好几十个上线任务,整个运维的工作都会耗费在部署上线,怎样去把运维从部署上解放出来?这都是我们在分布式上面临问题。
二、框架-自研JWS
九游平台统一用的自研框架,最主要的特点是做了服务容器。你要做一个服务容器,实际上有很多东西需要考虑:右边这块包括访问控制、请求队列和过载保护、空闲的连接,防攻击服务(后来接入了阿里集团统一的TMD防攻击服务),访问日志和性能统计日志。我们做追踪问题,做JVM监控日志,能够随时知道JVM上面有一些什么问题,可以随时追踪。
三、存储-DAL
我们当时也考虑过用持久性框架,但没有找到满足需求的框架,所以我们自己做了一套DAL存储层。我们模仿了对象映射,但不做对象关系映射,最重要的是分库分表,包括hash、range、range-hash、regex-hash,满足我们做垂直和水平的扩展。
缓存架构也是比较重要的,我们发现在做各类业务需求的时候缓存这块是少不了的,尤其是热点缓存,把它集成在框架里面,包括row cache、list cache。举个列子,我们可以通过list cache把前面5页占据了80%的点击的热点ID列表缓存起来,然后通过row cahce 批量获取每一页的ID内容。在缓存容器上,我们实现了L1:Ehcache、L2:Mc/Redis两级缓存。我们可以先使用mc或者redis做缓存,后续优化时,发现某些key数量有限,不会频繁变更,或者访问量很大,我们就可以不改任何代码的情况下,配置L1的缓存。还有一个统一的缓存预警机制,避免缓存到期时出现雪崩的情况。另外,我们在DAL上还做了读写分离、负载均衡。这都是存储上的优化。
四、存储-UCMHA
存储还有另外一块是UCMHA平台,mysql的高可用平台。如果没有Mysql高可用,当主库出问题时,运维需要花很大的精力把业务切换到从库,而现在所有的业务都接入到了UCMHA这个平台,通过ZK集群来控制proxy的主从切换,负载均衡方面,我们实现了TcpHA,负载均衡的方式访问proxy,同时TcpHA实现了mysql协议,能主动对proxy进行监控。这就是我们当前Mysql层面的高可用。
五、自动化部署&海量日志处理
海量日志处理基本上离不开Hadoop集群,都是比较常规用法,MapReudce任务、Storm流处理、Kafka队列、HDFS文件系统来做支撑。而JAE是我们的自动化部署平台,统一管理我们所有系统的包发布和实例部署。
六、2014年,阶段性胜利
到2014年,我们的分布式拆分实际上已经取得了阶段性的成果,通过刚才的介绍,我们完成了对框架、存储、日志和部署的优化,整个系统是相对比较稳定的,系统本身可以配合业务快速扩张进行扩展。我们在2013年、2014年业务扩展得非常快,所有这些演进都在2014年前逐步稳定下来的。
服务治理的技术点
一、服务治理-思路
2014年我们自我感觉技术挺好了,当我们梳理全平台依赖状况的时候出现了这样一个状况:我们的平台存在各种各样的依赖关系,没有人能理清楚这样繁杂的依赖关系,我们面临着一个新的挑战:服务治理。
我们现在的服务数量达到200+,服务实例数量600+,接口数超过3000,链路数400+,日调度数超过25亿,机房5个,各种抖动、服务之间的调度会导致我们每天出现超过10万次的调度失败。这种情况下出现了很多问题:
1、服务资源配置困难。一个业务依赖很多其他服务,运维要花很多的资源来服务配置。
2、授权密钥配置更困难,业务上线依赖着哪个系统就要去运维那边申请,运维分配一个密钥,服务访问者需要配置该密钥进行签名,服务提供方也需要新增该caller的密钥,所以密钥配置是一个非常痛苦的经历。
3、强依赖F5/或者DNS。F5做一些结点切换的时候没办法做到实时修改,比如域名,涉及DNS的过期问题;内部DNS更明显,遇到DNS过期的问题,不能实时处理一些由于链路导致的具体问题,而且很多抖动问题都是瞬间发生,但很快又恢复,根本没有时间让运维去识别故障并做节点移除。
4、级联故障,故障识别成本高。我们的那些链条过于复杂,有些四级、五级的链条,一旦哪个环节的一「点」出了问题,会出现一「片」的症状,这是比较痛苦的。
5、接口沟通成本高。HTTP协议+JSON的协议和签名方式,接口沟通成本高。
我们的解决方式是搭建服务中心,服务中心就是服务治理的总体解决思路,由服务中心解决服务发现、服务调度、服务资源、服务质量、服务监控。
二、服务治理-设计
我们的设计非常简单:
1、厘清三个节点:服务的消费者、生产者、服务中心。服务消费者和生产者之间没有任何生产节点,我们的调用还是通过原生调用,服务中心只负责后面的一些资源管理和服务的配置推送,这期间运维是相对清闲的,因为所有的接入实例都会自动注册到服务中心,无论是消费者,还是生产者都会实时接收到相关的配置变动。另外,服务消费者和服务生产者之间是没有中间节点的,自动接入的服务都是以内网IP的方式进行通信。
2、服务调度框架HTTPSF,包括权重负载均衡、自动降权、自动恢复,调度链染色,后续我们基于HTTPSF实现了RPC框架。
3、内部/外部服务,内部服务是无需资源管理的,服务实例启动时就会自动接入服务中心,同时,我们有很多的外部服务依赖,要访问各种外部渠道平台的接口,一个重点是把外部服务纳入到我们的服务框架,在外部出现问题的时候,我们能够自动做一个降权跟恢复处理,外部服务的服务信息没办法自动注册,由运维做人工录入到服务中心。
三、服务治理-协议
为什么用HTTP协议?在着手进行服务治理时,整个平台实际在线上已经有100+的服务,而且是用HTTP协议JSON的序列化方式,这造成我们没办法对现有的服务做一个彻底的改造,所以我们没办法使用现成的服务框架,例如淘宝的HSF服务框架来做服务化的改造,同时我们是个完全业务驱动的平台,我们也没法投入这样的成本停下业务进行服务化改造,因此我们的框架是完全兼容现有的服务在上面做的HTTPSF,基于HTTP协议做的一个调度框架,我们几乎没有投入任何的接入成本就完成了服务化,后面我们做了一个HTTPSF-RPC框架,JWS框架中也集成了RPC的服务容器,新业务组件为了更高的开发效率会优先使用RPC框架。
四、服务治理-去中心依赖
因为加入了服务中心,平台变成了一套非常复杂的分布式系统,服务中心变得非常重要,同时去中心化也就非常必要了。在服务中心出问题的时候,怎么去处理,这边会有两级容灾:第一级很简单,就是服务中心集群部署,它保证内部不出问题,目前一直保证着5个9的可用性;另外一个层面就是万一服务中心出问题了怎么办?每个服务接入都会有一个Backup文件,所有跟服务中心的交互、配置都会在本地同步一个Backup文件,一旦服务器出问题,没法做接入的时候,会读取最近一次的Backup文件,按照之前的配置结点启动程序,这是去中心依赖的设计。
五、服务治理-负载均衡
这是我们的后台页面,实际上是运维同学用的最多的页面,但其实要做的事情并不多,只要知道我们服务都接入了哪些实例,权重如何,是否开启等等,内部服务接入之后会有一个已接入状态,内网IP就会自动发布,上面account_server是我们九游的帐号服务,就可以通过内网IP来做访问。uop是UC的帐号系统,就是UC的账户开放平台提供了整个UC的帐号体系,uop相对我们平台而言就是一个外部服务。这里运维启用了4条链路,都是在做多级容灾。正常情况下绝大部分访问量都会到我们运维希望最快的链路上面去了,一旦出现了问题,他会自动切换到移动或者电信或者汕头机房的集群。这就实现了多链路容灾处理。
六、服务治理-调度依赖管理
我们在服务的依赖管理方面可以拿到很多数据,所有对外依赖的系统和接口,各个负责人都可以看到自己系统的依赖情况,我们还可以看到实时访问量的曲线波动情况,可以对系统做一些依赖管理。
我们每周会发布依赖质量情况:比如这个系统对外依赖的情况,它依赖了这么多对外系统,其实都是我们内部的系统;它的访问量占比代表依赖情况;接着就是性能情况,以及可用性情况,每周可用性是增加还是降低的,都是可以看到的;访问量有没有增大,都可以做分析。负责人可以根据这个情况来判断对外依赖的情况是否需要改善或者做一些系统优化。
七、服务治理-故障拓扑
现在我们有整个的故障拓扑,这张图是我们线上的真实情况,服务量不是很多,因为我们做了一个服务的分级处理,运维只关心核心服务,核心服务涉及到的调度都在这里。运维只关心不同级别的服务的可用性,这是比较关键的,像上图中核心服务实际上是要求可用性能够确保达到4个9,这是最真实的可用性,因为不是通过采样得到的数据,而是所有的调度数据都会进行实时的统计。
我们九游服务管理人的角色,你进去之后可以看到所有依赖你的服务跟你对外依赖的服务组成的网状故障拓扑,基于这个拓扑,我们可以做一些报警或者一些质量分析。
八、服务治理-流程审批
在服务治理方面有流程审批,为了使整个运维工作更加便捷。我们所有的上线会变得非常简单,只需要在后台就可以完成,因为用户注册、申请权限、然后上线申请、服务调度授权申请,都是由各个业务负责人去提交流程。这也是在打造我们的运维生态,让运维工作可以更简单,变得规范化、降低成本,毕竟维护200多个独立系统,没有规范的流程,一定会出现疏漏的问题。
我们尽量把所有涉及到的服务管理、服务治理的问题处理好,尽量让运维不需要人工操作,包括看到的各种系统展示,运维没有任何的操作概念在里面,他只要管控、处理好监控、处理好报警阀值,这是他的一些具体工作。同时,服务中心系统让运维人员更关注一些应用上的数据,包括服务的依赖,容量,和质量,运维不再局限于只是做部署,从而把运维上升到一个应用运维层面,做的这些事情都是在帮助运维打造这样一个生态。
打造运维生态
要做好服务治理,尤其是有运维产品的服务治理,要做的事情挺多、也挺专业的。可以看到我们有这么多内容,包括服务发现、服务调度、服务资源、服务质量,包括后面我们一直在打造的运维生态。我可能实现了一个协议,我们做了一个调度,实现了服务的交互,这些就是服务治理吗?不是的,我们要做很多事情来维护所有服务,比如确保服务注册、服务发现、接口发布都是实时感知的。
服务调度,一旦出现波动问题都是做自动降权或者自动恢复,以应对运维来不及处理异常的情况。之前很多级别的问题都是数据突然间抖动或者网络突然间抖动,但抖动的时间不长,可能是几十秒,造成依赖方阻塞,这种情况下通过服务调度做自动降权,确保不会被你依赖,保证自身的稳定;当然服务方的服务容器这块我们有统一请求队列关联和过载保护机制,共同确保我们整个平台的可用性。
服务资源包括服务、实例、接口、接口黑白名单、RPC元数据、RPC容器、调度授权、调度策略、集群管理。
还有服务质量,包括调度监控、调度链监控、依赖监控、故障拓扑、故障时间分布、质量报告、服务分级、集群监控、报警系统。我们各种调度的监控,刚才列出来的只是很少一部分,我们还有各种各样的依赖、调度链,包括故障分布时间,服务分级的一些监控、质量报告、集群监控等等。
我们做了很多工作来确保整个平台的稳定性,所做的工作基于打造运维生态,确保我们的运维能够尽量减少人工操作,尽量可以让系统自动识别、自动处理,运维从全局把控住整个依赖关系的具体情况,保证系统全局处于稳定的情况。
Q&A
:您怎么选择存储DAL方式或Hash方式?
:DAL提供了各种可配的分库方式,比如hash, range, regex-hash, range-hash, 拿帐号ID举例,可以用range-hash的分库方式进行扩展,比如在1亿用户之内我们用Hash,1-2亿以上我们可以Hash到新的几个库,各自业务场景可以选择需要的分库方式进行扩展。
:Mysql的性能有很大局限性,为什么不选用性能更好的NoSQL数据库?为什么选用HTTP协议?
:先讲一下性能问题,我们在做服务治理的时候,选择HTTP协议,并不是基于性能考虑,我们现有100多个服务,做服务框架的时候没办法做改造,而且当时九游是强业务驱动,没有办法投入这个资源,把业务停下来进行协议改造,所以选择其他协议在当时没有操作可能性的。
另外HTTP的性能没有想象中得那么差,消息体积是会大些,但我们异步HTTP客户端的设计,性能是可以保证的,服务容器方面,我们JWS框架其实就是完全基于netty,在加上了HTTP协议管道处理,在常用的web框架性能测试对比上,我们性能可观。
关于Mysql, 真的没有想象得那么差,你觉得它差,多半是没用好,可能是索引没有处理好,一旦索引没有处理好,它就变成一个垃圾堆了,另外只要处理好mysql的水平扩展,能够通过磁盘硬件的升级提升写能力,通过加机器的方式做扩展,其实设计的好是不会存在储存瓶颈的。提到NoSQL,其实在很多业务场景是不适用的,尤其是些海量数据需要涉及多个过滤条件并且实现排序的需求,一味使用NoSQL,很多时候会发现你会设计一个很复杂的方式来实现一个本来很简单的需求,这个在UC早期的乐园项目也是吃过不少苦头,我们大量使用了tt的NoSQL存储,一些特别场景下确实带来了可观的性能提升,但随着业务变得复杂,有了关注,有了新鲜事,有了推送,有了LBS,有了地主,抢地盘,聊吧等等,NoSQL几乎让我们的业务寸步难行,后面我们才切换到了mysql。
:在负载均衡方面,你这边有服务消费者和服务提供者,这中间有没有中间节点?如果没有的话权重是怎样的?
:没有中间节点,权重是服务中心维护的,由运维控制每个服务实例能分多少权重,一台差一点的机器可能分的权重稍微低一点,设置完了之后就会主动推送,这个服务的依赖方跟调用方都会收到最新配置,基于权重的负载均衡机制就是在HTTPSF调度框架里面实现的。
网友评论