服务治理在猫眼娱乐的演进之路
现实业务背景下的挑战
我们开篇之前,我们先来讨论一个问题,我们谈到服务治理的时候,说的其实是什么呢?我们认为,服务治理的关键要素在于两点,人和系统,我们在系统层面希望服务能够按照业务愿景和架构师的理念进行运行和持续演进。而在人的层面,我们希望在这些系统之上工作的工程师能够取的最大的舒适感。听起来有点像废话,但确实是我们很容易在一些高大上的名词中忽视掉的点。在微服务设计一书中,其实也提到了类似的论点。猫眼的服务治理体系在几年的演进过程中,其实也碰到了一些问题。
猫眼作为国内互联网在线票务的领头羊,也在全娱乐领域在做持续深耕,目前有数千万的DAU,每日百亿级的调用和百万级的QPS。
在这个领域,猫眼目前面临的主要难点有如下几个,
从系统层面来说,主要面临的是系统稳定性的问题。大家都知道,每一家公司都会有一些数据资产是很敏感的,所以需要对于一些爬虫和恶意流量进行准实时的处理。另外,猫眼娱乐作为一个全娱乐领域的平台型公司,也会承载业务在大档期和大活动中流量几倍、十倍甚至百倍千倍的压力爆发,如何在这种情况下,保障系统的可用性,也是一个值得研究的问题。以及,故障是无法避免的,无论流量高低。所以如何保障系统的日常柔性可用,也是需要关注的问题
从人这一层面来说,主要面临的是人效问题。服务治理的各种中间件采用的总体上是一个富SDK的方式嵌入业务方。所以这必然会带来两个问题,多语言的情况下会带来更高的维护成本,以及SDK升级所会带来的和业务相互耦合掣肘的问题。
以上难点都在过往的几年中困扰着猫眼,也一定程度上阻碍了猫眼业务的发展。
举两个典型的例子,
在稳定性这方面,2017年春节档Redis千兆网卡打满带来的严重影响,我们常规时候的流量曲线会有两个规律的早晚高峰,而大家可以看到,在故障当天,因为网卡打满导致了非常长时间的服务不可用。这也是猫眼经历过的一个非常重大的事故。
而在人效方面,目前猫眼有1000+的服务,中间件的升级动辄半年以上的版本升级周期,这个其实对于业务方或者底层架构团队来说都是非常不友好的一个体验。
所以基于这些问题,猫眼开始开展服务治理的演进之路。
高可用治理中心在猫眼的落地
我们关注发现,猫眼主要面临的场景是大流量下的概率性故障。基于这样一个前提,我们需要展开了故障前、中、后全生命周期的分享优化。基于这样的一些背景,我们得出高可用治理在猫眼的落地理论架构是:面向故障全生命周期进行治理,悲观与乐观并济。
怎么理解这句话呢,从乐观角度上来说,我们通过各种测试和评估,我们的系统应该可以避免所有的问题。但是从悲观角度来看,基于墨菲定律,我们知道可能发生的就一定会发生,所以我们必须来看假定问题发生,我们能做什么。以及问题真的发生,我们又能做什么。
而高可用在猫眼落地,需要面临实际生产中的诸多难点与挑战,比如前面有提到的,如何应对永无止境的爬虫和恶意流量的攻击,如何应对大档期洪峰或者秒杀场景,另外,基于墨菲定律我们知道,故障是无法避免的。所以如何应对随时随地可能出现的各种上下游的故障,如何避免快速故障恢复可能带来的服务雪崩。这些都是做高可用中需要去应对的挑战,也是猫眼几年历程中真实出现的问题。
另外,高可用领域中,流控的常规解决方案是用富SDK来做的,而富SDK是否能满足业务快速滚动以及流控自身敏捷迭代升级的诉求呢?而如果将流控功能都剥离到Server端,我们知道分布式环境中会带来最大麻烦的就是网络,全部剥离到Server端的话如何保障实时性和可靠性?也就是所谓的Client Side或Server Side,我们应该如何抉择?
再者,我们有了限流、熔断、混沌工程等等的工具后,很容易就出现有工具但实际上用不好的情况,更有甚者,因为分布式网状拓扑下的这些限流熔断工具的滥用,反而可能将你的系统推向不可控的深渊。我们如何避免有术而无道的局面呢?
基于这样的一些考虑,我们开展了专项的治理行动,自研了猫眼高可用治理中心,代号大禹。旨在提供自动化的限流、熔断、降级、隔离、演练、监控报警的一站式可用性保障方案。由于篇幅限制,下面我们主要介绍系统的整体架构以及一些产品上的特点。更加具体的架构实现会在后续大会上去做逐步的披露。
首先,这个是我们高可用治理中心的一个分层架构。我们来做一个简单的介绍
在最上层,是我们的产品层,高可用治理中心目前提供出了恶意流量探测、演练、限流、熔断、降级的产品,同时,为了达到策略上线效率的最大化,我们还提供了可自定义规则的模块,我们将一条策略抽象为以断言、条件判断和处理三个组件为核心的一个表达式,这样就可以在无需前端任何产品级别开发的前提下完成策略的快速上线。同时你可以基于我们原子的策略组件的自由拼装,来实现你想要的自定义的策略能力。
左下角的演练管控中心则支撑起了演练产品,他底层依托阿里的Sandbox实现动态注入和销毁,同时协同猫眼的全链路压测平台来提供流量的模拟和调度。在最右边则是一些基础模块,包括全方位巡检和兜底容灾的模块,以及配置分发模块。
接下来,我们可以看到支撑产品形态的核心能力是一个策略引擎,除了演练模块之外,其余无论是哪个产品,底层都是由这个策略引擎进行支撑的。策略引擎提供了各种策略模块的支撑,并提供了复合的策略层能够进行自由的组合拼装来实现你所需要的策略。
而在策略引擎的底层依托于高可用治理中心的语法引擎,我们将策略的场景最近高层次的抽象,提取出四个模块——App、Pipeline、Stage、Component四部分。并通过高度内聚的语法解析器来进行解析。具体我们后面会有介绍。
所以这就是我们高可用治理中心的一个分层架构。
接下来,我们简要介绍一下,前面一直提到的语法引擎。可以看到,在我们的建模中,我们抽象出四个维度,比如我们希望对于下单请求进行限流策略控制。那么APP就是订单服务、Pipeline就是下单功能、Stage就是限流策略,这都是一对多的关系。最后策略会由很多最原子的策略组件Component所构成。
我们接着看,可以看到实际中,我们可以定义很多策略组件,比如特征维度、过滤器、动作、条件判断、恢复策略等等。而这些策略你可以定义实际组件和视图组件,来让你的语义尽可能地友好。所以你可以看到,有很多组件,他们在视图层和控制层其实是分离的,他们可以是一对一、多对一、一对多的关系。这样就可以让你的视图层尽可能友好的情况下,实现控制层最大程度的精简。
整个语法引擎的核心表达式即如标题右侧所示。如何解析由Component自己定义说了算。这样就实现了组件与组件之间的松耦合和自己内部的高内聚,用轻量级的原子解析,来避免将语法解析器做成一个难以维护的巨无霸产品。
右下方是一些简单的示例。可以看到,无论你是简单的频率限制、或者稍微复杂一点的反爬策略,再或者是状态机熔断这类规则,都可以通过我们的表达式轻松搞定。
最小固化约束下的松散解析,加上VC的分离,这就是猫眼高可用治理语法引擎的核心理念。
接下来,我们看下大禹的系统架构。系统架构上,我们目前分成了几大平台,包括流量控制平台、演练平台、健康度量平台、决策中心和租户隔离,其中右侧置灰的系统目前正在设计开发中。左侧的系统都已经大规模落地并经历了几年线上流量的考验。
下方有个演练平台,即用来进行压测模拟、故障模拟、预案验证。这边不做过多介绍。
上面流量控制平台主要负责进行对流量的限流、熔断、降级和实时监控,是流量的安全保护罩。业务流量会调用远端的流控服务进行实施的决策。
说到这,大家如果了解Servicemesh的可能会有点熟悉,这种远端实时控制的方式,是不是有点类似于Istio的Mier服务?是的,在我们16年启动的时候,也是经历了控制平面如何切割,Server还是Client为重的苦苦思索和探索,后来确定了猫眼的流控路线是Server Side为主的方式进行。这个路线也为后续猫眼的发展带来了非常大的便利。仅这个架构选型几年内节省了数百人天以上的投入。你们可能会有疑问,Mixer在Servicemesh中被认为是一个相对没那么成功的实现,那猫眼是如何规避的呢?我们接着往下看。
流控的核心如图所示,包含两大服务——断言服务和分析服务。断言服务主要用以快速判断流量是否符合预期,而分析服务用以进行流量的准实时分析,并将结果传递给断言服务。
这个流控核心里面有几个大的特色
时效性,我们采用了一些预测算法来进一步保障决策的实时性,同时,断言分析底层依赖同一套策略模型,所以你想要绝对的实时,我们也能够快速地让断言服务同时具备实时分析能力。目前能够做到无延时判断。
轻接入。采用API快速接入服务、轻SDK的方式,让运维升级成本下降70%以上。
高性能。
就近的本地缓存,并进行严格的Key控制来防止指数级增长撑爆业务进程的case出现。
分析和断言流程的快慢分离,比较重的分析流程异步化,进一步提升实时流量性能。
基于请求特征树缓存来加速特征提取,速度比Spring快50%以上。同时也通过这种方式对请求大小进行了压缩。
通过以上这些方式对性能的优化。目前我们的RT仅在9us。这个其实也就侧面解决了Mixer目前的最大困扰。
低成本。除了上述因为采用Server-Side为主进行一些重策略的处理所带来的运维成本大幅下降之外,策略底层是由一个个策略原子组件所构成,这些组件本身具备高度的可复用性,目前策略复用率在90%以上。
前面其实说到了猫眼面临的一些场景,我们这边可以来进一步感受下。比如最开始猫眼面临的锯齿状的恶意流量,如果攻击者或者爬虫做得好的话,我们一些场景下连这个锯齿都看不到。以及随时业务的快速发展,大档期来临时候的脉冲流量的冲击。再之后随着热门演唱会业务、以及银行活动等等业务的蓬勃发展,我们逐渐碰到了越来越高流量的秒杀场景。从最开始的一些核心系统QPS突增1k,到后来的突增近100w QPS。这给猫眼其实带来了非常高的技术挑战。而大禹中心是如何在限流上去做出反应的呢?
阶段一:恶意流量
在2016年及以前,我们其实面临了非常多的恶意流量、爬虫流量,在尝试对猫眼进行一些攻击和商业敏感数据的抓取,针对这个问题,我们研制了策略引擎,并基于此搭建了流控平台,在此基础上,在策略上,我们提供了一些精细化的行为探测,黑白名单,以及基于请求内容特征的决策策略。
实现的关键点主要有四个,第一,从0到1研制策略引擎,第二,策略需要支持任意维度的组合。第三,基于Redis采用滑动窗口计数。第四,进行行为和内容特征库的持续持久化积累。
在这个阶段,我们的业务特性要求我们的攻防处理时候,准确性的重要性大于实时。经过第一阶段的处理,我们也有不错的一个效果,比较明显的是我们的一些系统负载下来了,我们核心数据的反爬率到达了70%以上,且保持误伤率在百万分之三以下。
阶段二:大档期洪峰
到了下一个阶段的攻防,主要就是流量过载的防御,2017年大年初一的服务雪崩给业务带来了较大影响。大档期洪峰来临后,我们这个阶段要求实时性要进一步提升,而本来的这种异步分析的方式是无法满足我们需求的,我们研发了持续洪峰限流策略,基于去噪预测算法基于过去的历史数据来预测当前时刻的数据,准确率可以达到99.2%。这不到1%的一个qps牺牲来换取0延时的效果,是可以接受的。同时我们采用多级缓存、批量缓冲以及自然窗口计数的方式,进一步降低对Redis的依赖,彻底实现了Redis无热点情况下的集群持续洪峰限流策略。
除此之外,我们也提供了其他的能力。比如客户端限流,我们都知道限流越靠前效果越好,所以我们也将限流前置到了客户端层面。以及降级预案,我们可以选择非核心交易链路的功能,进行紧急情况下的部分流量降级和全部降级。
通过第二阶段的攻防,我们对于实时性有了更高的要求,准确性可以有接受小的牺牲。第二阶段完成后,接入的系统能够承载数百倍过载流量压力而不被压垮。
阶段三:秒杀
从2018年下半年开始,我们也不断衍生出银行活动和演出秒杀的场景。这类秒级别激增的流量,用预测算法已经无法很好支持,因为这种瞬时激增的脉冲流量是无法被预测的,更甚可能由于去噪预测算法会将其标识为噪点数据而导致误差的进一步放大。
所以基于这样一个背景,我们经过权衡之后,这个阶段我们在流控层面的优化主要在于入口层面单机限流+内部基础服务持续洪峰限流。主要两点考虑:
因为此时入口服务由于大流量的突发冲击,持续洪峰限流策略基于过去的预测算法会失效,已经难以满足要求,而猫眼入口服务的负载均衡策略的RR策略,而且能够更加实时地反应,所以单机限流策略能够取代前者发挥很好的作用。
同时内部服务的流量策略是多样的,且流量已经一定程度被打散,所以采用持续洪峰的集群式限流仍然可以起到作用,且相较于单机限流会更为保险不容易误判。
而从更全局上,我们和业务方协作,进行了“一控四化”的权衡,什么叫一控四化呢?即:
对系统进行兜底控制。如上面我们所做的单机+集群持续洪峰限流策略双管齐下。
对业务进行场景化。比如答题、玩游戏。
对业务进行异步化。比如排队
对资源静态化。一些内容和功能可以分发到CDN、服务端LB上去做静态化提速。
对资源进行隔离。无论是Set化、还是其他方式,对服务器、缓存、数据库等等的热点资源进行物理/逻辑层面隔离。
阶段四:百万级别QPS秒杀
但是呢,还没结束。随着业务的蓬勃发展,2019年演出迎来了百万级别的QPS秒杀场景,我们发现后续步骤用持续洪峰限流也已经无法满足要求,即使经过前面几个步骤的打散,后续步骤的一些系统qps仍然会有激增到很高的情况,我们的用户、订单服务都遭受到类似的问题冲击影响了系统的稳定性甚至导致关联业务受损。
所以这时候我们对于这类的场景,进行了如下几点的处理。
由于复杂服务拓扑部署结构和LB策略的不同,所以内部流量本身是不均匀的。无法直接对一些下沉在底部的基础服务直接使用单机限流。所以我们将业务流量通过RR策略重新导到流控服务端进行流量打平,再在流控Server端完成单机限流。
但是,这个对于QPS不高的场景,比如业务方设置QPS为150,但是当前流控服务端机器数只有100台,则最大会带来50%的误差,进而产生误判,这种情况是不可被接受的。所以我们针对QPS不高(5k)的场景,自动将其降级为击穿到Redis进行自然窗口计数实时分析判断。来解决这个误差问题。
前面提到分析和断言两个服务底层依赖的其实是同一套策略引擎,以便于同步、异步分析的随时切换。所以我们在这个策略中进行了策略引擎的同步化定制,同时将业务SDK缓存失效,以此在牺牲1-2ms性能的基础上达到绝对的实时。
同时,在后续流控马上会对其进行Set化。针对演出业务进行独立Set部署。避免大流量下的业务之间相互影响问题。
刚刚提到的是限流,现在说一下熔断。我们的高可用治理中心的熔断能力相比于业内的一些实现,也有自己的一些特别的地方。
我们能够支持精细化的过滤和错误判断标准的自定义
同时为了避免快速恢复导致服务雪崩,因而我们采用了阶梯式的熔断策略。有点类似于开手动挡的车,可以跨档降速,但不能跨档提速。通过这种方式,我们提供了这种快速熔断-阶梯恢复的策略。
我们目前基于猫眼业务主要提供的是基于错误率的判断,但我们都知道,当流量较低的时候,按照错误率来判断很容易造成误伤,而流量很低的时候,我们进行熔断的现实意义也不是很大,所以我们也设置了当前熔断策略启动的流量阈值。
One More Thing
提完了限流、熔断。之后不得不说道目前业内提到的一个越来越流行的做法,即基于类似BBR的TCP拥塞控制算法的方式来进行自动化的处理,来避免各种参数配置带来的不可控的成本和影响。这是一个非常好的思路。
但是这种类BBR的算法的启动阈值判断是需要多维考虑的,而不能就仅仅基于业内目前提供的这种基于cpu load的方式来做。这样对于低load的场景无法适用,我们认为应该有一些更智能更全面的评估方式来评估系统的健康度,当系统因为流量原因马上要出问题的时候,再启动对应的BBR策略。即,我们不能将系统健康直接粗暴地等同于cpu使用率或者load高这样的单一判断。比如基于QPS、基于load、基于错误率、基于容量评估、基于链路判断等等。
这其实给我们提供了很大的想象空间的同时也能解放人力,是未来猫眼希望去深耕的一个方向。
我们除此之外,还提供了演练的能力,进行了混沌工程的落地实践。我们的演练产品进行了全链路压测和故障演练的打通,可以通过调度压测的流量来进行演练,对应的预案能够在演练过程中不影响线上真实流量。
同时,我们知道,为了尽量降低风险,一般的演练都会放到晚上高峰期之后再进行。所以针对这种情况,我们也实现了在灰度链路上进行演练,以此来大幅削减演练的成本。两年以来,我们累计进行十余次大型演练,发现了超百个问题,包含数十个致命级别的问题。这说明,我们的演练事实上发挥了非常关键的作用。
介绍一下大禹的精细化运营。我们能够进行秒级别的流量探测(精确到1s),同时提供了多维度的图表来便于日常使用者和运维方的使用,也提供了自动巡检的能力,来对接入的业务方进行几个维度的巡检来简单判断他的健康状态。
同时,我们也提供了“流量预观察”的能力。因为我们知道,一条策略上线是否可靠,是否会阈值设置有问题带来一些不可控的影响,上线之前基本业务方心里是没有底的。这个时候我们提供了这种预观察的能力,能够模拟策略生效,让你实时观察实际的影响。从而给你提供了“反悔”的机会。这种能力在我们的限流熔断足够智能化无参化之前,是一个极其重要的能力。
当然,稳定性保障仅靠高可用治理中心是不够的,业务层面,也进行了大量的持续优化。我们来看下在猫眼2018年稳定性保障持续深耕之后的整体非功能性的故障趋势:
可以看到,P2及以上故障下降了46%,无P1级别故障。和前面几年形成了鲜明的反差。这也侧面体现了猫眼在稳定性保障这个领域的努力,以及高可用治理中心的价值。
结语
这是猫眼服务治理几年发展以来,在稳定性保障方面基于业务痛点去做出的一些工作。
推荐阅读
站长 polarisxu
自己的原创文章
不限于 Go 技术
职场和创业经验
Go语言中文网
每天为你
分享 Go 知识
Go爱好者值得关注