微服务极大地改变了软件的开发和交付模式单体应用被拆分为多个微服务单个服务的复杂度大幅降低库之间的依赖也转变为服务之间的依赖。由此带来的问题是部署的粒度变得越来越细众多的微服务给运维带来巨大压力即使有了Docker 容器和服务编排组件 Kubernetes这依然是个严肃的问题。常见的追踪分布式系统调用链路的方式在分布式系统场景中一个请求可能需要经历多个业务单元的处理才能完成响应如果出现了错误或异常是很难定位的。因此利用分析性能问题的工具以及理解系统的行为就变得很重要了。对于早期系统或者服务来说开发人员一般通过打日志的方式来进行埋点常用的数据采集方式然后再根据日志系统和性能监控定位及分析问题。采用日志打点的方式虽然可以排查大部分的问题但是侵入性非常大且对于出现的紧急问题往往并不能快速进行响应处理。对于排查性能问题涉及更改的工作量更大具体到调用的每个服务和服务里面的方法得到的结果往往也是事倍功半。除了日志打点的方式有时甚至会出现开发人员直接连接服务器进行代码调试的情况这种方式的优点是能够针对请求涉及的某个服务进行排查或许也能快速解决但其弊端是显而易见的线上生产环境很多时候并不具备直接Debug 调试的条件这种调试也是严重影响了线上服务的正常运行。特别是随着业务变得越来越复杂现代互联网服务通常会使用复杂的、大规模的分布式系统来实现。虽说传统的日志监控和服务器调试的方式也可以解决业务异常问题但是很显然这两种方式已经无法满足跟踪调用、排查问题等一系列需求了。为什么需要分布式链路追踪如上所述随着服务数量的增多和内部调用链的复杂化开发者仅凭借日志和性能监控难以做到全局的监控在进行问题排查或者性能分析时无异于盲人摸象”。为了解决这个问题业界推出了分布式链路追踪组件。Google 内部开发了Dapper用于收集更多的复杂分布式系统的行为信息Twitter开源了分布式链路追踪组件Zipkin同时也有很多其他公司开发了自己的链路追踪组件。分布式链路追踪不仅能够帮助开发者直观分析请求链路快速定位性能瓶颈逐渐优化服务间的依赖而且还有助于开发者从宏观角度更好地理解整个分布式系统。什么是分布式链路追踪在微服务架构下原单体服务被拆分为多个微服务独立部署客户端的请求涉及多个微服务从而无法知晓服务的具体位置。系统由大量服务组成这些服务可能由不同的团队开发可能使用不同的编程语言来实现多实例部署这些实例横跨多个不同的数据中心。在这种环境中当出现错误异常或性能瓶颈时获取请求的依赖拓扑和调用详情对于解决问题是非常有效的。所谓分布式链路追踪就是记录一次分布式请求的调用链路并将分布式请求的调用情况集中展示。其中调用详情包括各个请求的服务实例信息、服务节点的耗时、每个服务节点的请求状态等分布式链路追踪还可以分析出请求的依赖拓扑即这次请求涉及哪些服务、这些服务上下游的关系等这对于排查性能瓶颈非常有帮助。链路追踪与日志、Metrics 的关系在上文我们提到早期通常是使用日志和监控的方式来排查系统问题的但在工作中我发现一些开发者对链路追踪和日志、Metrics三者之间的关系并不是很清楚经常会混淆因此这里我们就简要介绍下这三者。Tracing 表示链路追踪Logging 和 Metrics 是与之相近的两个概念。这三者的关系如下图所示Tracing记录单个请求的处理流程其中包括服务调用和处理时长等信息。Logging用于记录离散的日志事件包含程序执行到某一点或某一阶段的详细信息。Metrics可聚合的数据通常是固定类型的时序数据包括 Counter、Gauge、Histogram 等。同时这三者相交的情况或者说混合出现也比较常见。Logging Metrics可聚合的事件。例如分析某对象存储的Nginx日志统计某段时间内GET、PUT、DELETE、OPTIONS 操作的总数。Metrics Tracing单个请求中的可计量数据。例如SQL 执行总时长、gRPC调用总次数等。Tracing Logging请求阶段的标签数据。例如在Tracing 的信息中标记详细的错误原因。针对这每种分析需求我们都有非常强大的集中式分析工具。LoggingELKElasticsearch、Logstash和KibanaElastic 公司提供的一套完整的日志收集以及展示的解决方案。MetricsPrometheus专业的Metric 统计系统存储的是时序数据即按相同时序相同名称和标签以时间维度存储连续数据的集合。TracingJaeger是 Uber 开源的一个兼容 OpenTracing 标准的分布式追踪服务。通过以上讲解你现在应该知道Tracing、Logging 和 Metrics 这三者之间有一定的关系既可以单独使用也可以组合使用。每一个组件都有其侧重点Tracing用于追踪具体的请求绘制调用的拓扑Logging 则是主动记录的日志事件Metrics 记录了请求相关的时序数据通常用于性能统计。在分布式系统中这三者通常是组合在一起使用。分布式链路追踪的基础概念分布式链路追踪组件涉及 Span、Trace、Annotation 等基本概念这些概念还是比较重要的所以下面我们就具体介绍下这些概念。Span分布式链路追踪组件的基本工作单元。一次请求即发起的一次链路调用可以是RPC、DB调用等会创建一个 Span。通过一个64位ID标识Span通常使用UUIDSpan中还有其他的数据例如描述信息、时间戳、parentlD 等其中 parentlD 用来表示 Span 调用链路的层级关系。TraceSpan 集合类似树结构。表示一条完整的调用链路存在唯一标识。Trace代表了一个事务或者流程在系统中的执行过程由多个 Span 组成的一个有向无环图每一个 Span 代表 Trace中被命名并计时的连续性的执行片段。Annotation注解。用来记录请求特定事件相关信息、例如时间通常包含4 种注解信息分别包括①CSClient Sent表示客户端发起请求②SRSerVer Received表示服务端收到请求③SSSerVerSent表示服务端完成处理并将结果发送给了客户端④CRClientReceived表示客户端获取到服务端返回信息。链路信息的还原依赖于两种数据一种是各个节点产生的事件如CS、SS称为带外数据这些数据可以由节点独立生成并且需要集中上报到存储端另一种数据是TraceID、SpanID、ParentlD用来标识 Trace、Span 以及 Span 在一个Trace中的位置称为带内数据这些数据需要从链路的起点一直传递到终点。通过带内数据的传递可以将一个链路的所有过程串起来通过带外数据可以在存储端分析更多链路的细节。下图展示了Trace 树的运行机制通过 Trace 树我们可以了解一次请求过程中链路追踪的基本运行原理。对于每个 Trace 树Trace 都要定义一个全局唯一的TraceID在这个跟踪中的所有 Span 都将获取到这个TracelD。每个 Span 都有一个 ParentID 和它自己的 SpanlD。上面图中 Frontend Request 调用的ParentlD 为空SpanID 为 1然后 Backend Call 的 ParentlD 为1SpanID 为 2BackendDoSomething调用的 ParentID 也为1SpanlD 为 3其内部还有两个调用Helper Call 的 ParentID为3SpanID为4以此类推。Span 表示一个服务调用的开始和结束时间即执行的时间段。分布式链路追踪组件记录了Span的名称以及每个 SpanID 的 ParentlD如果一个 Span 没有 ParentlD 则被称为 Root Span当前节点的ParentlD 即为调用链路上游的 SpanlD所有的 Span 都属于一个特定的 Trace共用一个TraceID。小结分布式链路追踪组件主要用来追踪分布式系统调用链路的问题。本文我们主要介绍了分布式链路追踪组件产生的背景以及分布式链路追踪的相关概念。分布式链路追踪组件对于快速解决线上问题、发现性能瓶颈并优化分布式系统的性能、合理部署服务器资源具有重要的作用。在接下来我们将具体介绍几种业界流行的分布式链路追踪组件并选择其中的一款进行实践。