基于Dapper的分布式链路追踪入门——Opencensus+Zipkin+Jaeger

简介: 最近做了一些分布式链路追踪有关的东西,写篇文章来梳理一下思路,或许可以帮到想入门的同学。下面我将从原理到demo为大家一一进行讲解,欢迎评论区交流~。
微信搜索公众号 「程序员白泽」,进入白泽的编程知识分享星球

最近做了一些分布式链路追踪有关的东西,写篇文章来梳理一下思路,或许可以帮到想入门的同学。下面我将从原理到demo为大家一一进行讲解,欢迎评论区交流~。

1. 分布式链路追踪出现原因

讲解分布式链路追踪出现的原因,分析dapper论文中给出的分布式链路追踪系统dapper的实现方式

1.1 分布式链路追踪的需求 —> Dapper论文 (2010)

image-20220110162359212

Dapper论文翻译版:
https://bigbully.github.io/Dapper-translation/

互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。因此,就需要一些可以帮助理解系统行为、用于分析性能问题、追踪请求传递过程的工具。

Dapper—Google生产环境下的分布式跟踪系统,其运作两年之后,Google发布了基于Dapper的论文,重点介绍Dapper的设计思想,为后续分布式链路追踪相关分析工具的产生提供理论支持。

接下来先介绍Dapper论文中是如何实现分布式链路追踪,并提炼核心概念—span。

1.2 Dapper的分布式跟踪

image-20220110162500965

左图展现的是一个和5台服务器相关的一个服务,包括:前端(A),两个中间层(B和C),以及两个后端(D和E)。当一个用户(这个用例的发起人)发起一个请求时,首先到达前端,然后发送两个RPC到服务器B和C。B会马上做出反应,但是C需要和后端的D和E交互之后再返还给A,由A来响应最初的请求。对于这样一个请求,简单实用的分布式跟踪的实现,就是为服务器上每一次你发送和接收动作来收集跟踪标识符(message identifiers)和时间戳(timestamped events)。

为了将所有记录条目与一个给定的发起者(例如,图中的RequestX)关联上并记录所有信息,Dapper倾向于应用程序或中间件明确地标记一个全局ID,从而连接每一条记录和发起者的请求,该方案最主要的缺点是,很明显,需要代码植入。但dapper开发者认为,可以把代码植入限制在一个很小的通用组件库中,从而实现了监测系统的应用对开发人员是有效地透明。

从形式上看,Dapper跟踪模型使用的树形结构,且Dapper中称追踪树上的每一个节点为span,span代表分布式链路追踪中的节点。

1.3 跟踪树和span

image-20220110162525596

在Dapper跟踪树结构中,树节点是整个架构的基本单元,而每一个节点又是对span的引用。节点之间的连线表示的span和它的父span直接的关系。

左图中说明了span在一个大的跟踪过程中是什么样的。Dapper记录了span名称,以及每个span的ID和父ID,以重建在一次追踪过程中不同span之间的关系。如果一个span没有父ID被称为root span。所有span都挂在一个特定的跟踪上,也共用一个跟踪id(在图中未示出)。在一个典型的Dapper跟踪中,我们希望为每一个RPC对应到一个单一的span上,而且每一个额外的组件层都对应一个跟踪树型结构的层级。

image-20220110162548826

左图给出了一个更详细的典型的Dapper跟踪span的记录点的视图。图中这种某个span表述了两个“Helper.Call”的RPC(分别为server端和client端)。span的开始时间和结束时间,以及任何RPC的时间信息都通过Dapper在RPC组件库的植入记录下来。如果应用程序开发者选择在跟踪中增加他们自己的注释(如图中“foo”的注释)(业务数据),这些信息也会和其他span信息一样记录下来。

Annotation:上述植入点足够推导出复杂的分布式系统的跟踪细节,使得Dapper的核心功能在不改动Google应用的情况下可用。然而,Dapper还允许应用程序开发人员在Dapper跟踪的过程中添加额外的信息,以监控更高级别的系统行为,或帮助调试问题。我们允许用户通过一个简单的API定义带时间戳的Annotation,这些Annotation可以添加任意内容。

1.4 跟踪的收集

image-20220110162606747

Dapper的跟踪记录和收集管道的过程分为三个阶段(参见左图)。首先,span数据写入(1)本地日志文件中。然后Dapper的守护进程和收集组件把这些数据从生产环境的主机中拉出来(2),最终写到(3)Dapper的Bigtable仓库中。一次跟踪被设计成Bigtable中的一行,每一列相当于一个span。Bigtable的支持稀疏表格布局正适合这种情况,因为每一次跟踪可以有任意多个span。

2. zipkin和jaeger介绍

2.1 ZipKin结构

image-20220110162730223

Zipkin是Twitter开源出来的一个Trace系统组件,通过将前两张图虚线框中的zipkin的结构与第三张图的dapper结构进行对比,明显可以印证:zipkin的实现中就参考了Google Dapper。如图1,每个instrumented节点会将链路追踪信息发送给zipkin的collector,然后由zipkin存储数据,提供ui显示链路监测情况。

注意:每个需要链路追踪的节点为span,每个span的信息都是独立发送给collector的(因为span中有统一的全局traceId,以及父spanId,因此,跟踪树是在等某次trace相关的所有span都发送到zipkin之后,由zipkin构件成树结构提供ui展示的)

2.2 Trace信息的发送与展示

image-20220110162815132

2.3 Jaeger结构与监测展示

摘自Jaeger官方文档:Jaeger, inspired by Dapper and OpenZipkin, is a distributed tracing system released as open source by Uber Technologies

Jaeger与Zipkin一样可以作为分布式链路追踪组件,但是后出现,使用Go语言开发,二者的技术选择取决于具体的项目需要,这里按下不表,但需要明确的是二者都是基于Dapper的分布式链路追踪组件,铭记下方左侧的Dapper结构图
image-20220110162838140

3. OpenCensus介绍

3.1 OpenCensus介绍

显然无论是Dapper、Zipkin、Jaeger在工作时都是在一个端口接收Collector发送的span数据,然后构建跟踪树并展示,必然需要被监听服务主动发送span数据,而发送span数据到指定追踪组件的行为必然需要相应api支持。

OpenCensus目前提供了一些语言的库,允许你捕捉、操作和导出指标和分布式跟踪到你选择的后端。因此关键在于如何构建span(使span之间建立上下游联系),以及如何将span发送到指定后端,接下来细谈
image-20220110162907170

3.2 OpenCensus —> Span构建

创建span的方法OpenCensus api提供了两个,参数中:Context.Context是一个接口类型,用于存放trace数据,用于在内存中层层传递,第二个方法多了一个名为parent的SpanContext类型,表示基于给定父span(来自外部request)创建一个span
image-20220110162937361

3.3 OpenCensus —> Span构建 —> 深入两个startSpan的源码探寻其使用场景

image-20220110163004850

image-20220110163029688

3.4 OpenCensus —> Span构建 —> startSpanInternal部分核心源码

image-20220110163055323

ab9592fb1e204db58fdf54ca8d0d2518~tplv-k3u1fbpfcp-zoom-1.image

这里如果在调用startSpanInternal()方法的时候hasParent为false(为nil)则会自动生成随机的traceId,显然不是我们想要的,而上面说明startSpan()方法是从ctx中获取parent,而startSpanWithRemoteParent()从形参中获取parent,因此我们使用第二方法用于在获取前端的traceId之后创建我们整个trace链路的第一个span,之后将span信息存入ctx于内存中传递,此后都调用startSpan()方法创建以后的span

3.5 OpenCensus —> Span的传播

刚刚讲解了span如何创建,并且也说明span在golang中将存放于context.Context接口描述的类型中,那么span在
一个微服务节点上,以及微服务节点之间的传递是如何实现的呢?下面引用一段字节运维团队的文章节选:

image-20220110163220740

换言之span是追踪树的最小单位,一个微服务节点上可以随服务处理流程提取多个span(利用context于内存传递Span信息),而跨微服务节点的context传递将由微服务框架实现,比如grpc在pb文件生成后,调用的方法中就自带context这个参数选项,只需要传入即可在微服务间传递context

注意trace最初的traceId是通过一个http request请求放在http header中传递到后端http server的,在此之后都将使用微服务框架去自动
传递span(context)信息

3.6 OpenCensus —> Exporter注册

Exporter将trace信息(span)发送到任何有能力消费它们的后端。Exporter本身可以改变而不需要改变你的客户端代码。这就是OpenCensus真正与供应商无关的原因。只需收集一次trace(span)信息,就可以同时导出到不同的后端。

整体的使用流程 :1. 注册Exporter(相当于声明要发送给哪个后端zipkin、jaeger等);2. 使用opencensus提供api构建span;3. 发送span(发送过程就会遍历所有注册的Exporter,将构建的span挨个发送到每个exporter指向的后端)。

其中2、3两个步骤的代码不用变动,每次如果需要添加或者置换目标后端,只需要修改Exporter即可。

4. demo结构介绍与演示

项目结构

e556ec3a77304d86a4835bcaa9d3f39c~tplv-k3u1fbpfcp-zoom-1.image

目录结构

a21afae4236b4e8c9e9b2c5de841761b~tplv-k3u1fbpfcp-zoom-1.image

4.1 http_server.go部分代码

  • main函数
    image-20220110163524295
  • sendHttp
    image-20220110163543060
  • callGrpcServer
    image-20220110163601864

4.2 使用Zipkin和Jaeger展示监听效果

  • Zipkin
    image-20220110163625945
  • Jaeger
    image-20220110163643295
相关实践学习
分布式链路追踪Skywalking
Skywalking是一个基于分布式跟踪的应用程序性能监控系统,用于从服务和云原生等基础设施中收集、分析、聚合以及可视化数据,提供了一种简便的方式来清晰地观测分布式系统,具有分布式追踪、性能指标分析、应用和服务依赖分析等功能。 分布式追踪系统发展很快,种类繁多,给我们带来很大的方便。但在数据采集过程中,有时需要侵入用户代码,并且不同系统的 API 并不兼容,这就导致了如果希望切换追踪系统,往往会带来较大改动。OpenTracing为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范。OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。Skywalking基于OpenTracing规范开发,具有性能好,支持多语言探针,无侵入性等优势,可以帮助我们准确快速的定位到线上故障和性能瓶颈。 在本套课程中,我们将全面的讲解Skywalking相关的知识。从APM系统、分布式调用链等基础概念的学习加深对Skywalking的理解,从0开始搭建一套完整的Skywalking环境,学会对各类应用进行监控,学习Skywalking常用插件。Skywalking原理章节中,将会对Skywalking使用的agent探针技术进行深度剖析,除此之外还会对OpenTracing规范作整体上的介绍。通过对本套课程的学习,不止能学会如何使用Skywalking,还将对其底层原理和分布式架构有更深的理解。本课程由黑马程序员提供。
相关文章
|
3月前
|
消息中间件 关系型数据库 MySQL
分布式事物-全面详解(学习总结---从入门到深化)
分布式事物-全面详解(学习总结---从入门到深化)
1472 0
|
3月前
|
消息中间件 Dubbo 应用服务中间件
分布式事物【Hmily实现TCC分布式事务、Hmily实现TCC事务、最终一致性分布式事务解决方案】(七)-全面详解(学习总结---从入门到深化)
分布式事物【Hmily实现TCC分布式事务、Hmily实现TCC事务、最终一致性分布式事务解决方案】(七)-全面详解(学习总结---从入门到深化)
86 0
|
1月前
|
Docker 容器 关系型数据库
【PolarDB-X从入门到精通】 第四讲:PolarDB分布式版安装部署(源码编译部署)
本期课程将于4月11日19:00开始直播,内容包括源码编译基础知识和实践操作,课程目标是使学员掌握源码编译部署技能,为未来发展奠定基础,期待大家在课程中取得丰富的学习成果!
【PolarDB-X从入门到精通】 第四讲:PolarDB分布式版安装部署(源码编译部署)
|
3月前
|
Java 数据库连接 API
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
61 0
|
3月前
|
存储 Oracle 关系型数据库
分布式事物【Seata实现、下载启动Seata服务、搭建聚合父工程构建】(四)-全面详解(学习总结---从入门到深化)
分布式事物【Seata实现、下载启动Seata服务、搭建聚合父工程构建】(四)-全面详解(学习总结---从入门到深化)
48 0
|
17天前
|
Dubbo Java 应用服务中间件
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
|
2月前
|
消息中间件 SpringCloudAlibaba Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
788 0
|
3月前
|
NoSQL Java API
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
301 0
|
3月前
|
消息中间件 数据库 RocketMQ
分布式事物【库存微服务业务层实现、实现充值微服务、充值微服务之业务层实现、账户微服务之业务层实现】(九)-全面详解(学习总结---从入门到深化)
分布式事物【库存微服务业务层实现、实现充值微服务、充值微服务之业务层实现、账户微服务之业务层实现】(九)-全面详解(学习总结---从入门到深化)
69 0
|
3月前
|
消息中间件 RocketMQ Docker
分布式事物【RocketMQ事务消息、Docker安装 RocketMQ、实现订单微服务、订单微服务业务层实现】(八)-全面详解(学习总结---从入门到深化)
分布式事物【RocketMQ事务消息、Docker安装 RocketMQ、实现订单微服务、订单微服务业务层实现】(八)-全面详解(学习总结---从入门到深化)
56 0