前段时间,我们有发布过一篇题为《类似 Google Dapper,微服务需要这样的分布式跟踪工具》的文章,很多读者反馈没看尽兴,确实,文章只是谈到分布式追踪工具的意义,以及可以解决什么问题,但并没有谈到如何实现分布式追踪。今天这篇文章,作者是东软集团基础软件事业部技术总监,他在这方面有丰富的经验,文中他将会聊到目前主流的几个解决方案实现思路以及他们的落地方案。
另外,这里有个不错的关于 Spring Boot 和 Spring Cloud 的沙龙活动,免费,在北京,感兴趣的同学别忘了报名。
随着互联网技术的高速发展,各种创新技术、前沿概念如雨后春笋般层出不穷,云服务、云计算、大数据处理、大数据分析……,以往单应用的服务架构已经很难处理如山洪般增长的信息数据,随着分布式的普及、服务的快速增长与云计算技术的进步,微服务架构逐渐进入人们的实现,它也因其特有的优势而备受关注。
微服务架构的本质,是把整体的业务拆分成很多有特定明确功能的服务,通过很多分散的小服务之间的配合,去解决更大,更复杂的问题。对被拆分后的服务进行分类和管理,彼此之间使用统一的接口来进行交互。
微服务的特点决定了功能模块的部署是分布式的,以往在单应用环境下,所有的业务都在同一个服务器上,如果服务器出现错误和异常,我们只要盯住一个点,就可以快速定位和处理问题,但是在微服务的架构下,大部分功能模块都是单独部署运行的,彼此通过总线交互,都是无状态的服务,这种架构下,前后台的业务流会经过很多个微服务的处理和传递,我们难免会遇到这样的问题:
- 分散在各个服务器上的日志怎么处理?
- 如果业务流出现了错误和异常,如何定位是哪个点出的问题?
- 如何快速定位问题?
- 如何跟踪业务流的处理顺序和结果?
我们发现,以前在单应用下的日志监控很简单,在微服务架构下却成为了一个大问题,如果无法跟踪业务流,无法定位问题,我们将耗费大量的时间来查找和定位问题,在复杂的微服务交互关系中,我们就会非常被动。
对于这个问题,业内已经有了一些实践和解决方案,让我们来看看行业内的领先设计思想。
Google Dapper
Google 公司广泛使用了分布式集群,为了应对自身大规模的复杂集群环境,Google 公司研发了 Dapper 分布式跟踪系统,并发表了论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,给行业内分布式跟踪的实现提供了非常有价值的参考,该论文也成为了当前分布式跟踪系统的理论基础。
我们先来看个例子:
图 1 这个路径由用户的 X 请求发起,穿过一个简单的服务系统。用字母标识的节点代表分布式系统中的不同处理过程。
分布式服务的跟踪系统需要记录在一次特定的请求中系统中完成的所有工作的信息。举个例子,上图展现的是一个与 5 台服务器相关的一个服务,包括:前端(A),两个中间层(B 和 C),以及两个后端(D 和 E)。当一个用户(这个用例的发起人)发起一个请求时,首先到达前端,然后发送两个 RPC 调用到服务器 B 和 C。B 会马上做出反应,但是 C 需要和后端的 D 和 E 交互之后再返还给 A,由 A 来响应最初的请求。对于这样一个请求,简单实用的分布式跟踪的实现,就是为服务器上每一次发送和接收动作来收集跟踪标识符(message identifiers)和时间戳(timestamped events)。
基于这个模型,Google 在此论文中提出了几个重要的概念:
1、基于标注(annotation-based),又叫植入点或埋点
在应用程序或中间件中明确定义一个全局的标注(annotation),它可以是一个特殊的 ID,通过这个 ID 连接每一条记录和发起者的请求,当然,这需要代码植入,在生产环境中,因为所有的应用程序都使用相同的线程模型,控制流和 RPC 系统,可以把代码植入限制在一个很小的通用组件库中,从而达到监测系统应用对开发人员的透明。Dapper 能够以对应用开发者近乎零侵入的成本对分布式控制路径进行跟踪,几乎完全依赖于少量通用组件库的改造。
- 当一个线程在处理跟踪控制路径的过程中,Dapper 把这次跟踪的上下文在 ThreadLocal 中进行存储。追踪上下文是一个小而且容易复制的容器,其中承载了 Scan 的属性比如跟踪 ID 和 span ID。
- 当计算过程是延迟调用的或是异步的,大多数 Google 开发者通过线程池或其他执行器,使用一个通用的控制流库来回调。Dapper 确保所有这样的回调可以存储这次跟踪的上下文,而当回调函数被触发时,这次跟踪的上下文会与适当的线程关联。在这种方式下,Dapper 可以使用 trace ID 和 span ID 来辅助构建异步调用的路径。
- Google 几乎所有的进程间通信都是建立在一个用 C++ 和 Java 开发的 RPC 框架上。我们通过跟踪植入该框架来定义 RPC 中所有的 span。span 的 ID 和跟踪的 ID 会从客户端发送到服务端。基于 RPC 的系统被广泛使用在 Google 中,这是一个重要的植入点。
2、跟踪树和 span
图 2:5 个 span 在 Dapper 跟踪树中的关联关系
在 Dapper 跟踪树结构中,树节点是整个架构的基本单元,而每一个节点又是对 span 的引用。节点之间的连线表示的 span 和它的父 span 的直接关系。通过简单的 parentId 和 spanId 就可以有序地把所有的关系串联起来,达到记录业务流的作用。
Twitter 公司的 Zipkin
Twitter 公司的 Zipkin 是 Google Dapper 系统的开源实现,Zipkin 严格按照 Dapper 论文实现,采用 Scala 编写,并且紧密集成到 Twitter 公司自己的分布式服务 Finagle 中,使得跟踪做到对应用透明。
图 3:Zipkin 应用架构图
Zipkin 的整体架构如上图所示,涵盖了信息的收集、处理和展现。
淘宝鹰眼系统(EagleEye)
淘宝鹰眼是基于网络调用日志的分布式跟踪系统,它可以分析网络请求在各个分布式系统之间的调用情况,从而得到处理请求的调用链上的入口 URL、应用服务的调用关系,从而找到请求处理瓶颈,定位错误异常的根源位置。同时,业务方也可以在调用链上添加自己的业务埋点日志,使各个系统的网络调用与实际业务内容得到关联。
图 4:鹰眼系统的总体架构图
我们的解决方案
针对于微服务,东软平台产品提供了一套完整的微服务解决方案,在此基础上,对微服务架构进行了扩展,基于 Google Dapper 的概念,设计了一套基于微服务架构的分布式跟踪系统。
该跟踪系统支持基于 dubbo 的微服务框架的监控,以及分布式服务调用链跟踪:
图 5:东软 UniEAP 平台基于微服务的分布式跟踪系统流程设计图
分布式跟踪系统的整体流程是通过扩展 dubbo 作为入口,把监控代码植入到 dubbo-filter 扩展模块中,然后通过 trace-client 模块抓取日志数据,存入文件服务器,为了避免产生大量 Trace 对象造成内存堆积引发的 GC 问题,trace 首先采取写入堆外内存的方式落地,然后通过 trace-agent 代理模块读取日志信息,给 trace-collector 收集模块提供日志数据,trace-collector 收集模块通过提供统一的接口,为外部获取数据提供支持,可以传输到数据库记录,或者传输给其他的数据分析产品。Trace-web 模块作为可视化展示平台,展现服务的完整跟踪链条、心跳监控信息、dubbo 原生监控信息,达到数据的展示工作。
除了数据的跟踪以外,该系统还会整合数据分析功能:
图 6:集成数据分析功能,提升数据的利用价值
通过集成可视化分析产品,简单的分布式跟踪功能变成了可视化的监控系统,跟踪中产生的数据,会发挥出更大的价值:
- 实时抓取数据,实时分析统计,生成统计和报表
- 可视化的报表系统,更快的了解系统运行状态
- 搜索功能,快速的定位关键问题
- 数据挖据,利用优秀的数据分析能力和数据模型,进行数据预测
- 分布式跟踪系统应用展望
在分布式场景越来越普及的今天,分布式跟踪将作为微服务必备的基础组件存在,完善的跟踪系统可以极大的提高开发和运维的效率,是企业应用发展不可获缺的组成部分,特别是以下几点:
1、业务流调用流程跟踪
分布式系统的所有日志信息可以通过完善的跟踪串联起来,使系统的运维更加稳定,定位问题更加准确。
2、可视化的监控界面
通过分布式跟踪系统的可视化监控页面,避免了去服务器上查看日志的烦恼。
3、业务分析
在使用了分布式跟踪系统后,我们可以快速的梳理每个调用链路,分析耗时过长的业务是如何产生的,并且可以定位性能不佳的调用片段,合理的分析问题,为性能优化提供非常有价值的参考意见。
作者简介
张德阳,东软集团基础软件事业部技术总监,具有十多年基础平台软件的开发、架构和实施经验,目前主要关注微服务、DevOps、数据分析等领域。
感谢郭蕾对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ , @丁晓昀),微信(微信号: InfoQChina )关注我们。
评论