目前越来越多的复杂系统开始采用微服务的架构,对于开发团队来说微服务的优点有很多,易于维护,开发效率高,多语言,松耦合等;但是对于运维团队来说增加了很多挑战,例如
•分布式系统的运维复杂性•调用链路过长难以监控•报错链路追踪困难•底层模块出问题容易造成雪崩等。
本文要讲的链路追踪系统就是为了解决微服务架构下的性能分析与故障溯源问题。
现在的分布式链路追踪系统基本上都是基于google的Dapper论文演化而来,具体的可以参考附录1的链接,论文简单明了,清晰易懂。这里仅简单做一下介绍:
链路追踪系统的核心是要将系统间的调用链路给构建出来,类似构建一个双向链表。我们可以通过给每次http调用系统调用(或者RPC)分配一个唯一ID(命名为span id),在调用时将本身ID传递给下游ID的方式来建立调用链路,比如在A模块调用B模块时,A模块可以将自身的span id
在调用同时传递给B模块,对于B模块而言A模块的span id
就是它的parent id
。这个过程可以用下图来描绘:
图1 (来自google Dapper论文)
我们上该系统时比较早大概在2017年左右, 在实施过程中主要调研了zipkin、cat这两个开源方案,这两个方案对java支持是比较友好的,但是对我们主力系统的 php-fpm 支持比较差,要实施的话对系统的改造比较大。如果是 java 系统的话可以考虑用这两个开源方案(最近比较火的skywalking也建议考虑, 我们线上部分系统也在使用skywalking)。
综合考虑实施成本、安全性以及自研的难易程度等问题之后,我们决定尝试自己捣鼓一套,原理比较简单,所以自研一套的成本也是比较低的。
整个方案大概分为三个部分:
这一层是整个方案的基础,如缺少字段会影响之后的分析使用。经过探讨,我们在这一层如果记录了如下5个追踪基础字段以及性能分析的关键字段:
5个追踪基础字段:
•span id:本次请求的id•span hostname:本次请求机器名称(或者机器ip也可以),用来标识本次请求发生在哪个机器上,方便查询日志•parent id:父请求id•parent hostname:父请求机器名称,属于冗余字段,通过该字便于统计出调用本模块的来源•trace id:追踪ID,用来标识一次请求的ID,通常在入口处分配trace id,在各个请求调用之间传递该id,对于同一个外部请求发起的各个span而言该trace id都是相同的,通过该id可以将span id串连起来。
性能分析关键字段:
•uri:请求uri•process time:请求处理时间,代表uri的处理效率
该层我们采用了filebeat -> kafka -> logstash -> elasticsearch 的方案
整个方案大体展示如下:
该方案有许多优点,例如:
1.方案无性能瓶颈,各个模都可进行扩展;2.对于业务性能基本不受影响,不会增加额外网络请求;3.业务安全有保障,系统挂掉不会影响到业务系统;
方案设计只是个起点,之后的实施才是重点。如果实施过于复杂或者实施周期过长很有可能会夭折;所以在实施过程中我们做了或多简化实施的工作。
该部分工作主要由研发团队实施。因为使用的微服务模块框架比较统一,所以我们对模块间调用SDK以及日志模块SDK进行了改造,研发同学只要接入这两个SDK就可以了,非常简单;
这两个SDK仅对日志记录格式做了调整, 完全不会影响性能;
我们关注的日志有两块:一块是访问日志,一块是业务日志。
•访问日志:请求日志记录至少需要记录下上面介绍的 7个字段,该访问日志可以在 tomcat
、nginx
层面记录,也可以在业务框架中进行记录;•业务日志:业务日志至少需要标识出span id
字段,通常业务日志都会比较大,在这里我们只收集了错误日志;
日志收集有比较成熟的架构,可以参考
filebeat → kafka → logstash → elasticsearch
在实施过程中elasticsearch
是一个需要重点优化与监控的模块,需要保证日志收集的实时性。尤其当日志量比较大的情况下,很容易出现瓶颈,造成日志延迟,进而对查询服务或者报警服务造成影响。
该层通过对elasticsearch
中的数据进行检索汇总,形成实用的功能。只有这一层做好了才能展现出整个系统的价值。通过该层我们可以做链路调用追踪,故障溯源,性能分析等,简要介绍可参见下面的 "如何使用" 章节.
这一章节我们介绍下在之前架构下做的展现层的一些功能。(UI未做优化, 只看功能即可)
这是比较的基础功能,用来对调用链路进行追踪。给定一个trace id
就可以将完整的调用过程绘制出来。该功能通常在慢请求分析,调用链路分析等方面。
该部分主要解决的问题是:微服务模块较多,调用复杂,出现问题找不到根源模块。我们把一个模块绘制成一个圆圈,模块间的调用绘制成一个单箭头的直线(正常请求颜色为绿色,报错超过一定比例之后颜色为红色),通过这么绘制之后就可以画出一个调用网络,在出问题的时候很容易看出来报错根源所在的模块。如下图所示:
通过对模块的url 响应时间,请求次数,状态码等进行统计汇总,可以绘制模块的url性能图表。
在模块上线前后通过观察该图表可以监控到上线有无异常;
在出现故障时也可以比较方便的统计出故障前后的统计。
目前该系统已在业务系统中全面使用起来,在调用链路追踪,故障溯源,性能监控等方面起到了重要作用,极大提高了故障恢复时间。但同时也在使用过程中发现许多可优化,可以扩展的地方。
在这些基础功能上面其实有许多功能可以进行扩展,例如:
现在的系统只是对模块间的请求进行了监控分析,其实我们可以更进一步对请求内的资源(例如redis
,db
)调用进行监控。一般框架中都会有打印记录资源请求日志的功能,稍加改造就可以很方便的集成到系统中;
对于调用时长的波动;其实可以结合模块上线,报错日志等做一些智能监控,自动定位等方面的功能。
英文版
Dapper, a Large-Scale Distributed Systems Tracing Infrastructure https://research.google/pubs/pub36356/
中文版
Dapper,大规模分布式系统的跟踪系统 http://bigbully.github.io/Dapper-translation/