蚂蚁金服分布式链路跟踪组件 SOFATracer 总览|剖析

简介: SOFATracer 是一个用于分布式系统调用跟踪的组件,通过统一的 TraceId 将调用链路中的各种网络调用情况以日志的方式记录下来,以达到透视化网络调用的目的,这些链路数据可用于故障的快速发现,服务治理等。

SOFA S calable O pen F inancial A rchitecture
是蚂蚁金服自主研发的金融级分布式中间件,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。 SOFATracer 是一个用于分布式系统调用跟踪的组件,通过统一的 TraceId 将调用链路中的各种网络调用情况以日志的方式记录下来,以达到透视化网络调用的目的,这些链路数据可用于故障的快速发现,服务治理等。
SOFAT racer https://github.com/alipay/sofa- data-type="color" style="color:rgb(51, 51, 51)">tracer
本文为《剖析 | SOFATracer 框架》第一篇。《剖析 | SOFASOFATracer 框架》系列由 SOFA 团队和源码爱好者们出品,
项目代号:<SOFA:TracerLab/> 文章尾部有参与方式,欢迎同样对源码热情的你加入

0、前言


在单体应用时代,我们不需要花费时间去关心调用链路这个东西。但是链路跟踪不仅仅是在分布式场景下才会有,即使是单体应用,同样也会存在调用链路。例如,我们把应用中的每个服务接口作为一个链路节点,那么从请求进来到返回响应,把这个过程中多历经的所有的方法接口串联起来,就能组成一条完整的链路,如下图所示:



对于单体应用而言,如果访问一个资源没有成功,那么我们可以很快的锁定是哪一台机器,然后通过查询这台机器上的日志就能定位问题。


但是在微服务体系架构下,这种方式会显得非常无力。对于一个稍具规模的应用来说,一次请求可能会跨越相当多的服务节点,在这种情况下,如果一个请求没有得到成功的响应,就不能确定到底是哪个节点出了问题。



因此在面对这种复杂的大规模分布式集群来实现的服务体系来说,就需要一些可以帮助理解各个应用的线上调用行为、并可以分析远程调用的组件。


基于上述背景,蚂蚁金服开源了基于 OpenTracing 规范http://opentracing.io/documentation/pages/spec.html data-type="color" style="color:rgb(38, 38, 38)">)实现的 SOFATracer 分布式链路跟踪组件,为实施大规模服务化体系架构场景下提供了链路跟踪的解决方案。


在介绍 SOFATracer 之前,先来了解一下 Opentracing 规范。


1、Opentracing 简介


首先来解释下 OpenTracing 是什么OpenTracing力于为分布式跟踪创建更标准化的API和工具,它由完整的API规范、实现该规范的框架、库以及项目文档组成。


OpenTracing 提供了一套平台无关、厂商无关的 API,这样不同的组织或者开发人员就能够更加方便的添加或更换追踪系统的实现。 OpenTracing API 中的一些概念和术语,在不同的语言环境下都是共享的。


1.1、数据模型


Opentracing 规范中,一条 trace 链路是由多个与之关联的 span 组成,一条链路整体可以看做是一张有向无环图,各个span之间的边缘关系被称之为“References”。下面是官方提供的示例:



如果已时间轴维度来看的话,也可以表现为下面的形式(官方示例):


root span : 当前链路中的第一个 span
ChildOfFollowFrom 是目前被定义的两种 References 类型
  • ChildOf : 父级 span某种程度上取决于子span (子span的结果可能会对父span产生影响)
  • FollowFrom : Span 不以任何方式依赖子 Span


但是为了简化 span 之间的这种依赖关系,在具体实现时通常会将具有嵌套关系的作为 ChildOf,平行执行的作为FollowFrom,比如:


a、ChildOf 示例


methodA 中调用了 method B :


methodA(){            // spanA start
    methodB();            
}                     // spanA finish
methodB(){            // spanB start
}                     // spanB finish

产生的 span在时间维度上展现的视角如下:



这种关系一般会 表示为 SpanB ChildOf SpanA


b、FollowFrom 示例


method 方法中,methodA执行之后 methodB 执行 :


method(){
    methodA();
    methodB();
}

产生的 span在时间维度上展现的视角如下:



这种关系一般会 表示为 SpanB FollowFrom SpanA


1.2、API


Opentracing API 是对分布式链路中涉及到的一些列操作的高度抽象集合。Opentracing 中将所有核心的组件都声明为接口,例如 TracerSpanSpanContextFormat(高版本中还包括 ScopeScopeManager)等。SOFATracer 使用的版本是 0.22.0 ,主要是对 TracerSpanSpanContext 三个概念模型的实现。下面就针对这三个组件结合 SOFATracer 来分析。


1.3、SOFATracer 标准实现


下图为 SOFATracer 中对于这三个核心接口实现的类图结构:



由于篇幅原因,下面的介绍过程中一些点不会展开说明,有兴趣的同学可以自行官网查看完整的 OpenTracing-api 规范https://opentracing.io/specification/)。


a、Tracer & SofaTracer


Tracer 是一个简单、广义的接口,它的作用就是构建 span 和传输 span 。核心接口列表如下:

SpanBuilder buildSpan(String operationName) 根据指定的operationName构建一个新的span
void inject(SpanContext spanContext, Format format, C carrier); 将 spanContext 以 format 的格式注入到 carrier 中
SpanContext extract(Format format, C carrier); 以 format 的格式从carrier中解析出 SpanContext

SofaTracer 实现了 Tracer 接口,并扩展了采样、数据上报等能力。


b、Span & SofaTracerSpan


Span 是一个跨度单元,在实际的应用过程中,Span 就是一个完整的数据包,其包含的就是当前节点所需要上报的数据。核心接口列表如下:

SpanContext context() 从 span 中获取 SpanContext
void finish()/void finish(long finishMicros) 结束一个 span
void close() 关闭 span
Span setTag(String key, value) 设置 tags
Span log(long timestampMicroseconds, String event) 设置 log 事件
Span setOperationName(String operationName) 设置span的operationName
Span setBaggageItem(String key, String value) 设置 BaggageItem
String getBaggageItem(String key) 获取 BaggageItem

关于tagslog的解释:如果把从进入公司到离开公司这段时间作为一个 span,那么 tags 里面可以是你写的代码,你喝的水,甚至你讲过的话;log 则更关注某个时刻的事,比如在12:00 去吃了个饭,在15:00 开了个会。
如果说 tags 里面都是和公司有关的,那么 Baggage 里面则不仅仅是局限于你在公司的事,比如你口袋里的手机。

SofaTracerSpan 在实现 Span 接口,并扩展了对 Referencetags、线程异步处理以及插件扩展中所必须的 logType和产生当前 spanTracer类型等处理的能力。

c、SpanContext & SofaTracerSpanContext


SpanContext 对于 OpenTracing 实现是至关重要的,通过 SpanContext 可以实现跨进程的链路透传,并且可以通过 SpanContext 中携带的信息将整个链路串联起来。

官方文档中有这样一句话:“在 OpenTracing 中,我们强迫 SpanContext 实例成为不可变的,以避免 Spanfinishreference 操作时会有复杂的生命周期问题。” 这里是可以理解的,如果 SpanContext 在透传过程中发生了变化,比如改了 tracerId,那么就可能导致链路出现断缺。

SpanContext 中只有一个接口:

接口 描述
Iterable> baggageItems();.entry 拿到所有的baggageItems 透传数

SofaTracerSpanContext 实现了 SpanContext 接口,扩展了构建 SpanContext、序列化 baggageItems 以及SpanContext等新的能力,除此之外,SpanContext 在跨进行透传时携带的信息进行了规范:

携带信息 描述
traceId 全链路唯一的标识信息
spanId spanId
parentId 父 spanId
isSampled 采样标记
sysBaggage 系统透传数据
bizBaggage 业务透传数据

2、SOFATracer 扩展


为了满足在复杂场景下的链路跟踪需求,SOFATracerOpentracing 规范基础上又提供了丰富的扩展能力。


2.1、SOFATracer 架构及功能扩展



SOFATracer 基于 OpenTracing 规范https://opentracing.io/specification/)实现,并且通过 Disruptorhttps://github.com/LMAX-Exchange/disruptor)组件实现了日志的无锁异步打印能力。


基于 SLF4J 的 MDC 扩展能力

应用在通过面向日志编程接口 SLF4J 打印应用日志时,可以只在对应的日志实现配置文件的 PatternLayout 中添加相应的参数即可,如添加 [%X{SOFA-TraceId},%X{SOFA-SpanId}] ,那么应用日志就可以在发生链路调用时打印出相应的 TraceIdSpanId ,而无论应用具体的日志实现是 LogbackLog4j2 或者 Log4j。关于这部分的实现原理,期待大家一起编写,领取方式见文末。

SOFATracer 的埋点机制

SOFATracer 目前仅提供了基于自身 API 埋点的方式。SOFATracer 中所有的插件均需要实现自己的 Tracer 实例,如 MvcSpringMvcTracerHttpClientHttpClientTracer 等,如下图所示:



SOFATracer 将不同的扩展组件分为 AbstractClientTracerAbstractServerTracer,再通过AbstractClientTracerAbstractServerTracer 衍生出具体的组件 Tracer 实现。这种方式的好处在于,所有的插件实现均有 SOFATracer 本身来管控,对于不同的组件可以轻松的实现差异化和定制化。


但是为了能够拥抱社区,我们在后续的版本中将会提供基于 Opentracing API 的埋点扩展实现,从而实现与 opentracing-contrib 的无缝对接。基于 Opentracing API 的插件埋点方案如下图所示:



关于 SOFATracer 基于特有 API 埋点的实现以及如何实现对接 OT-api 埋点,期待大家一起编写,领取方式见文末。

SOFATracer 的数据上报机制

SOFATracer 中并没有将不同的 Reporter 设计成不同的策略,然后根据不同的策略来实现具体的上报操作,而是使用了一种类似组合的方式,并且在执行具体上报的流程中通过参数来调控是否执行具体的上报。



从流程图中可以看到,此过程中涉及到了三个上报点,首先是上报到 zipkin,后面是落盘;在日志记录方面,SOFATracer 中为不同的组件均提供了独立的日志空间,除此之外,SOFATracer 在链路数据采集时提供了两种不同的日志记录模式:摘要日志和统计日志,这对于后续构建一些如故障的快速发现、服务治理等管控端提供了强大的数据支撑。关于数据上报,期待大家一起编写,领取方式见文末


  • SOFATracer 的采样机制

对于链路中的数据,并非所有的数据都是值得关注的。一方面是可以节约磁盘空间,另一方面可以将某些无关数据直接过滤掉。基于此,SOFATracer 提供了链路数据采样能力。目前我们提供了两种策略,一种是基于固定比率的采样,另一种是基于用户扩展实现的自定义采样;在自定义采样设计中,我们将 SofaTracerSpan 实例作为采样计算的条件,用户可以基于此实现丰富的采样规则。关于采样机制,期待大家一起编写,领取方式见文末


  • SOFATracer 链路透传机制

关于透传机制,我们不仅需要考虑线程内传递,还需要考虑跨线程以及异步线程场景,对于分布式链路来说,最核心还有如何实现跨进程的数据透传。关于 SOFATracer 链路透传 以及 OpenTracing 新规范中对线程传递的支持,期待大家一起编写,领取方式见文末。


2.2、SOFATracer RoadMap


首先介绍下目前 SOFATracer 的现状和一些正在做的事情。



SOFATracer 版本说明:3.x 版本支持 webflux 等,基于分支发布。
2.x 版本基于master 发布,目前版本是 2.3.0 。

欢迎对相关功能和 feature 有兴趣的同学,一起参与开发~


3、欢迎加入 ,参与 SOFATracer 源码解析


本文作为《剖析 | SOFATracer 组件系列》第一篇,主要还是希望大家对 SOFATracer 组件有一个认识和了解,之后,我们会逐步详细介绍每部分的代码设计和实现,预计会按照如下的目录进行:


  • 分布式链路跟踪组件 SOFATracer 概述
  • SOFATracer 数据上报机制和源码分析
  • SOFATracer API组件埋点机制和源码分析
  • SOFATracer链路透传原理与 SLF4J MDC 的扩展能力分析
  • SOFATracer 的采样策略和源码分析

如果有同学对以上某个主题特别感兴趣的,可以留言讨论,我们会适当根据大家的反馈调整文章的顺序,谢谢大家关注 SOFA ,关注 SOFATracer,我们会一直与大家一起成长的。


领取方式:
关注公众号:金融级分布式架构(Antfin_SOFA),后台回复想认领的文章名称,我们将会主动联系你,确认资质后,即可加入 ,It's your show time!


除了源码解析,也欢迎提交 issue 和 PR:
SOFATracerhttps://github.com/alipay/sofa- data-type="color" style="color:rgb(51, 51, 51)">tracer

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
2月前
|
设计模式 安全 Java
【分布式技术专题】「Tomcat技术专题」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)
【分布式技术专题】「Tomcat技术专题」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)
34 0
|
2月前
|
NoSQL Java Redis
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件(一)
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件
42 0
|
2月前
|
NoSQL Java Redis
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件(二)
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件
15 0
|
5天前
|
Windows
Windows系统下安装分布式事务组件Seata
Windows系统下安装分布式事务组件Seata
|
8天前
|
NoSQL Java 关系型数据库
【Redis系列笔记】分布式锁
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
29 2
|
26天前
|
NoSQL Java Redis
redis分布式锁
redis分布式锁
|
2天前
|
监控 NoSQL 算法
探秘Redis分布式锁:实战与注意事项
本文介绍了Redis分区容错中的分布式锁概念,包括利用Watch实现乐观锁和使用setnx防止库存超卖。乐观锁通过Watch命令监控键值变化,在事务中执行修改,若键值被改变则事务失败。Java代码示例展示了具体实现。setnx命令用于库存操作,确保无超卖,通过设置锁并检查库存来更新。文章还讨论了分布式锁存在的问题,如客户端阻塞、时钟漂移和单点故障,并提出了RedLock算法来提高可靠性。Redisson作为生产环境的分布式锁实现,提供了可重入锁、读写锁等高级功能。最后,文章对比了Redis、Zookeeper和etcd的分布式锁特性。
29 16
探秘Redis分布式锁:实战与注意事项
|
4天前
|
NoSQL Java 大数据
介绍redis分布式锁
分布式锁是解决多进程在分布式环境中争夺资源的问题,与本地锁相似但适用于不同进程。以Redis为例,通过`setIfAbsent`实现占锁,加锁同时设置过期时间避免死锁。然而,获取锁与设置过期时间非原子性可能导致并发问题,解决方案是使用`setIfAbsent`的超时参数。此外,释放锁前需验证归属,防止误删他人锁,可借助Lua脚本确保原子性。实际应用中还有锁续期、重试机制等复杂问题,现成解决方案如RedisLockRegistry和Redisson。
|
5天前
|
缓存 NoSQL Java
【亮剑】如何使用注解来实现 Redis 分布式锁的功能?
【4月更文挑战第30天】分布式锁是保证多服务实例同步的关键机制,常用于互斥访问共享资源、控制访问顺序和系统保护。基于 Redis 的分布式锁利用 SETNX 或 SET 命令实现,并考虑自动过期、可重入及原子性以确保可靠性。在 Java Spring Boot 中,可通过 `@EnableCaching`、`@Cacheable` 和 `@CacheEvict` 注解轻松实现 Redis 分布式锁功能。
|
6天前
|
NoSQL Redis 微服务
分布式锁_redis实现
分布式锁_redis实现