饿了么分布式服务治理及优化经验

【作者简介:兰建刚,饿了么框架部门技术总监,前爱立信首席软件工程师,10 年以上高可用性,高并发系统架构设计经验。现饿了么框架工具部负责人,负责饿了么中间件的设计及实施,通过中间件以及研发工具的辅助提升研发人员的工作效率,提升网站的稳定性及性能。】

发布日期:2016-08-18

本文移除部分一官话,套话和废话;可以参考原文进行对比;


MVP 原则

先讲一下 MVP 原则,MVP(Minimum Viable Product) 现在比较火,一个产品是做大而全,还是可用就行?我从去年 3 月份加入饿了么,开始组建框架工具的团队。中间件里面很多东西都可以去做,但是我真的需要把所有的东西都做全吗还是 MVP 原则就好?这是我们思考的一个问题。

MVP 的意思就是做一个最小可用的就可以,大家以前很流行说,“世界那么大,我想去看看”,其实框架很多东西看看就好,做全做好是需要长时间积累的,我们缺的恰恰是时间。我们要做的就是立足现状,解决痛点问题。现在饿了么的现状说白了比百废待兴好一点。当有太多事情可以去做的情况下,更需要抓住重点,不死人的尽量不要去踏。

服务治理的现状

服务治理是一个很大的话题,它涵盖了很多内容;

编程语言

饿了么语言主要有两种,PythonJava,原来整个公司语言都是以 Python 为主,可以说是上海最大的 Python 大厂。为什么不坚持用 Python?不是说 Python 语言不好,而是招不到人。在业务急速发展的时候怎么办?换 Java 语言就成了自然的选择。

据另外一个 eleme 的高人表示:引入 java 的原因是由于使用各种开源组件的需要;另外,最早 eleme 的开发语言为 php ,当前尚残留部分;

在我进公司的时候,其实不仅仅是这两种语言,还有 PHP,C 语言等。基于这些现状,框架的选择点就比较少。因此做了一些妥协,SOA 的框架有两套,主要是为 Python 和 Java 做的,Python 的叫 Vespense,Java 版本的叫 Pylon

SOA 框架

**SOA 框架里面需要包含什么?**首先必须包含 RPC,我们的 RPC 有两种:ThriftJSON。Python 使用 Thrift,Java 使用 JSON。为什么 Java 框架重新选择一套 RPC 协议? 主要是觉得 Thrift 对 Java 不太友好。举个例子,用 Thrift 生成的 Java 代码在接口比较多的时候,它的一个文件就超过 20M ,连 IDE 都拒绝分析这个文件。另外,因为当初没有日志系统,也没有链路跟踪系统,所以在排查问题的时候,一种好的办法就是抓包,如果是一个二进制的协议的话那就痛苦,而 JSON 是纯文本格式满足需要,所以最终 Java 选择了 JSON。当然 RPC 都是对业务透明的,SOA 框架会屏蔽 RPC 细节,业务就像使用本地调用一样使用远程服务。

路由我们是基于集群做的,没有进一步细化到机器级别,因为觉得这个就足够了。此外也做了客户端的 SLB,还有_熔断_、降级限流,这是保护服务的几大法宝,充分证明了这些东西拯救了我们很多次。还有一些_埋点_,_全链路跟踪_等等,全部都内嵌在框架里面。这样的好处就是,增加特性只要升级一个框架就可以了。

  • 客户端 SLB 是通过 eleme 自己研发的 goproxy 实现的,功能类似 haproxy ,但增加了其他功能;

  • 全链路跟踪就是基于埋点实现的;

服务发现与配置中心

我们的服务发现和配置中心叫 Huskar,它是基于 ZooKeeper。

TODO

负载均衡

负载均衡有好几种,首先有嵌在** SDK 里的软负载**,拿到一个服务全部的列表,做一个轮循就可以了。这种策略有一些不足,比如一个 IDC 里面机器型号性能可能会稍微不一样,如果单纯用轮循会产生负载不均的问题。但这个问题当前还不是最紧迫的,我们可以绕过去,只要保证同一个集群里机器型号都是相同的就好。

这里没说清楚是什么 SDK ,通过上下文应该是嵌入到业务中的各种语言的 SDK ;

此外也用中间层的方式,以前我们的 haproxy 用起来比较麻烦,配置复杂,而且它不能进行热加载,一个配置上去了之后需要重启一遍,因此工程师就用 Go 语言写了一个 GoProxy,基于四层,它会从服务中心把你需要的列表全部抓下来,做四层的负载均衡。他可以代理一些没有 SOA 框架支持的语言写的服务,也可以代理其他的基础组件,比如说像 Redis,数据库,它都会代理。

这里可以看出 goproxy 和 haproxy 的主要差别:实际上就是通过主动拉取的方式从服务注册中心将相关信息加载到内存中,避免了基于配置文件无法解决服务动态变化的问题;

CI / CD 灰度发布

我们有 CI、CD 灰度发布的系统,叫 eless,虽然我们做了 CD ,但是百废待兴,目前只有一些基本的单元测试,因为这个太耗工夫了。灰度发布也是基于发布系统做的,我们会在发布系统里面定义集群,每个集群里面的机器又分成不同的组,发布的时候按这些组来发布的,你可以先发一个组,观察没有问题后,然后再发其他的组。

监控与报警

我们有自己的监控和报警系统。监控系统现在做的比较简单,是** statsd + graphite + grafana** 的组合。现在最大的问题是监控系统不支持 tags,所有的指标汇聚到一起,一个服务的指标是汇聚在一起的,一个机器或者集群慢了,它会把这些指标分摊到其他机器或者集群上去的,所以查的时候比较困难,所以我们现在准备切成我们自己的系统。

报警系统也是自己写的。报警系统的需要是快、全、准。我们现在做的是全,逼着大家去把报警系统用起来,只看监控系统是看不过来。报警最常见的基于阈值,各种功能指标的阈值如何去配,需要经验指导。所以一个同事提出基于趋势来配置来判断,我们在一段时间发现趋势在偏离了,就做报警。

这里说的内容有点 out :eleme 内部其实使用了多套监控系统,有小米的 Open-Falcon ,有上面说的 statsd + graphite + grafana 一套东东,还有自研的 eleme system monitor (esm) ;

我们也在做 trace,前两天终于把拓扑图给画出来,把一个业务所有的调用展示成一个调用树,这样就可以很好的分析业务。我们现在是一个近千人公司,业务系统极度复杂,很少有工程师能清晰说出一个业务到底调用了哪些服务,通过这种 trace 方式来做辅助分析就很有用了。

这套系统称为 etrace :通过在业务客户端 sdk 和服务 sdk 中埋点,基于 request id 和 sequence id 等内容将调用链串起来;

光展示,我们觉得它的价值还没有利用到极致,可以把所有的调用关系和报警结合在一起。大家分析问题的时候,会发现如果某个点上发生的错误一直往上报,从而导致整条链路失败,那这个点就是 root cause,把它修复就可以问题解决了。我们做 trace 的思路是一样的,在调用链上进行着色,辅助找到问题的 root cause,也就是最初发生问题的那个点。

这个尚未做到

服务治理的经验

我们最重要的经验是做好保护与自我保护

“不能被烂用的框架不是好框架”:你无法预测每个开发者会怎么使用你的框架,即使框架被滥用了,最坏的情况,也需要保证能够活的下去。

写的东西都严格要求自我状态的检查,比如秒杀的时候,所有的监控系统,链路跟踪系统都是可以降级的,不能因为这些东西导致整个系统崩溃。

框架由于在底层,出了问题最容易被怀疑。比如一个 SDK,使用方说为什么占用了整个集群上 8% 的 CPU?跑过去一看,整个机器的 CPU 才 12%。某种程度做框架其实有无助的时候,容易被质疑及谴责,所以做好自我状态检查是很必要的。

定期线上扫描

为了避免滥用的问题,我们会定期线上扫描。比如一些日志本来就是可以降级可以丢的,但如果开发用了写文件的同步方式,那性能就会变慢,通过扫描发现这些问题,改成异步日志服务性能就会更好。

限流、熔断、降级

服务不行的时候一定要熔断。限流是一个保护自己最大的利器,做任何框架都会有限流措施。

木有提及降级问题

连接复用

对一些基础服务来说,比如 Redis,数据库,连接是个昂贵的消耗。所以我们一些中间件的服务都实现了连接复用的功能。

在 RabbitMQ 的使用上也遇到了短链接导致的性能问题,该问题由本人提出并解决;

代码发布

上线发布是很危险的一件事情,绝大部分的事故都是由发布引起的,所以发布需要跟很多系统结合起来。在每次发布的时候,与发布相关的调用链分析就开始了,通过指标对比,可能发现到底哪些指标发生了改变;如果指标有异常,就触发报警并打到监控主屏上去;如果出了什么问题,则优先回滚,之后再进行问题的解决。

服务治理的痛点

配置复杂

超时配置:超时配多少是合适的?100ms?300ms?极端情况有些业务配到 3 秒的。阈值怎么配和超时怎么配其实是同一个概念,并不是所有的程序员都知道超时设成多少合适。解决办法:你的监控系统,调用链分析系统,日志系统和基础监控系统每天会产生多少数据?是否有用?是否可以从中挖掘出一些东西?比如推导出超时配置的合理值;

线程池配置:刚才说最重要的是限流,因为不可能无限制的接受请求;线程池的配置要经过严格的性能测试,做很多调整才能调出来。在做性能测试的时候,可以发现曲线的最高点;如果能够实时计算出最高点的位置就好了,但这个比较困难。

我们便用了另外一种方法,每个线程池用一个排列队列,当我发现它在涨的时候我就适当把那个线程池扩大一点,同时我们监测其他指标。如果发现在我扩大并发量的时候这些指标产生了报警,那我就把这个线程调整的操作直接拒绝掉,就保持原来那个大小。如果这些指标证明是没有问题的,那我就把它再扩大一点。

这段说明了什么呢?说明了并发量大到一定程度时,系统中其他服务能力不足了,因此才暂时不调整线程池大小;呵呵

Cache,DB,Queue 的手工配置问题。还有一个是服务治理,Redis、数据库等配置还都是手工的,我们也不知道我们线上有 Redis,怎么办?我们正在做基础服务的服务化,业务其实不需要关心到底连到哪个 Redis,你上线的时候你告诉我你需要多大的容量,填个工单过来,运维帮你配好了,然后通过一些自动化的方式你把这些拿到初始化 SDK 就可以了。

目前已经有一些提供服务注册功能的组件存在,解决了部分服务治理问题;但由于相关信息在多个地方存有保存,因此存在不一致问题,以及脏数据问题;另外,维护这些信息的服务由不同部门、不同的人负责,沟通成本略高;很多服务那么只有 web 接口,要么只能本地访问,要么文档不全,使用起来甚为不变;

故障定位

还有一个比较痛的问题就是排查问题很难。首先故障定位困难,每次我们出了事情之后,大家各自查各自的,比较低效。问题排查其实是有方法可以做,需要把它自动化,我们现在还缺这个东西,调用链分析是需要考虑去做。

性能退化

我们现在的业务增长量非常恐怖,去年我们是以 5 倍的速度增长了,但其实这个 5 - 10 倍要看你的基数,当基数很大,扩一倍量也是非常多,评估线上到底要布多少台机器是一件很复杂的事情。

我们成立一支性能测试团队,做全链路的压测。基于全链路压测的结果来评估整个系统的容量。这个全链路只能在线上做,也不能在白天压,只能在晚上低峰期的时候做。所以性能测试也是一个比较挑战的工作,不仅仅是智力上,也是身体上的一种考验。

全链路压测试一些服务有时候出现性能下降,比如 QPS 从 500 下降到了 400,但大家并不知道最直接的原因。上次毕洪宇老师也帮我们出了主意,比如把全链路指标拉出来做一下对比,看看哪些指标有变化,可能就是罪魁祸首。

容量评估的方法

容量评估方面容易出现温水煮青蛙的事情,今天流量增长一点没问题,明天再增长一点也没有问题,连续几天然后服务就挂了。这个时候怎么办?只能用最苦逼的方法,找个性能测试团队进行压测。有没有更智能化的方法?我们也正在探寻这条道路。

资源利用率低

资源利用率低的问题。很多团队一碰到性能下降,就希望扩容,这会导致很多时候机器利用率只有 10%(更新一下数据,其实很多服务器的利用率不足1%)。我们正在积极准备上容器化方案来解决这个问题。

资源利用率低的问题还有另外一个方面,就是有些服务资源利用不合理的高;目前已经会定期输出报表指出那些人头下属的东东耗费了多少xxx资源(全是钱啊);

服务依赖不清晰

大的系统中,服务依赖的调用链相当复杂,一个业务下去到底调用了哪些服务比较难说清。我们已经在做一个泳道规划。泳道这件事情有多种说法,有的人喜欢所有的服务都做一个大池子,只要保证它的足够容量就可以了,但是我更倾向小集群的思路,因为隔离起来就会更安全。

泳道?甬道?

我们现在还没有做到按用户来区分泳道,目前只是按流量来切,50% 随机,部署的东西都一样。我们想通过泳道把这些流量隔离,VIP 客户可以把他放最重要的泳道里面,一些不那么重要的城市,可以放到另一个集群,如果不得已降级,只能牺牲这些次重要的用户。

按照我目前的理解和了解,甬道的实现也就是通过业务的 appid 和部署 cluster info 来进行划分,保证信息相同的内容位于同一个甬道内,以便进行隔离;

服务治理的方向

有痛点就要努力,要么放弃,要么努力,这是我们努力的方向,前面讲了一下智能流控系统,超时推荐我们也做,大数据和智能化才是将来。有些监控数据只是落在磁盘上不用那就是浪费,是不是能把它利用起来?

智能流控?超时推荐?我咋没看到有讲~

然后我们也在做 Cache、数据库、Queue 等服务化。

Trace 系统我们也在做,拓扑图画出来,帮助大家了解是怎么回事,我们可以做链路染色,帮你了解问题的根源在哪里,我们也可以做依赖度的分析。我们说依赖分两种,强依赖和弱依赖。弱依赖情况下,当异常出来的时候可以直接将相应的服务干掉,防止异常传播给调用链顶端的业务,导致整个服务都挂掉;但如何确定是弱依赖还是强依赖则需要进行分析。

又扯到了 trace 系统,补充一句,这里说的链路染色,其实是指业务调用链路的标识,能够看出来业务调用走的路径,借此发现某些服务的异常;针对链路染色,其实还有针对网络流量方面的,目前没有这部分内容;

容量预警需要通过做一些大数据的分析,所有的指标跟订单量这些关联,做一个相似度的分析,当这些指标偏离的时候,是不是可以认为它的容量有问题,当然这是努力的方向。

这个已经开始做了

容器方面我们也在做,系统叫 APPOS,有的服务 CPU 只用了10%,但是我们规定了一台服务器只能装一个服务怎么办,那就上容器吧。

容器方面 eleme 目前还比较落后

Q&A

提问:想问一下报警阈值设定,上面提到的方法是根据趋势来设计报警,这个趋势其实您能做到多少准确?能控制在什么情况下?

兰建刚:准确度的确是个问题,我们用了一个算法叫 3-sigma,准确度还不是特别确定,因为这个东西真的是服务治理里面最大的难题,报警分级怎么分?这是很大的学问,我们现在整个报警系统里面报警通道每天上千个报警,很多都不看的,因为觉得这个报警没什么意义。这是一个实际当中要去调整的问题。

提问:报警存在误报的情况?

兰建刚:对,你要知道我们的业务有两个明显的尖峰,十二点和下午四五点的时候都是订餐的高峰,之后则所有的指标都会有下降趋势的时候,如果你曲线偏离的很厉害就会引发报警。

多指标聚合是我们正在做的,发生一个指标报警的时候可能是一个小问题,但是这个问题会触发一个 CEP 的流程,比如“是不是 CPU 飙高的同时响应时间会抖动?”,我们可以定义这样整个一套规则,去做报警来提高准确度。

提问:刚才您提到用 Go 写的一个东西,为什么选择使用 Go ?因为 Go 有自己的垃圾回收机制?而且我想了解一下您认为 Go 它是比较适合什么样的系统?

兰建刚:当然是底层的基础服务了,我们不建议用 Go 写业务。为什么我们选 Go 做工具,是因为我前面提到,公司原来一些工具是搭在 Python 上,有一帮 Python 工程师,让他去写 Java 他是绝对不干的,但是让他去写 Go 语言是没有问题的,Python 其实不适合写底层框架,因为它是个动态语言,工程化方面也会差一点。

提问:刚刚看到您提到超时配置推荐,这块是和你们的应用场景有关系,还是说和你们遇到的故障?

兰建刚:就是因为遇到故障了,因为很多超时配得很乱,有的同学直接配 3 秒超时(这是配置模版里的一个例子,很多同学就拿去直接用了),那还不如不配,有些情况很多服务就是 10ms 就正常返回了。只要保证这件事情对绝大部分的服务来说,是有利可图的,那我们就去做这件事情。

转载于:https://my.oschina.net/moooofly/blog/768382

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值