.NET Core分布式链路追踪框架的基本实现原理

2022-04-18 10:05:27
目录
分布式追踪什么是分布式追踪分布式系统分布式追踪分布式追踪有什么用呢Dapper分布式追踪系统的实现跟踪树和 spanJaeger 和 OpenTracingOpenTracingJaeger 结构OpenTracing 不给出了,明白表示时间就行。

在 C# 中,将当前时间转为这种时间戳的代码:

        public static long ToTimestamp(DateTime dateTime)        {            DateTime dt1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0);            return (dateTime.Ticks - dt1970.Ticks)/10;        }// 结果:1611467737781059

如果我们直接使用 Guid 生成或者 string 存储,都会消耗一些性能和内存,而使用 long,刚刚好可以表示时间戳,还可以节约内存。

获得这个时间戳后,要传输到 Jaeger Collector,要转为 byet 数据,为什么要这样不太清楚,按照要求传输就是了。

将 long 转为一个 byte 数组:

            var bytes = BitConverter.GetBytes(time);// 大小端            if (BitConverter.IsLittleEndian)            {                Array.Reverse(bytes);            }

long 占 8 个字节,每个 byte 值如下:

0x00 0x05 0xb9 0x9f 0x12 0x13 0xd3 0x43

然后传输到 Jaeger Collector 中,那么获得的是一串二进制,怎么表示为字符串的 trace id?

可以先还原成 long,然后将 long 输出为 16 进制的字符串:

转为字符串(这是C#):

Console.WriteLine(time.ToString("x016"));

结果:

0005b99f1213d343

Span id 也是这样转的,每个 id 因为与时间戳相关,所以在时间上是唯一的,生成的字符串也是唯一的。

这就是 trace 中的 trace id 了,而 trace process 是发起请求的机器的信息,用 Key-Value 的形式存储信息,其格式如下:

                        {                            "key": "hostname",                            "type": "string",                            "value": "Your-PC"                        },                        {                            "key": "ip",                            "type": "string",                            "value": "172.6.6.6"                        },                        {                            "key": "jaeger.version",                            "type": "string",                            "value": "CSharp-0.4.2.0"                        }

Ttace 中的 trace id 和 process 这里说完了,接下来说 trace 的 span。

Span

Span 由以下信息组成:

An operation name:操作名称,必有;A start timestamp:开始时间戳,必有;A finish timestamp:结束时间戳,必有;Span Tags.:Key-Value 形式表示请求的标签,可选;Span Logs:Key-Value 形式表示,记录简单的、结构化的日志,必须是字符串类型,可选;SpanContext :跨度上下文,在不同的 span 中传递,建立www.easck.com关系;Referencest:引用的其它 Span;

span 之间如果是父子关系,则可以使用 SpanContext 绑定这种关系。父子关系有 ChildOfFollowsFrom 两种表示,ChildOf 表示 父 Span 在一定程度上依赖子 Span,而 FollowsFrom 表示父 Span 完全不依赖其子Span 的结果。

一个 Span 的简化信息如下(不用理会字段名称大小写):

{                    "traceID": "790e003e22209ca4",                    "spanID": "4b73f8e8e77fe9dc",                    "flags": 1,                    "operationName": "print-hello",                    "references": [],                    "startTime": 1611318628515966,                    "duration": 259,                    "tags": [                        {                            "key": "internal.span.format",                            "type": "string",                            "value": "proto"                        }                    ],                    "logs": [                        {                            "timestamp": 1611318628516206,                            "fields": [                                {                                    "key": "event",                                    "type": "string",                                    "value": "WriteLine"                                }                            ]                        }                    ]}

OpenTracing API

在 OpenTracing API 中,有三个主要对象:

TracerSpanSpanContext

Tracer可以创建Spans并了解如何跨流程边界对它们的元数据进行Inject(序列化)和Extract(反序列化)。它具有以下功能:

开始一个新的 SpanInject一个SpanContext到一个载体Extract一个SpanContext从载体

由起点进程创建一个 Tracer,然后启动进程发起请求,每个动作产生一个 Span,如果有父子关系,Tracer 可以将它们关联起来。当请求完成后, Tracer 将跟踪信息推送到 Jaeger-Collector中。

 

.NETCore分布式链路追踪框架的基本实现原理

SpanContext 是在不同的 Span 中传递信息的,SpanContext 包含了简单的 Trace id、Span id 等信息。

我们继续以下图作为示例讲解。

A 创建一个 Tracer,然后创建一个 Span,代表自己 (A),再创建两个 Span,分别代表 B、C,然后通过 SpanContext 传递一些信息到 B、C;B 和 C 收到 A 的消息后,也创建一个 Tracer ,用来 Tracer.extract(...) ;其中 B 没有后续,可以直接返回结果;而 C 的 Tracer 继续创建两个 Span,往 D、E 传递 SpanContext。

.NETCore分布式链路追踪框架的基本实现原理

这个过程比较复杂,笔者讲不好,建议读者参与 OpenTracing 的官方文档。

详细的 OpenTracing API,可以通过编程语言编写相应服务时,去学习各种 API 的使用。

到此这篇关于.NET Core分布式链路追踪框架的基本实现原理的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。