APMCon 2017|听云研发副总裁廖雄杰:微服务架构的应用性能监控
中国应用性能管理行业盛宴—2017中国应用性能管理大会(简称APMCon 2017)于8月10日至11日在北京新云南皇冠假日酒店隆重召开。本届APMCon是由听云、极客邦和InfoQ联合主办,作为国内APM领域最具影响力的技术大会,本次大会以“驱动应用架构优化与创新”为主题,致力于推动APM在国内的成长与发展。
听云研发副总裁-廖雄杰于智能运维专场发表了题为《微服务架构的应用性能监控》演讲,现场解读了当单体应用演变为微服务架构后,上层应用与微服务之间以及不同的微服务之间的调用关系的性能问题。
以下为演讲实录:
廖雄杰:刚才赵宇辰老师从算法的角度给大家分析了如何选择合适的算法做异常的检测以及预判上的一些算法的点。而且算法的选择,还是需要大量的metrics做支撑的,分析的话metrics是必须要全的,所有的系统都完备才有可能做出这样的分析。刚才赵宇辰老师的演讲可能稍微有些深奥,我的演讲会将一些浅显易懂的,就是如何采集微服务架构下面的性能指标方面的数据。
今天的演讲大概分成四大块,也不多,一个是我们为什么要选择微服务,第二是讲一下微服务架构的应用性能监控的方法、方法的概念这样的东西。之后从实践角度给大家分享一下听云微服务化的过程以及监控方面的实践,听云在微服务方面也是一个起步阶段,这一块更多还是希望通过一些分享跟大家能够有更多的深入的交流。最后是会有一个稍微再深入一点的,在微服务架构下边我们的复杂调用链的时候怎么样进行一些性能的监控和追踪。
一、Why Micro Services
首先是why micro services,这个大家不管在网上论坛还是行业交流里已经讨论的很多了,我列举几点自己在这方面不太成熟的想法。
1、单体架构适用于中小型产品前期快速迭代验证。在座的很多也有研发同事,大家都很有经验,单体架构在中小型产品的前期还是适用的。随着产品的发展,之后就进入到我们经常讲怎样转型微服务的阶段。转微服务并不是说从一开始就必须要做微服务的架构,我们经常讨论这个设计的时候也会提到一个过渡设计的概念,这就是说从一开始就搭一个非常完善的微服务平台的话,其实是有些过度设计的嫌疑的。中小型产品的前期是一定要快速迭代并进行验证的,那么单体的架构会更适合一点。如果一开始就一股脑的要把所有的东西都实现,可能不太适合在微服务上面做很多的事情,转型微服务,其实是一个自然而然的事情。
2、服务及数据体量的爆炸性增长,从单体到微服务其实也是随着互联网的服务、数据体量的爆炸性增长的一个自然而然的过程,很多比如作为电商甚至一个页面里面都会分为搜索、详情、客户端、用户的模块,可能一个页面里面都有很多性能瓶颈的点在这里,这时候自然而然就会想到要不要把单独一个搜索框分出去、用户的模块或者单页的详情要不要分出去,这都是随着你的服务和数据在达到一定体量的时候自然而然产生的一些想法。
3、分布式环境下的单体架构,单体架构的部署、运维或者研发在现在分布式环境下都已经越来越不适应。因为分布式环境下面需要考虑更多的复杂性的事情,比如你的数据量会非常大,分布式环境下要不要考虑事务的一致性等等很多的问题,而原先的单体架构在这方面越来越显得力不从心。
4、中央集权VS领域自治,这点是很多的开发同学在开发设计的过程中都会有切身体会的,单体架构更多是在设计时倾向于中央集权的概念,更多的从一个系统的角度去设计所有的问题;当我们系统大量报错的时候我们也会考虑到各种模块的结耦,微服务化也是这样一个模块式,我们之前设计的时候经常会讨论的一个范式是一个领域模型。这些东西设计上都是相通的,只不过微服务化我们是希望能够把它做的更彻底一点,不只是在设计开发的时候,也希望能对部署、上线等等所有过程都进行一个解耦。
微服务架构的优势
微服务架构的优势我们也讨论的比较多了,这里大概列几点不做详细的讨论,因为后面更多的还是想把重点放在监控那里。
1、 低耦内聚
2、 更轻量、快速迭代、快速集成、实时集成,部署能够做到更简单
3、 更可靠,每个服务都可以单独的运维、维护升级做迭代
4、 微服务更容易监控,发现/定位问题更快捷
微服务的正确打开姿势
1、 按业务垂直拆分,比如刚才说的那样,一个页面上可能有好几十块,可能会按照业务的特点进行一个垂直拆分。
2、 按模块水平拆分,在系统里面都会有一些核心的组件、公用的服务,但是对系统性能的影响很大,所以像这种被多个业务所依赖的公共服务,是比较适合按照水平拆分单独拎出来一个功能做。
3、 垂直+水平拆分,但实际上我们真正实践的时候这两个方面都会考虑,垂直和水平都会考虑做混合的拆分。
二、微服务架构下的应用性能监控
第二部分是微服务架构下面的应用性能监控,在说到微服务架构优势的时候,我们说它的监控其实是越做越简单,但是当你的服务拆分的越来越细的时候,你第一反应并不是它越来越简单,第一反应一定是它更复杂了。因为原来你的系统在一个单体架构下面的时候,把系统分布式环境下面拆成多个节点也只是并列的几个节点,只要监控这几个节点就可以了。但是拆分微服务之后,每个微服务都会拆分出来10个、20个上百个点,第一反应一定是更复杂了,这种情况下如何快速发现定位问题?有成百上千个微服务的节点,应用端调用出问题的时候怎么知道是哪个节点出的问题?
这时首先需要比较完善的metrics指标采集体系,把数据尽快的监控出来并进一步定位和发现问题。服务器数量激增之后,首先部署和管理上有些问题,另外一个是调用链可能比原来变的更复杂了,原来是在一个单体里边模块跟模块之间的调用而已,现在有可能是A服务调用了B服务,B服务可能又调用了C服务,最上层的应用出问题的时候到底哪个服务出了问题,这对我们监控也是提出了一个比较大的挑战。
微服务架构下的应用监控
这个PPT里面更多强调了我们关心的还是应用层方面的监控,系统层面的比如CPU、内存这样一些网络、硬盘、磁盘这样一些监控的话,我们不在这里面讨论了,那是最基础采集的一些数据,很多人都会有一个比较完善的监控体系,很多开源工具都支持这些指标的采集。
从应用的角度会把监控大致分成两种,一种是微服务化之后根据业务特点是以性能监控优先还是以事务为优先。举个例子,从运维的角度微服务化之后,比如部署了很多A服务B服务C服务,但是并不关心每一个事务它是不是成功到达A服务B服务C服务,它的性能是不是好的,当然说成功失败这个词可能会比较敏感,但是说性能好还是差很多时候只需要一个统计数据就可以了,或者说它特别慢的时候我们能够有足够的信息让它辅助我们定位问题,我们其实并不需要每一个调用的数据,这是一种场景。
另外一种场景恰恰相反,是需要每一条调用的记录都不能丢,在日志里面或者某个地方存起来。比如交易信息,每几个用户打电话投诉为什么转了笔账中间某个环节失败了或者有些卡顿,这时候有可能查每一条从前到后在哪个环节失败的,这是两种场景,这两种场景在监控角度还是有比较大的区别的。
先说第一个场景,如果以性能监控优先只是为了解决微服务化之后性能不可控的问题的话,那么这两种区别在于什么呢,如果以性能优先,刚才说了不需要每一条调用链的日志,只需要一些统计信息就OK了;如果以事务为优先则需要把每一条调用链都存下来,这个数据量还是比较大的。
第一种方式我们一般会有自动的探针去实现,这个嵌码一般会比较简单,比如说框架用的dubbo或者简单的API调用,这些通用的组件都是可以通过一些自动化方式嵌进去,采集数据量并不是很大,很容易SaaS化,也可以用http/dubbo/thrift等支持,甚至可以将数据链调用,这个看案例的时候会看到。
另外一种就比较麻烦,以调用链追踪为优先,就需要把每条调用链的日志采集下来,目前还没有一个SaaS厂商支持,即便支持通常也是以一个私有化部署的方式去做,因为这个数据量太大了,传到SaaS上的话,用户的带宽也会是一个问题,另外服务器处理需要大量的资源,它不太适合以SaaS的方式应用。
另外我们多多少少听到过大众点评有一个cat监控平台,国外有zipkin,这两个都是开源的,有很多人也都用过。但是当数据体量比较大后也很难做到全量,全量所消耗的资源不一定是监控的需求所能承受的,这时候就需要做一些全量采样到追踪。一般这个模式配合应用层,会提前在某些地方埋点并开发一些插件。这是两种,一个是性能优先,另外一个以调用链追踪优先或者事务优先这两种方式。
三、听云微服务化历程及监控
这里会讲一下听云在微服务化的历程及监控的实践。听云其实在微服务化这里做的时间不长,因为我们的业务对于微服务化也没有特别多的需求,因为我们后端的架构基本上都是以业务线为主,业务线里的逻辑不是特别复杂。这里我们先分享一下微服务化之前的架构,但是会在微服务化的时候把几个关键组件抽象出来,会针对这一块重点说一下。
听云在后端这里相对简单一点,重点是在数据采集那一块,那一块是分到很多层的,包括用kafka分很多层处理的一些海量的数据。这方面下午会有另外一个同事的分享会提到,有一个后端的流式数据采集的分享,我这里面只是画一个简单的架构图,大概说下这一块。
数据采集层,探针会把数据通过我们的负载均衡传到数据采集上,每个业务模块会分APP、Server、Browser三条产品线,每个产品线都有自己的数据,数据库原本单体化是比较严重的。
存储层那里有帐号和每个产品的配置库,都是在一个Metric数据库里面,除此之外每个都有单体的数据库,这个数据是按照时间序列之后统计的数据,之后会做分片的集群,后面还有一些ES以及非结构化的存储。
纯用户交互层,这一块的访问量不高,毕竟我们也不是2C的业务,所以上面那一块访问量不是特别大,也不是我们重点关注的部分。
1.0单体架构所面临的问题
1、组件依赖多,迭代效率低下。我们每一个数据采集模块所依赖的东西是非常多的,比如每一条APP探针上传的数据都需要解析它的IP或者它的经纬度,需要分解出来很多维度的信息,比如是哪个城市、哪个运营商的,我们会通过IP或者它的经纬度解析,这本身是比较耗资源的,尤其在服务器端解析经纬度,由于我们自己本身没有调百度的API、谷歌的API或者第三方API,顶多能做的是在经纬度这里加一级缓存,因为每一条都调第三方API的话后端肯定是受不了的。
2、核心组件升级周期长。如果后端涉及到一些升级的话,不说全量的回归,至少系统涉及到的相关的模块它的功能都需要回归一遍,测试需要看一下这个功能是否正常。并且在测试上线之前一定会做回归测试,回归测试大家知道周期一般情况下都是比较长的。尤其是对于核心组件的升级,因为它不仅仅只影响到这一个系统,比如像非结构化存储就是我们自己开发的NBFS的组件,这个组件不只是APP在用,几乎所有产品线都在用,报表那一层也需要调用独立的接口,原来没有微服务之前的话,只要接口一动所有系统都要动一遍,回归测试的周期很长,这是单体架构典型的痛点之一。
3、单一配置库,DB问题影响多个系统,排查困难。我觉得微服务化不仅仅是在我们的程序的架构,微服务化它是一个思想,你的数据库的拆分也是要纳入到微服务化的。昨天听了另外一个讲师关于微服务化的分享,他提到一个很好的观点,微服务化首先是需要捋清楚系统本身架构里面的一些业务的逻辑,这里面也包括数据库的逻辑,微服务化对于数据库同样是适用的。原来的时候我们配置库全都用一个,包括警报的、报警的,每个业务线对于配置库访问的频次是不一样的。有的系统APP探针量特别大,这可能对配置库的访问频率非常高,很容易就会因为某一个业务线的效率低下从而拖累了其他的业务线,这是对于资源隔离是不太好做的一方面。
单体-微服务架构
我们从单体转变到微服务主要做水平的拆分
1、核心组件微服务化。把核心组件拆分出来,对于非结构化的对象存储,我们是自己写了nbfs的服务;基于IP、基于地理位置做location的解析,这个我们所有的系统都会用;Metric Service,每一个metric入到最终的时间序列的数据之前是需要对它进行ID化的,并且metric量是非常非常庞大的。因为在我们系统里任何一个东西基本上都是一个metric,比如说APP里面的URL也算是一个metric,每一种类型的metric它的数据量很有可能是非常非常大的,作为我们非常关键的服务,需要把它单独拎出来。
2、配置库按业务线垂直拆分。这样做可以避免刚才说的因为APP的问题把库拖垮,结果Server其他的业务线也无法用了。由于不可控性而且排除起来非常麻烦,出了问题后很难知道是哪个系统引起的,只能从其他渠道配合排查。
3、核心微服务按业务线资源隔离。我们的核心服务有全局的服务,基本上上面三个服务都会跨业务线使用,使用的时候跟配置库是一个概念,会从垂直方面考虑做资源的隔离,最好是业务线别把整个微服务集群拖垮。
4、日志统一如EFK
听云微服务化2.0
这是我们微服务化之后的,基本上刚才大概说到了,把几个关键的服务给它抽了出来,数据库也做了垂直的拆分,APP、Server、Browser都用自己的数据库。这样做完之后加上我们之前做的努力,今年年初比原来纯单体架构的时候可用性提高了非常多,原来只能做到99.9%,现在基本上前面两个进度已经轻松做到99.99%了。但是这个可以说是经过很多方面的努力,不只是微服务方面,只是微服务让我们对系统更有信心,至少把几个关键的,最有可能影响我们业务系统的几个组件抽出去之后对运维更加透明。
微服务后的效果
1、核心组件独立为原组服务,升级对应用几乎零影响。每个核心的组件都独立成为一个原组的服务,升级的时候流程完全没有原来那么繁琐,回归测试基本上不会通知业务方测试,只是有非常重大的微服务升级的时候可能会通知它“我们要升级了,要把你的业务大概验证一下”。
2、监控由面向监控调整为面向服务,粒度更细。监控层面面向服务之后,每个核心服务都可以做到很细粒度的监控。
3、可靠性高,核心组件对应用性能的影响更加透明。
4、配置库按业务线拆分不同业务线数据库资源隔离。不同数据库的资源做到了完全的隔离,DBA现在比原来感觉心里有谱多了,原来一出问题不知道从哪儿入手。
听云NBFS服务简介
后面简单分享一下我们的监控方面的实践,把刚才比较核心的服务做了一个案例,简单介绍一下这个服务。
我们这个服务主要是为了存储一些非结构化的数据,它的整个功能比较类似于淘宝的TFS或者S3,要把很多非结构化的数据存储起来。有些数据可能对它也不做任何的查询,比如当应用响应比较慢的时候会存一条它的详细记录,这个详细记录包括它慢的时候有很多现成的堆栈的信息,包括上下文的信息,这些信息存数据库是不太划算的,顶多是根据它慢的时候将某个ID详细的数据拉出来仅此而已,所以我们给它单独开发了一个数据存储,有点类似于TFS。
为什么没有直接用TFS?一方面TFS对于我们的需求来说有一些重,另外TFS开源出来之后它的门槛不像自己内部使用那么细,有很多问题开发人员或者运维同学不太能hold住。最主要的问题是我们使用场景还是比较特殊的,首先像TFS,它也是用于解决海量的小文件对象的存储,这个场景跟我们是一样的,我们80%的海量小文件存储数据都在4K以下,大部分在1K以下这样的情况。另外一个场景是写多读少,写入延时要求非常高。这时我们跟一般使用场景不太一样的地方就是写入,但是什么时候用它就不一定了,用户需要的时候察看一下它,读是非常少的,结果就是要把它快速写入后端存储里面去。
这个架构比较简单,每个节点都会挂一个本地存储,但是这个本地存储只是临时存储,每隔半小时就会把数据同步到云端,在本地存储的时候会考虑做很多性能方面的优化,这个读写接口都会以dubbo的方式暴露给应用层。
指标关注
另外我们关注监控的时候会关注一些指标,比如API的响应时长、吞吐率等等,当它慢的时候耗时在哪些组织上面这是我们监控的重点。中间有两个网络层耗时和API的调用排队时间,过去在dubbo的时候会遇到这个问题,当访问量非常大的时候有一段时间会耗在排队上面,因为服务器线程是有限的。我们希望把所有消耗性能的地方都暴露出来,另并且性能监控的时候我们希望看到慢请求的堆栈或者调用链,当它慢的时候可以快速排查这个问题。
自动发现应用拓扑
对于监控的话我们首先希望看到是一个自动发现的机制,因为微服务本身是有自动发现机制的,我们的监控肯定也需要发现微服务以及发现应用和微服务之间的调用关系,这是比较直观的第一步。
这是一张可以自动发现应用的拓扑,可以发现响应时间、吞吐率、错误率这样一些概览性的指标,在应用层可以通过web服务里面,分为HTTP、thritt和dubbo这些通用的是可以看到的,可以看到吞吐率和响应时间的趋势。点进去之后可以看到它的微服务后端的监控,可以看到概览,每个解剖的时间是什么样的、吞吐率是什么样的、每个服务可以看到调用阶段,可以细到每一个函数。但是事实上我们不会采集每一个函数,只会采集像数据库MySQL这样一些可能产生瓶颈的地方,也可自定义嵌码的规则把核心的函数采集出来,比如这里面仍然会经过一个核心的方法,有一个压缩的过程,每一个阶段都分解出来,让你非常直观的看到它到底在哪个阶段比较耗时。
案例分享
有一个案例的分享,我们在上线不久后发现了一个问题,在业务高峰时NBFS服务会偶发性响应耗时突增,持续几秒到几分钟,运维心里很忐忑,不知道什么问题,我们去图上一看,它会不定期有个尖刺在那里,这个尖刺平均耗时平的那条线大部分是在1毫秒以下,单次写入基本上在0.1~0.2毫秒。但是当它出现尖刺的时候响应时间提高到5毫秒多,最大的一次调用达到8秒多,这样对于应用的话短期几秒钟是没有问题的,但是如果持续几分钟的话就会造成我们采集端堆列积压的现象。
再往下钻取的时候看到它的调用组件里面有一个文件open的操作,这是由于每隔10分钟需要自动切割一个新文件,这个过程是阻塞的,一旦创建文件并打开文件句柄,后续会复用文件句柄,纯顺序写入操作是很快的,但是创建并打开文件可能很慢。回到刚才说的这个页面会看到其中随便点一条日志之后都有一个调用的追踪,进去之后可以看到它追踪的情况,在这里面看到这个时间基本上都是耗在了文件的open上面,百分之百原因是它,刚才那个是103毫秒持续时间基本上都是它造成的,但是它的调用次数并不多,这个时间点上面有两次。之后再进到追踪详情里面可以看到更详细的调用站的情况是由哪个函数调用的。
复杂调用链性能监控及追踪
对于微服务的情况我们很有可能碰到这样一个情况,刚才分享了我们的案例,架构相对来说是比较简单的,实际情况很有可能是大家使用过程中你的微服务的应用拓扑比这个复杂得多。这可能出现A到B到C很复杂的调用情况,应用端出问题时到底是哪个服务出现问题。
这里面我们是看到了一个50秒的响应时间的调用,点进去之后,会看到它是调用了后端的服务,这是方法级别。进到里面看到99%的问题都是由它调用导致的,我们肯定很疑惑到底是后端的哪个调用,这里面也会采集到相关的SQL以及把整个调用的拓扑绘制出来,这些都不是关键的东西,这里面最终说的是一次远程的调用,我们肯定希望看到它后端调用了某个服务,到底是服务本身出问题还是后端的一个服务出的问题。
我们进入到追踪的详情之后就会看到是SQL的调用情况出了问题,可以看到到底是哪一行发生的,可以看到是后端的auto-audit的服务,再往后还是调用追踪的调用链,我们会看到服务里边,还是同样的方式,打开它一看它又调用了另外一个API,发生远程调用的代码行也能看到,是AutoScreeningServiceImpl.java,第131行。可能你的应用层调用A服务,你从应用层看到是A服务有问题,其实它的背后是A服务调用了B服务,在调用过程中后端某个服务出现了问题。
这是我们对于复杂微服务的架构我们怎么样快速的监控和定位问题这样一个案例的分享。谢谢大家!
QA环节
问题:现在Web的查询是实时的读取吗?调用链信息采集方式是怎样的?
廖雄杰:我们查询基本上算是实时的,后台存储数据的时候处于优化考虑会分为2部分,一种是按Metric及维度统计的数据,这部分会聚合为分钟级别再存储;另一部分慢调用或错误的tracing数据,则存储原始数据,不做聚合。调用链信息的采集通常是在HTTP头(HTTP协议)或者上下文对象(例如,dubbo或thrift)里附加一个TraceId,用于调用链之间的关联。
问题:我现在遇到了一些问题,因为我现在也是做链路上的,因为数据量特别大,后期我会做针对应用的单独性能的报告,比如昨天凌晨到今天凌晨这些,在原有基础之上拆分,是以应用划分的呢还是纯链路做?
廖雄杰:我们这边是分开的,比如说想看每个时间节点慢的次数,平均时间是多少类似于这样的,我们是分开存储的,你刚才前面说的那个问题,我们会将它作为一个持续的数据做汇总。
问题:你们现在客户端采集上传的数据是不是实时上传到服务器端的?
廖雄杰:基本上是这样,客户端上传的时候比如APP和Server,刚才说数据都是来自于Server,Server的数据上传方式比较简单,每分钟上传一次,但是上传之前会在探针那段做一个聚合,要不然就会出现上传数据量非常大的问题。APP就不太一样了,因为APP访问时间太分散了,节点比较分散,所以APP上无法做这样的聚合,但是也会每分钟打包成一个报文上传,并不会每次采集就立即上传。
问题:如果是实时上传的话,如果你的数据通道因为服务端异常或者中间网络异常的时候导致上传不能成功,服务端持续情况很长的话是不是对客户端原始应用产生影响?
廖雄杰:会丢数据但是不会因为这个产生影响,因为刚才说我们以性能优先,但是并不代表着性能数据能够优先你的应用端,要尽量避免对应用造成严重的影响,比如你的网络问题持续了很长一段时间,我们会把这个数据尽量的保留,但是你的持续时间很长的话我们会把较早采集的一些性能数据排除掉。
问题:你这个微服务架构是听云内部的架构,听云这个产品是私有化部署,私有化部署之后也是按照微服务的部署架构吗?
廖雄杰:我们现在是在SaaS环节下部署,私有化环节至少短期内没有这么考虑,因为私有化会涉及到部署、运维这样一些问题,所以还是不太适合把架构搞很的复杂,服务器部署的特别的散,因为私有化的环节确实比较特殊一点。
问题:你刚才提到的全链路调用链监控,在客户环境下面可能客户用到的这些技术组件框架跟你的是不太一样的,是怎么把客户这些系统的微服务业务串起来的;如果自己实现肯定是基于公司内部的技术组件进行链路的串联,你们这块是怎么支持的还是每个公司都要定制去做还是什么?
廖雄杰:这个不会,做微服务所用的框架部分还是开源的或者业界比较常用的框架,在它的基础上做一些扩展或者运维这方面的扩展性的东西,大部分纯粹自研微服务框架的好像还不太多。
问题:因为有些公司RPC框架是自己做的,我们公司就是这么做的,我们公司调用链也是基于自己的这条协议去实现的。
廖雄杰:我们会支持一些业界常用的成熟的框架在底层,比如是用http协议的,基本上大部分的http是原生就支持的,如果纯粹基于其他的框架通讯协议基础上自己封装一个RPC框架的话,这个我们目前原生是没有支持的,可能需要做一些定制或者自定义的一些工作,我们探针端是支持扩展开发的。
问题:你们现在的全链路有没有打通端到端,移动端、PC端跟服务端链路的产生?
廖雄杰:早就打通了,在听云产品内部都是打通的。
问题:你们的数据上传的是明细数据,明细数据上传之后提供给用户查询这些报表的时候是经过二次的聚合计算还是直接经明细的数据实时的计算?
廖雄杰:是分两层,第一层一定是聚合的,这个性能根据查询的场景会按照查询的维度先给它做一层聚合,第二层点进去的时候才会出详情,详情会单独存在,比如ES这样的存储里面。
问题:计算是用大数据平台还是用云计算或者其他引擎做的?
廖雄杰:用流式的处理方法,但是框架并没有用spark streaming和storm,刚才说的数据采集是我们自己开发的框架,但是主要还是做一些流式处理,跟kafka消息分发做了一些集成的事情。