概述
导读:高可用架构 7 月 30 日在上海举办了『互联网架构的基石』专题沙龙,进行了闭门私董会研讨及对外开放的四个专题的演讲,期望能促进业界对互联网基础架构的建设及发展,本文是兰建刚分享饿了么服务治理经验。
兰建刚,饿了么框架部门技术总监,前爱立信首席软件工程师,10 年以上高可用性,高并发系统架构设计经验。现饿了么框架工具部负责人,负责饿了么中间件的设计及实施,通过中间件以及研发工具的辅助提升研发人员的工作效率,提升网站的稳定性及性能。
今天我想站在一个大的角度上,看一下饿了么最近一年多的时间,经历的技术上一些痛苦的问题与改进的过程。
为什么讲比较痛苦的事情?昨天和一位专家聊天受益很大,他说人在什么时候能够自我驱动?就是痛苦的时候。只有感到痛苦,才会有改变。当然改变有两种结果,一种是彻底放弃沉沦,另外就是一想办法自动化、智能化,把自己变成一个高手。
MVP 原则
我现在也很痛苦,但是还没有放弃。先讲一下 MVP 原则,MVP(Minimum Viable Product) 现在比较火,一个产品是做大而全,还是可用就行?我从去年 3 月份加入饿了么,开始组建框架和工具的团队。中间件里面很多东西都可以去做,但是我真的需要把所有的东西都做全吗还是 MVP 原则就好?这是我们思考的一个问题。
MVP 的意思就是做一个最小可用的就可以,大家以前很流行说,“世界那么大,我想去看看”,其实框架很多东西看看就好,做全做好是需要长时间积累的,我们缺的恰恰是时间。我们要做的就是立足现状,解决痛点问题。现在饿了么的现状说白了比百废待兴好一点。当有太多事情可以去做的情况下,更需要抓住重点,不死人的尽量不要去踏。
服务治理的现状
服务治理是一个很大的话题,它涵盖了很多内容,比如前面晓波老师介绍的 Redis 治理、姚捷老师讲的链路监控系统(参看文末文章),都可以涵盖在里面。
编程语言
先介绍语言,刚才会场一些人说他们是异构的语言,但可能还是没有饿了么这么复杂。饿了么语言主要有两种,Python 及 Java,原来整个公司语言都是以 Python 为主,可以说是上海最大的 Python 大厂。为什么不坚持用 Python?不是说 Python 语言不好,而是招不到人。在业务急速发展的时候怎么办?换 Java 语言就成了自然的选择。
在我进公司的时候,其实不仅仅是这两种语言,还有 PHP,C 语言等。基于这些现状,框架的选择点就比较少。因此做了一些妥协,SOA 的框架有两套,主要是为 Python 和 Java 做的,Python 的叫 Vespense,Java 版本的叫 Pylon,Vespense 和 Pylon 都是星际争霸里面的两种最基本的东西,没有这两种东西游戏根本打不下去。
SOA 框架
SOA 框架里面需要包含什么?首先必须包含 RPC,我们的 RPC 有两种:Thrift 和 JSON。Python 使用 Thrift,Java 使用 JSON。为什么 Java 框架重新选择一套 RPC 协议? 主要是觉得 Thrift 对 Java 不太友好。举个例子,用 Thrift 生成的 Java 代码在接口比较多的时候,它的一个文件就超过 20M ,连 IDE 都拒绝分析这个文件。另外 JSON 是纯文本的,因为当初也没有日志系统,也没有链路跟踪系统,排查问题的时候,一种好的办法就是抓包,如果是一个二进制的协议的话那就痛苦。所以最终 Java 选择了 JSON。当然 RPC 都是对业务透明的,SOA 框架会屏蔽 RPC 细节,业务就像使用本地调用一样使用远程服务。
路由我们是基于集群做的,没有进一步细化到机器级别,因为觉得这个就足够了。此外也做了客户端的 SLB,还有熔断、降级、限流,这是保护服务的几大法宝,充分证明了这些东西拯救了我们很多次。经常看到监控群里面说,我们把什么什么服务限流了吧,把它降级了吧,那是因为这个服务可能写的不太好,把它降掉了,保住我们的主要业务。还有一些埋点,全链路跟踪等等,全部都内嵌在框架里面。这样的好处就是,增加特性只要升级一个框架就可以了。
服务发现与配置中心
我们的服务发现和配置中心叫 Huskar,这是 DOTA 里面的一个英雄人物的名字,它是基于 ZooKeeper。
负载均衡
负载均衡有好几种,首先有嵌在 SDK 里的软负载,拿到一个服务全部的列表,做一个轮循就可以了。这种策略有一些不足,比如一个 IDC 里面机器型号性能可能会稍微不一样,如果单纯用轮循会产生负载不均的问题。但这个问题当前还不是最紧迫的,我们可以绕过去,只要保证同一个集群里机器型号都是相同的就好。
此外也用中间层的方式,以前我们的 haproxy 用起来比较麻烦,配置复杂,而且它不能进行热加载,一个配置上去了之后需要重启一遍,因此工程师就用 Go 语言写了一个 GoProxy,基于四层,它会从服务中心把你需要的列表全部抓下来,做四层的负载均衡。他可以代理一些没有 SOA 框架支持的语言写的服务,也可以代理其他的基础组件,比如说像 Redis,数据库,它都会代理。
CI / CD 灰度发布
我们有 CI、CD 灰度发布的系统,叫 eless,虽然我们做了 CD 但是百废待兴,目前只有一些基本的单元测试,因为这个太耗工夫了。灰度发布也是基于发布系统做的,我们会在发布系统里面定义集群,每个集群里面的机器又分成不同的组,发布的时候按这些组来发布的,你可以先发一个组,观察没有问题后,然后再发其他的组。
监控与报警
我们有自己的监控和报警系统。监控现在做的比较简单,是 statsd + graphite + grafana 的组合。现在最大的问题是监控系统不支持 tags,所有的指标汇聚到一起,一个服务的指标是汇聚在一起的,一个机器或者集群慢了,它会把这些指标分摊到其他机器或者集群上去的,所以查的时候比较困难,所以我们现在准备切成我们自己的系统。
报警系统也是自己写的。报警系统的需要是快、全、准。我们现在做的是全,逼着大家去把报警系统用起来,只看监控系统是看不过来。如果线上发生了一个故障,比如交换机发生故障,影响到某个业务,但是业务报警没有报出来,那业务要承担连带责任,因为你没有报警出来。
报警最常见的基于阈值,阈值这件事情比较痛苦,我们有很多指标,但这个阈值怎么去配,需要很有经验的人才能配好,阈值配小了,你会经常收到报警,配太大有可能出问题收不到报警,这个非常痛苦。所以一个同事提出基于趋势来配置来判断,我们在一段时间发现趋势在偏离了,就做报警。
我们也在做 trace,前两天终于把拓扑图给画出来,把一个业务所有的调用展示成一个调用树,这样就可以很好的分析业务。我们现在是一个近千人公司,业务系统极度复杂,很少有工程师能清晰说出一个业务到底调用了哪些服务,通过这种 trace 方式来做辅助分析就很有用了。
光展示,我们觉得它的价值还没有利用到极致,可以把所有的调用关系和报警结合在一起。大家分析问题的时候,会发现如果某个点上发生的错误一直往上报,从而导致整条链路失败,那这个点就是 root cause,把它修复就可以问题解决了。我们做 trace 的思路是一样的,在调用链上进行着色,辅助找到问题的 root cause,也就是最初发生问题的那个点。
服务治理的经验
我们最重要的经验是做好保护与自我保护。
“不能被烂用的框架不是好框架”,这个是我们 CTO 经常说的一句话。原因是什么?比如我们那个监控的 SDK 曾经被业务错误的使用,每发一次报警就启一个新线程,最后整个进程因为开了太多的线程挂掉了。峰哥(CTO)说你无法预测每个开发者会怎么使用你的框架,即使框架被滥用了,最坏的情况,也需要保证能够活的下去。
所以后面写的东西都严格要求自我状态的检查,比如秒杀的时候,所有的监控系统,链路跟踪系统都是可以降级的,不能因为这些东西导致整个系统崩溃。
框架由于在底层,出了问题最容易被怀疑。比如一个 SDK,使用方说为什么占用了整个集群上 8% 的 CPU?跑过去一看,整个机器的 CPU 才 12%。某种程度做框架其实有无助的时候,容易被质疑及谴责,所以做好自我状态检查是很必要的。
定期线上扫描
为了避免滥用的问题,我们会定期线上扫描。比如一些日志本来就是可以降级可以丢的,但如果开发用了写文件的同步方式,那性能就会变慢,通过扫描发现这些问题,改成异步日志服务性能就会更好。
限流、熔断、降级
这个强调多少遍都不过分,因为确实很重要。服务不行的时候一定要熔断。限流是一个保护自己最大的利器,原来我们前端是用 PHP 做的,没有自我保护机制,不管有多少连接都会接收,如果后端处理不过来,前端流量又很大的时候肯定就挂了。所以我们做任何框架都会有限流措施。
有个小插曲,我们上 DAL (数据库中间件)第一版的时候,有次一个业务怎么指标突然降了 50%,然后大家去查,原来 DAL 做了限流,你不能做限流,你把它给我打开,听你们的我打开了,打开了然后数据库的 QPS 瞬间飙到两万,业务部门就慌了,赶紧邀请我们再给他限制住。这是我觉得 DAL 做的最好的一个功能。
连接复用
还有连接复用,有些工程师并不能特别理解,如果不用连接池,来一个请求就发一个连接怎么样?这样就会导致后端资源连接过多。对一些基础服务来说,比如 Redis,数据库,连接是个昂贵的消耗。所以我们一些中间件的服务都实现了连接复用的功能。
代码发布
上线发布是很危险的一件事情,绝大部分的事故都是由发布引起的,所以发布需要跟很多系统结合起来,所以我们做了整套流程。在每次发布的时候,一个发布事件开始,到我们这个监控系统以及调用链上,调用链就开始分析了,发布后把它的所有指标比一比,到底哪些指标发生了改变,这些指标如果有异常,就发报警,所有发布都会打到监控的主屏上面去,如果出了什么问题,这些事情优先回滚,如果可以回滚,我们肯定第一时间就把问题解决掉了。
服务治理的痛点
配置复杂
超时配置:超时配多少是合适的?100ms?300ms?极端情况有些业务配到 3 秒的。阈值怎么配和超时怎么配其实是同一个概念,并不是所有的程序员都知道超时设成多少合适。那怎么办?峰哥(CTO)想了一个办法,你的监控系统,你的调用链分析系统,你的日志系统,基础监控系统每天产生多少数据?这些数据到底有没有用?是否可以从这些数据里面挖掘出一些东西,比如这种超时的配置,是可以基于它历史的超时配置、所有请求的响应时间去做的。
这件事情正在进行中,但落地有点麻烦,比如说我们请求大概每天有三千万的调用量,你只有很小的一个比例它会超时,但是绝对量是很大的,你设置一个超时值,它可能有三万个请求都失败了。现在一直在优化这个东西,希望下次大家来我们这里的时候,能给大家详细介绍一下这个超时到底怎么做。
线程池配置:刚才说最重要的是限流,你不可能无限制的接受请求,不可能一百个并发你就接收一百个并发,并发到底怎么配?又是很复杂的事情。我经常看我们线程池的配置,这个东西要经过严格的性能测试,做很多调整才能调出来。在做性能测试的时候,其实有条曲线的,有个最高点的,我们在想在实时的过程中计算出这个最高点,但发现这个东西其实挺难的。
我们便用了另外一种方法,每个线程池用一个排列队列,当我发现它在涨的时候我就适当把那个线程池扩大一点,同时我们监测其他指标。如果发现在我扩大并发量的时候这些指标产生了报警,那我就把这个线程调整的操作直接拒绝掉,就保持原来那个大小。如果这些指标证明是没有问题的,那我就把它再扩大一点。
Cache,DB,Queue 的手工配置问题。还有一个是服务治理,Redis、数据库等配置还都是手工的,我们也不知道我们线上有 Redis,怎么办?我们正在做基础服务的服务化,业务其实不需要关心到底连到哪个 Redis,你上线的时候你告诉我你需要多大的容量,填个工单过来,运维帮你配好了,然后通过一些自动化的方式你把这些拿到初始化 SDK 就可以了。
故障定位
还有一个比较痛的问题就是排查问题很难。首先故障定位困难,每次我们出了事情之后,大家各自查各自的,比较低效。问题排查其实是有方法可以做,需要把它自动化,我们现在还缺这个东西,调用链分析是需要考虑去做。
性能退化
我们现在的业务增长量非常恐怖,去年我们是以 5 倍的速度增长了,但其实这个 5 - 10 倍要看你的基数,当基数很大,扩一倍量也是非常多,评估线上到底要布多少台机器是一件很复杂的事情。
我们成立一支性能测试团队,做全链路的压测。基于全链路压测的结果来评估整个系统的容量。这个全链路只能在线上做,也不能在白天压,只能在晚上低峰期的时候做。所以性能测试也是一个比较挑战的工作,不仅仅是智力上,也是身体上的一种考验。
全链路压测试一些服务有时候出现性能下降,比如 QPS 从 500 下降到了 400,但大家并不知道最直接的原因。上次毕洪宇老师也帮我们出了主意,比如把全链路指标拉出来做一下对比,看看哪些指标有变化,可能就是罪魁祸首。
容量评估的方法
容量评估方面容易出现温水煮青蛙的事情,今天流量增长一点没问题,明天再增长一点也没有问题,连续几天然后服务就挂了。这个时候怎么办?只能用最苦逼的方法,找个性能测试团队进行压测。有没有更智能化的方法?我们也正在探寻这条道路。
资源利用率低
资源利用率低的问题。很多团队一碰到性能下降,就希望扩容,这会导致很多时候机器利用率只有 10%(更新一下数据,其实很多服务器的利用率不足1%)。我们正在积极准备上容器化方案来解决这个问题。
服务依赖不清晰
大的系统中,服务依赖的调用链相当复杂,一个业务下去到底调用了哪些服务比较难说清。我们已经在做一个泳道规划。泳道这件事情有多种说法,有的人喜欢所有的服务都做一个大池子,只要保证它的足够容量就可以了,但是我更倾向小集群的思路,因为隔离起来就会更安全。
我们现在还没有做到按用户来区分泳道,目前只是按流量来切,50% 随机,部署的东西都一样。我们想通过泳道把这些流量隔离,VIP 客户可以把他放最重要的泳道里面,一些不那么重要的城市,可以放到另一个集群,如果不得已降级,只能牺牲这些次重要的用户。
服务治理的方向
有痛点就要努力,要么放弃,要么努力,这是我们努力的方向,前面讲了一下智能流控系统,超时推荐我们也做,大数据和智能化才是将来。有些监控数据只是落在磁盘上不用那就是浪费,是不是能把它利用起来?
然后我们也在做 Cache、数据库、Queue 等服务化。
Trace 系统我们也在做,拓扑图画出来,帮助大家了解是怎么回事,我们可以做链路染色,帮你了解问题的根源在哪里,我们也可以做依赖度的分析。我们说依赖分两种,强依赖和弱依赖。弱依赖要处理它,有一个异常出来的时候要把它干掉,不能把这个异常跑到最上面去,那整个服务就都挂掉了,但是大家并不知道到底它是弱依赖还是强依赖,这需要分析,我们去统计一下,它是一个强依赖还是弱依赖。然后弱依赖就可以做一些改进,比如做一些异步调用,节省整个服务的调用时间,优化用户体验。
容量预警我们通过做一些大数据的分析,所有的指标跟订单量这些关联,做一个相似度的分析,当这些指标偏离的时候,我们是不是可以认为它的容量有问题,当然这是努力的方向。
容器方面我们也在做,系统叫 APPOS,有的服务 CPU 只用了10%,但是我们规定了一台服务器只能装一个服务怎么办,那就上容器吧。
Q&A
提问:想问一下报警阈值设定,上面提到的方法是根据趋势来设计报警,这个趋势其实您能做到多少准确?能控制在什么情况下?
兰建刚:准确度的确是个问题,我们用了一个算法叫 3-sigma,准确度还不是特别确定,因为这个东西真的是服务治理里面最大的难题,报警分级怎么分?这是很大的学问,我们现在整个报警系统里面报警通道每天上千个报警,很多都不看的,因为觉得这个报警没什么意义。这是一个实际当中要去调整的问题。
提问:报警存在误报的情况?
兰建刚:对,你要知道我们的业务有两个明显的尖峰,十二点和下午四五点的时候都是订餐的高峰,之后则所有的指标都会有下降趋势的时候,如果你曲线偏离的很厉害就会引发报警。
多指标聚合是我们正在做的,发生一个指标报警的时候可能是一个小问题,但是这个问题会触发一个 CEP 的流程,比如“是不是 CPU 飙高的同时响应时间会抖动?”,我们可以定义这样整个一套规则,去做报警来提高准确度。
提问:刚才您提到用 Go 写的一个东西,为什么选择使用 Go 因为 Go 是自己的垃圾回收机制?而且我想了解一下您认为 Go 它是比较适合什么样的系统?
兰建刚:当然是底层的基础服务了,我们不建议用 Go 写业务。为什么我们选 Go 做工具,是因为我前面提到,公司原来一些工具是搭在 Python 上,有一帮 Python 工程师,让他去写 Java 他是绝对不干的,但是让他去写 Go 语言是没有问题的,Python 其实不适合写底层框架,因为它是个动态语言,工程化方面也会差一点。
提问:刚刚看到您提到超时配置推荐,这块是和你们的应用场景有关系,还是说和你们遇到的故障?
兰建刚:就是因为遇到故障了,因为很多超时配得很乱,有的同学直接配 3 秒超时(这是配置模版里的一个例子,很多同学就拿去直接用了),那还不如不配,有些情况很多服务就是 10ms 就正常返回了。只要保证这件事情对绝大部分的服务来说,是有利可图的,那我们就去做这件事情。
相关阅读
(点击标题可直接阅读)
预告:腾讯、微博、魅族、平安等技术专家在深圳分享架构从1到100演进历程
Mercury:唯品会全链路应用监控系统解决方案详解
同程旅游缓存系统设计:如何打造Redis时代的完美体系
蘑菇街电商交易平台服务架构及改造优化历程
本文及本次沙龙相关 PPT 链接如下,也可点击阅读原文直接下载
http://pan.baidu.com/s/1nvnOEBf
想更多了解高可用架构沙龙内容,请关注「ArchNotes」微信公众号以阅读后续文章。关注公众号并回复 城市圈 可以更及时了解后续活动信息。转载请注明来自高可用架构及包含以下二维码。
高可用架构
改变互联网的构建方式
长按二维码 关注「高可用架构」公众号
沙龙活动预告
8 月 20 日,高可用架构走进深圳,举办『互联网架构从 1 到 100』的技术沙龙活动,届时,腾讯、新浪微博、魅族、平安科技的技术专家将会同台分享架构演进经验。点击图片了解活动详情,点击阅读原文直接进入报名页面。
最后
以上就是开朗秀发为你收集整理的饿了么分布式服务治理及优化经验(含PPT)的全部内容,希望文章能够帮你解决饿了么分布式服务治理及优化经验(含PPT)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复