速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

深入可观测底层:OpenTelemetry 链路传递核心原理

  • 2022-06-22
  • 本文字数:5983 字

    阅读完需:约 20 分钟

深入可观测底层:OpenTelemetry 链路传递核心原理

前言


本文会系统讲解链路传递一些基本概念,同时结合案例讲解链路传递的过程。

Context、Propagator 的概念


我们先看一张在 HTTP 通信下,进行链路传播的例子。



图中是一个 Client 请求 Server 通信,同时 Client 把自己的链路信息传递给 Server 的过程

可以看到,链路信息通过一个叫 TraceContext 对象封装起来,通过 Http Headers

来存取这个对象,最后达到传播的效果,这里面 TraceContext 就是一个 Context 上下文对象。

 

Context 是一种传播机制,它跨越 API 边界,在逻辑执行单元间传递执行范围的值。链路追踪系统在系统进程内部,进程间通过共享进程的 Context 上下文 对象传递链路信息。


刚才 Http Headers 起到了一个传递 Context 载体作用,在链路追踪系统,我们把这样的载体统称为 Propagators。上面传递过程我们用另一张图看看 Propagators 和 Context 的关系:

 


什么是 Propagator?OpenTelemetry 是这样定义的


Cross-cutting concerns send their state to the next process using Propagators,  which are defined as objects used to read and write context data to and from messages exchanged by the applications.


大概意思,横切关注点通过它,在进程间进行状态的传递。这里,又牵涉了一个重要的知识点。

Cross-cutting concerns 的概念


关注点:如果系统按功能划分,关注点就是系统的核心功能。 横切关注点:从整个系统横向维度看到的核心功能。比如日志功能就是横切关注点的一个典型案例。日志功能往往横跨系统中的每个业务模块。图可能比文字来得更形象:



横切关注点(系统基本功能)通过 Propagator 可以传递它的状态给下一个进程,Propagator 是通过应用程序读写上下文数据进行数据的交换。 每个关注点都包含了一组 Propagator Propagator 把每一个横切关注点通过上下文去 Injection 注入和 Extract 提取。

 

链路传递一般通过特定的请求拦截器库来实现,拦截器通过拦截输入和输出请求,利用对 Propagator 的注入和提取操作来实现传递。


Propagator API 用户可以通过检测库来实现 Propagator 操作的 API。

Baggage 链路传递初探


在一个链路追踪过程中,我们一般会有多个 Span 操作, 为了把调用链状态在 Span 中传递下去,期望最终保存下来,比如打入日志、保存到数据库。SpanContext 会封装一个键值对集合,我们叫它Baggage .

Baggage 会在一条追踪链路上的所有 Span 内全局传输。原理是上一个 Span 将 Context 中 Baggage 注入到 Propagator ,Propagator 和目的 Span 联系,从 Propagator 提取出Baggage ,再写入到目的 Span 的 Context 中。


OpenTelemetry 中关于 Baggage 原文定义:

Baggage is used to annotate telemetry, adding context and information to metrics, traces, and logs. It is a set of name/value pairs describing user-defined properties.


通过 Baggage 我们可以实现强大的追踪功能。例如:在用户的手机端添加一个 Baggage 元素,并通过层层服务调用,传递到最后数据库的操作。有了 Baggage,如果我想定位请求中消耗很大的 SQL 语句是来自于具体哪个手机端用户从 Trace 中一看便知。反过来,我们可以知道一个手机端用户它依次做了哪些 SQL 语句查询。这比你登录服务器一台台找前端、后端日志,还要把他们关联起来分析,要容易很多。


方便理解,我们演示 Baggage 效果:


首先,我们在 loadBalancer 请求中加一个 Baggage,loadBalancer 请求了 resource 服务:

@GetMapping("/loadBalancer")@ResponseBodypublic String loadBalancer(String tag){    Span span = Span.current();          //保存 Baggage	Baggage.current()	  .toBuilder()	  .put("app.username", "蒋志伟")	  .build()	  .makeCurrent();......    ##请求 resourcehttpTemplate.getForEntity(APIUrl+"/resource",String.class).getBody();  
复制代码


然后我们从 resource 服务中获取 Baggage 信息,并把它存储到 Span 的 Attributes 中:

@GetMapping("/resource")@ResponseBodypublic String resource(){	String baggage = Baggage.current().getEntryValue("app.username");	Span spanCur = Span.current();     ##获取当前的 Span,把 Baggage 写的 resource		spanCur.setAttribute("app.username",                          "baggage 传递过来的 value: "+baggage);
复制代码


最终,我们从跟踪系统的链路 UI 中点击 resource 这个 Span,找到传递的 Baggage 信息:

图五:展示 Baggage 的传递

 

当然,Baggage 拥有强大功能,也会有很大的消耗。由于 Baggage 的全局传输,每个键值都会被拷贝到每一个本地(local)及远程的子 Span,如果包含的数量量太大,或者元素太多,它将降低系统的吞吐量或增加 RPC 的延迟。

Tag 链路标签和业务监控


我们进行系统链路追踪,除了 Trace 本身自带信息,如果我们还希望添加自己关注的监控。Trace 支持用打标签 Tags 方式来实现。Tags 本质还是 Span 的 Attributes(在 OpenTelemetry 定义中,统称 Attributes。在 Prometheus、Jaeger 里面沿袭 Tags 老的叫法)。

 

打 Tags 的过程,其实就是在 Span 添加我们自定义的 Attributes 信息,这些 Tags 大部分和我们业务息息相关,为了更方便做业务监控、分析业务。

 

我们看一个 Java 打 Tags 的例子:页面定义好了一个 Tag,名字叫"username"。我们输入 Tags 的值,然后把 Tags 通过一个 HTTP 请求发送给付账单的 API,


图六:打 Tags 的演示


 API 获取 Tags 后,把它保存到当前 Span 的 Attribute 中。这个 Span 对应的是代码里面的一个 gateway 方法, 如果不重名 Span 名称,默认使用 gateway 作为 Span 名称:

@GetMapping("/loadBalancer")public String gateway(String tag){   Span Span = Span.current();          ##获取当前 Span,添加 username 的 tag   Span.setAttribute("username", tag);      ...... }   
复制代码


打了 Tags 后,我们可以在跟踪系统搜索 Tag 关键字。通过 Tag 可以快速找到对应的 Trace:


图七:基于 Tags 的搜索

 

可以看到,根据 Tags 的 key 我们可以很方便筛选想要的 Span 信息。实际场景里,我们面临是从成千上万链路中快速定位访问异常的请求。打 Tags 对我们诊断程序非常有帮助。


我们从 Trace 中找到相应的 Span,能看到 Tag 已经注入到 gateway 的 Span 中:


图八:Tags 的用法


 Baggage 和 Span Tags 的区别


  • Baggage 在全局范围内,(伴随业务系统的调用)在所有 Span 间传输数据。Tags 不会进行传输,因为他们不会被子级的 Span 继承;

  • Span 的 Tags 可以用来记录业务相关的数据,并存储于追踪系统中。

Event 事件 监控更细微的链路行为


一个 Span 下可以添加多个事件,事件是相对于 Span 更小的操作粒度。在我们想知道一个 Span 内各种操作,这种操作期望在监控平台能够分析,Event 就可以满足需求。

 

比如请求一个商品结算的 API,这个 API 调用商品结算方法 Count,Count 里面分别做了查询商品库存,核实商品价格,结算总价这几个步骤,商品库存还是由几次数据库的 SQL 查询聚合的结果。如果这个 API 有异常,查看商品结算这个 Span 往往远远不够,在真实的线上场景,我们还需要追踪每个步骤的请求状态和详细日志。


事件和 Span 的数据结构类似,包含的基本属性:

  • 事件名称;

  • 事件时间:它支持由开发者自定义添加时间,让事件时间更精准;

  • 属性 Attributes:和 Span 一样,用来记录事件相关信息。

	@GetMapping("/event")		public String event() {			Span span = Span.current();			span.updateName("创建eventDemo");				// 手动更新Event持续时间			span.addEvent("time.update", System.currentTimeMillis() + 2000, TimeUnit.MILLISECONDS);			// 给Event添加相关信息			Attributes appInfo = Attributes.of(AttributeKey.stringKey("app.id"), "123456",					AttributeKey.stringKey("app.name"), "应用程序demo");			span.addEvent("appinfo.query", appInfo);			logger.info("this is a event");			return buildTraceUrl(span.getSpanContext().getTraceId());		}
复制代码


我们添加 Event 后,从链路中看到 Event 信息记录在 Trace Logs 中。在事件中,我们还能添加相关 Attributes ,记录事件某些信息:



OpenTelemetry 链路数据如何传播  

Carrier 和 Propagator 的关系

在 OpenTelemetry 中,Trace 的传递中有一个核心的概念,叫 Carrier(搬运工具)。它是 Propagator 用来读取 Context 数据的一种媒介 medium,Carrier 数据格式可能是一个字符 Map 或者一个字符数组。


Carrier 充当"搬运" Span 中 SpanContext 工具。例如 OpenTelemetry 中为了把 Trace 的 Span 信息传递下去,在 HTTP 调用场景中,会有 HttpCarrier,在 RPC 的调用场景中会有 RpcCarrier 来搬运 SpanContext。Trace 通过 Carrier 可以把链路追踪状态在进程中、进程间传递。

数据传播基本操作


回顾刚才一些概念,我们完整描述下 OpenTelemetry Span 在传播中有的基本步骤:


  • StartSpan:Trace 在具体操作中自动生成一个 Span

  • Inject 注入: 将 Span 的 SpanContext 写入到 Carrier 的过程


链路数据为了进行网络传输,需要数据进行序列化和反序列化。这个过程 Trace 通过一个负责数据序列化反序列化上下文的 Formatter 接口实现的。例如在 HttpCarrier 使用中通常就会有一个对应的 HttpFormatter。所以 Inject 注入是委托给 Formatter 将 SpanContext 进行序列化写入 Carrier。


Formatter 提供不同场景序列化的数据格式,叫做 Format 描述。比如:


  • TextMap: 基于字符串的 Map 记录 SpanContext 信息,适用 RPC 网络传输

  • HTTP Headers: 方便解析 HTTP Headers 信息,用于 HTTP 传输


一个 Python 程序实现 Inject 注入过程,Formatter 序列化 SpanContext 成 Text Map 格式:

##Trace 生成一个span    tracer = Tracer()    span = tracer.start_span(operation_name='test')    tracer.inject(        span_context=span.context,        format=Format.TEXT_MAP,        carrier=carrier)
复制代码

 

  • Extract 提取: 将 SpanContext 从 Carrier 中 Extract(提取出来):

span_ctx = tracer.extract(format=Format.TEXT_MAP, carrier={})
复制代码


同理,从 Carrier 提取的过程也需要委托 Formatter 将 SpanContext 反序列化。

运行原理


图一:链路数据在 HTTP 传递

 

我们基于 HTTP 通信解释传播原理。由图一,这个过程大致分为两步:


1、发送端将 SpanContext 注入到请求中,相应伪代码实现:

/**** 将 SpanContext 中的 TraceId,SpanId,Baggage 等根据 format 参数注入到请求中(Carrier)** carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)** err := Tracer.Inject(Span.Context(), opentracing.HTTPHeaders, carrier)**/Inject(sm SpanContext, format interface{}, carrier interface{}) error
复制代码


2、接收端从请求中解析出 SpanContext,相应伪代码实现:

// Inject() takes the `sm` SpanContext instance and injects it for// propagation within `carrier`. The actual type of `carrier` depends on/** 根据 format 参数从请求(Carrier)中解析出 SpanContext(包括 TraceId、SpanId、baggage)。** 例如: **  carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)**  clientContext, err := Tracer.Extract(opentracing.HTTPHeaders, carrier)**/Extract(format interface{}, carrier interface{}) (SpanContext, error)
复制代码


Carrier 负责将追踪状态从一个进程"Carry"(搬运)到另一个进程。对于一个 Carrier,如果已经被 Injected,那么它也可以被 Extracted(提取),从而得到一个 SpanContext 实例。这个 SpanContext 代表着被 Injected 到 Carrier 的信息。

 

说到这里,你可能想知道这个 Carrier 在 HTTP 中具体在哪。其实它就保存到 HTTP 的 Headers 中。而且,W3C 组织为 HTTP 支持链路追踪专门在 Headers 中定义了 Trace 标准:

https://www.w3.org/TR/trace-context/#trace-context-http-headers-format

W3C 组织是对网络标准制定的一个非盈利组织,W3C 是万维网联盟的缩写,像 HTML、XHTML、CSS、XML 的标准就是由 W3C 来定制

跨进程间传播数据


数据传播按照场景分为两类:进程内传播、跨进程间传播 Cross-Process-Tracing。


进程内传播是指 Trace 在一个服务内部传递,监控了服务内部相互调用情况,相当比较简单。追踪系统最困难的部分就是在分布式的应用环境下保持追踪的正常工作。任何一个追踪系统,都需要理解多个跨进程调用间的因果关系,无论他们是通过 RPC 框架、发布-订阅机制、通用消息队列、HTTP 请求调用、UDP 传输或者其他传输模式。所以业界谈起 Tracing 技术 往往说的是跨进程间的分布式链路追踪(Distrubute Tracing)。

 

我们用 OpenTelemetry 实践一个 HTTP 通信的 Trace 例子:


这是一个 Java 程序,我们向下游服务发起一个 HTTP 请求。


程序中使用 OpenTelemetry 的 inject 注入,通过 HTTP Headers 把 Trace 传递给 127.0.0.1:8080 的服务。传播前,手动还创建两个想要一块传播的 Attributes:

@GetMapping("/contextR")@ResponseBodypublic String contextR() {	TextMapSetter<HttpURLConnection> setter = new TextMapSetter<HttpURLConnection>() {		@Override		public void set(HttpURLConnection carrier, String key, String value) {			// 我们把上下文放到 HTTP 的 Header			carrier.setRequestProperty(key, value);		}	};	Span spanCur = Span.current();	Span outGoing = tracer.spanBuilder("/resource").setSpanKind(SpanKind.CLIENT).startSpan();	try {		URL url = new URL("http://127.0.0.1:8080/resource");		HttpURLConnection transportLayer = (HttpURLConnection) url.openConnection();		outGoing.setAttribute("http.method", "GET");		outGoing.setAttribute("http.url", url.toString());					// 将当前 Span 的上下文注入到这个 HTTP 请求中		OpenTelemetry.getPropagators().getTextMapPropagator().inject(Context.current(), transportLayer, setter);		// Make outgoing call...
复制代码


运行程序,从监控平台我们看到,Trace 从一个程序成功传递到了 127.0.0.1:8080 的服务:


跨进程传播数据

Github 中文文档


我们 OpenTelemetry Github 社区对链路传播中文介绍,目前还在逐步完善当中。也欢迎大家去贡献更多的中文内容。


  • Propagator 介绍

https://github.com/open-telemetry/docs-cn/blob/main/specification/context/api-propagators.md

  • Context 介绍

https://github.com/open-telemetry/docs-cn/blob/main/specification/context/context.md

  • Baggage 介绍

https://github.com/open-telemetry/docs-cn/blob/main/specification/baggage/api.md

 

作者介绍

蒋志伟,爱好技术的架构师,先后就职于阿里、Qunar、美团,前 pmcaffCTO,目前 OpenTelemetry 中国社区发起人。

欢迎关注我们的 Github 项目:https://github.com/open-telemetry/docs-cn

欢迎大家关注“OpenTelemetry”公众号,这是中国区唯一官方技术公众号。

 

2022-06-22 15:3111189

评论

发布
暂无评论
发现更多内容

香港 Web3 一周大事记: 胜利证券计划年底前在香港推出虚拟资产结构性产品及稳定币收益产品

TechubNews

PPSSPPSDL for mac(PSP游戏模拟器)附PSP游戏安装包 v1.17.1激活版

Rose

谈谈零信任

天翼云开发者社区

ssl 零信任

Microsoft 365 Office BusinessPro LTSC 2024 for Mac( 微软Office办公套件)v16.87激活版

iMac小白

RAW镜像格式介绍

天翼云开发者社区

云计算 虚拟化 raw

SaaS架构:流程架构分析

快乐非自愿限量之名

架构 SaaS 流程

权限系统:一文搞懂功能权限、数据权限

EquatorCoco

大数据 架构 权限

OpenAI 再发 Sora 新短片,传 Sora 两周内推出;李飞飞团队出品空间智能版 ImageNet丨 RTE 开发者日报

声网

Hive on Spark实战:深入理解与配置Spark集群

敏捷调度TASKCTL

scala MySQL hadoop hive jdk

标准、高效的管理测试用例和活动

测试人

软件测试

一体机场景ceph高可用介绍

天翼云开发者社区

云计算 Ceph

云手机群控和传统群控有什么区别?

Ogcloud

云手机 海外云手机 云手机群控 手机群控

macOS免费好用的硬盘测速工具 Blackmagic Disk Speed Test

Rose

Photoshop 2025 for mac(ps 2025图像编辑处理软件)v26.0中文激活版

iMac小白

Downie 4 for Mac(视频下载工具)v4.8.4中文版

iMac小白

IPLC、IEPL和MPLS的对比分析

Ogcloud

MPLS 企业组网 企业网络 IPLC IEPL

电池保养管理及续航提升工具 Endurance Mac破解版

Rose

易于使用的多连接数据库管理工具 Navicat Premium 16 Mac中文包

Rose

苹果电脑鼠标键盘捕捉工具 Macro Recorder Enterprise for mac

Rose

macOS Sequoia 15(Macos15系统)v15.1正式版

iMac小白

域名解析常见问题:什么是NS记录?域名解析更换NS多久生效?

国科云

一文彻底弄懂JUC工具包的CountDownLatch的设计理念与底层原理

不在线第一只蜗牛

Java 算法

Autodesk AutoCAD 2024 Mac(cad2024)v2024.3中文激活版

iMac小白

3D渲染和动画制作 Luxion KeyShot2023下载+Luxion KeyShot2023破解教程

Rose

Microsoft Office LTSC 2021 for Mac(office全家桶)v16.90正式激活版

iMac小白

外贸货币汇率转换解决方案

幂简集成

API 货币兑换API

豆包MarsCode 不允许你还没有女朋友

豆包MarsCode

程序员 AI 开发 程序

World Clock Deluxe for Mac(世界时钟豪华版)v4.19.3注册版

iMac小白

IK Multimedia T-RackS 6 MAX For mac (混音和母带制作软件)v6.1.0激活版

iMac小白

Maxon Cinema 4D R25中文破解版(c4d安装教程)-Mac&win

Rose

SmartSVN 11 Mac破解版 SVN客户端Mac软件安装

Rose

深入可观测底层:OpenTelemetry 链路传递核心原理_语言 & 开发_蒋志伟_InfoQ精选文章