.NET云原生挑战赛直播课-第十三课【郝冠军-实现云原生时代的软件可观察性 OpenTelemetry】

课程视频与课件浏览链接 https://club.51aspx.com/guruclass/

我今天讲这个内容,是OpenTelemetry是关于遥测的一个内容。背景,就是在云原生架构,实际上是有很多特性的,我们可以从多个纬度来看云原生的架构,我们今天比较关注的是这个可观察性Observability。

那么什么叫可观察性呢?对我们写好一个程序来说,是不是它能运行就可以了呢?实际上不是这样的,只要我们从事开发你就知道,写好的程序有bug,其实是很常见的情况,所以在开发过程中,我们一般可以用加断点,debug方式,看看问题在哪里。

可是如果我们的程序已经部署到生产环境了,都已经不在我们的开发环境了,那这个时候,我们就很难说跑到用户的机器上,我再装一个Visual Studio来打一个断点,这个就不太现实了,那这种情况我们怎么办呢?历史上,我们通常用的工具,是打一个日志,写一个log。一般来说,我们的log,可能是写到一个log文件里,回头,我们就可以来追溯,当时发生了什么,所以log,是我们非常常用的一个技术。

不过log,它还是有一些限制的,比方说最直接的限制,就是它是某个时间点发生了什么,通常,这个log日志会有一个时刻,我们知道那一个时刻,比如说我订单下了某一个时刻,我这个转账已经成功了。但是,很多情况下,我们需要用更多的纬度来观察,比如说我想知道这一段时间发生了什么。那这个时候。虽然我们也可以,把一批的log自己再做个汇总,甚至我们还有一些。分析log的工具。但是,毕竟不是那么方便。

所以在云原生架构环境下,我们提出了这个可观察性的概念,他的意思,就是说我们应该有一整套的手段,来让我们的云原生分布式应用,可以清楚的像水晶一样透明,来知道他到底发生了什么。比如性能降低的时候,比如说他出现问题的时候,我们可以,不用停下来就可以知道问题在哪里,来解决这个问题。

所以我们今天,要解决的就是这个可观察性的问题。那么为什么一定要用可观察性?为什么在云原生时代,我们把它这么重要内容提出来?

那这个页面的背景图呢,是这个Uber的一个示意图,就是对于原生架构来说,我们的应用通常是高度复杂的,他甚至分布在不同的地理区域,分布在非常多的数据中心,那么他们中间可能通过网络互相连接在一起。那么在这种复杂的环境下,如果系统出现问题,我们是很难以去定位问题是在哪里的。因此对于云原生架构来说,可观察性就是我们云原生应用的一个非常重要的一个特性。

那有的同学可能说了,在以前,我们也会有监控的。项目部署了,我也可以知道这个系统是不是有问题,比如,数据库连不上了,是不是网络断了。这是不是也叫这个可观察性呢?那是不一样的。这是两个角度的问题,还是不一样的,比如说对于我们的监控来说。关注监控的,通常是负责运维的基础架构人员,他们更关心的是说网络是不是通,延迟是不是太长,数据库是不是磁盘已经写满了。那这些内容,是从技术架构,技术的角度来说的,它并不能直接对我们应用的业务的状况,提出一个分析的数据来。而Observability,他关注在应用程序的方面,比如说我们可能更关注,微服务之间是否还是畅通的,延时是否太长了,我的一个订单,是从一个服务到下一个服务,是否到达了,中间出过异常没有?

如果出异常了,是一个什么样的异常?那使得我们可以在不停机的情况下,在运行时来获取。

我们应用程序的工作状况,所以Observability呢,并不是跟监控划等号的,他俩个关注点是不一样的,那么下面有一个链接https://club.51aspx.com/10342/,里面有一些详细的分析,我们就不再去讨论它了。

云原生提出了可观测性的三个纬度。其中一个是Logging。Logging就是我们传统的日志,这个大家已经很熟悉了,所以呢,它不是我们今天内容的重点。那中间那个叫Metrics。Metric是一个指标。比如说我想知道我的站点,那么一秒钟能处理多少个请求。我一分钟能完成多少订单这种叫做指标,指标也已经有一些通用的工具来解决这个问题,我们今天关注的呢,是列在这里的第一个,叫做Tracing。那么Tracing是什么?它要解决的是什么问题?Tracing是对我们云原生应用全生命周期的一个可观察性。

我们还是找一个直观一点儿的图来看一下。

对于云原生架构来说,他是有很多个服务来组成的,他又分了很多个节点。每一个服务内,完成一个业务也需要经过很多步操作。

那么我们就想知道。我们的一个业务操作,它经过了哪些处理?每一个处理过程发生了什么?扩展到一个服务之外,我们还想知道。服务A调了其他的哪些服务,什么时间点调用了?花了多长时间?那么它又往下调用了哪些服务?如果我们能获取到这些信息,那么我们就可以在一张层次的结构,画的图上清晰的知道我们微服务之间的协作关系,知道哪些点是我们性能的瓶颈点,我们需要扩展哪些服务。

比如说在这个图里,最上面的这个横条,我们叫做trace。那么一次跟踪的整体,我们叫做一个trace,所以一个trace,是我们的一个追踪的一个整体单位。那么一个trace里面,它的组成的单位叫做span,我们中文经常翻译叫做跨度,比如说这张图为例,上面是调一个messages的API。那这个调用,是我们trace的第一个跨度,所以我们可以把它叫做根的跨度。在这个API的服务上,他只是对外的一个接口,那么它内部为了实现这个message,我们实际上他可能再调用一个认证的服务,来检查一下你有没有权限来获取这些消息。

那这个auth就是它下面的一个跨度说明,在我们这个message这个API内部调用一个auth,我们可以认为有一个auth的API,那么他可能花了一点时间,比如花了100毫秒完成了这个认证过程。认证完了之后,为了提高效率,实际上API是从cache里面去读了一下这个message。

那么这个从开始get,就是第二个跨度,那这个跨度,你会看到它实际上从这个图里面可以看到,它是在认证完成之后才去从cache里面读的,所以从这个图,我们可以清晰的看到,他们之间的时序的关系。那这个长度你会看到比auth要长一点,那么它代表了这个跨度所花费的时间。

那么如果开始没有拿到,我们可能需要从mysql的数据库里做一个物理读取,做一个实际的查询,那我们看到的这个跨度更长,因为这是一个数据库访问了。访问完之后,他可能要在更新到这个cache里面去。

那么,如果我们有一个技术能把整个的操作过程以这样一个图的形式展示出来,随时可以知道我们的系统工作经过了哪些步骤,花的时间长短,出现了次序?注意:这些操作不一定是出现在一台机器上,甚至是涉及到多台分布式环境的多个服务器上。

那对我们的系统的运维来说,就能提供非常有价值的信息,我们就知道优化的点到底在哪里,比如出异常了,某一个服务出问题了,那么我们就可以。直观的在运行时定位到这个服务,那对我们来说就是非常有帮助的。

所以,这个里面就涉及到Tracing的几个概念,Tracing的概念,实际上最早是谷歌,有人写了一篇论文,来提出来这个概念,那后来就有一些系统来实现了这个Tracing概念,这里面最核心的概念,叫做span,就刚才所说的跨度。跨度表示系统中的一个工作单元,一个单个的工作单元,这个单个的工作单元,可能是一个应用程序内部的一个单元,那这样的话,一个程序内部它实际上分成了很多个单元,或者说程序内的一次逻辑操作分成了很多单元。那么这个逻辑单元也可以是一个系统中不同微服务上的工作单元,刚才我讲到了,可能是API上的工作单元,也可能是数据库上的工作单元,也可能是缓存服务器工作单元。这个工作单元,它并不要求是在这个程序内。通常的一个跨度用什么来表示呢?通常他会有个操作的名称,这个名称用来说明这个跨度是做什么的,像我们刚才看到的这个名字。

对于跨度来说,最基本的信息还有一个开始和一个对应结束时间的时间戳,一个开始时间和结束时间的时间戳,由这个时间戳我们就知道。这个跨度发生的具体的时刻和他的时间的长短,这对我们来说是最重要的三个基本信息。然后,他还可以有一个叫Parents span,父跨度的标识符,这个跨度实际上是一个层次化的。所以,有了父跨度,我们就可以把这些跨度构成一颗树,整个Trace,就是一个跨度组成的一棵树。然后还有一些相关的,对于跨度来说,每个跨度都会有一个identifier,有一个唯一的标识符,我们通过标识符来把不同的跨度关联起来,所以像刚才我们说到这个Parents span,这个identifier实际上,每个跨度,可以有一个,父跨度identifier,这样才能联系起来,就构成了一个跨度树。

还有一个叫做上下文context。什么叫上下文?注意,我们每一个跨度是有一个标识符的,但是我们刚刚讲到的一个Trace单位,实际上是有很多个跨度组成的。我怎么知道这些跨度是在一个Trace树里面。所以对他们来说,其实还要有一个叫做Trace ID的标识符。我们的系统会采集大量的Span,但是,这些Span会通过这个Trace ID。有着相同Trace ID的说明,他们是在一个Trace逻辑单位里。再根据这个Parent这个ID。构建成一个Span树,这样就构成我们刚才所看到的这个单位,所以这个Span是我们最核心单位,我们编程中涉及最多的,实际上也是这个Span。

那第二个呢,叫做Trace。实际上是由它所包含的大量的Span所组成的。

那一个Trace,他可能贯穿了很多个不同的服务。这些服务,采集各种各样的Span,他们跨越了服务的边界,最后构建成我们的一个Trace。所以对Trace来说,最基本的它要有一个Trace ID,然后,我们还可以有一些相关的信息跟他结合在一起。

那么,DistributedContext叫分布式的上下文。那么为了实现这个分布式上下文,我们就要解决,如何在不同的服务之间,传播我们的这个Trace的问题。比如说我怎么知道,不同服务采集的这些跨度是属于同一个Trace的呢,所以这个时候,至少我们就需要把这个Trace ID,从这个起点的服务往下一层一层的传下去,那除了这些信息,我们可能还有一些附加的信息,我也想传下去,比如说,我有一个订单号,我想传递下去,比如说,哪个用户是在操作,我也想传下去,等等这些信息。我们都需要,在这个上下文的概念下把它解决掉。所以说Trace里面,就会涉及到这么几个基本的概念,我们反过来说,也就是说我们的技术需要解决这么几个关注点。

所以说从这个概念来说,你会发现Trace,其实挺简单的,就是一堆Span,怎么样构成一个Trace树的一个问题。

那这个问题?在云时代,一开始就已经被提出来了,所以,在历史上,并不是因为OpenTelemetry而关注这个问题。那以前,有两个重要的这个组织就解决了这个问题。第一个叫OpenTracing,那么有很多人可能已经是用过,实际上我们现在很多服务,都已经在用它。那OpenTracing,我们会看到他的统计数字,他有这个370个贡献者,Github上有5000多个星支持,这个大量的开源库,框架100个以上,非常多,比如JS,Java的,各种各样的有很多,包括比较有名的,比如说这个SIPKI那种工具,它实际上是一整套的。

那除了OpenTracing这个组织,实际上,还有OpenCensus这个组织,那他也是希望解决同样的问题,我们会看到,他这个数量少一点,三百九十个贡献者,但是他后台,更硬一点,就看到谷歌、微软,有很多大公司,在这个组织里。

换句话说,就是这个问题,我们会看到其实已经解决过了,那为什么我们又说到今天的这个OpenTelemetry,就是随着这个云原生技术的发展,你会发现的对这个问题有各种不同的解决方案,那这些不同的方案,使用不同的工具,使用不同的协议,有不同的数据描述方式。

那么这就使得我们在集成这些服务的时候,就难以把这些框架统一在一起,比如说我们用了一个这个微服务,本来已经有十个用的OpenTracing技术,那就其中有十个用OpenCensus技术,我现在想办法放一块儿,这个事情就比较麻烦就比较麻烦。所以,这就是为什么我们今天这个OpenTelemetry会诞生的原因。

在这个下面你会看到一行字,这个是我从上面粘过来的,在上面这两个社区的首页,你都会看到下面一句话OpenTracing and OpenCensus have merged form OpenTelemery!他就是说Opentracing和OpenCensus已默置到OpenTelemery框架之下,他们都已经停止更新被归档了。

所以说,对我们来说,好消息就是我们只需要用OpenTelemery一个标准就可以了,当然,你可能说,那我以前的那个怎么办都需要改吗?那个到是不一定,我们接着往下看。那么OpenTelemery跟他有什么不一样的?那么他又提出了一些新的技术名词,这些名词呢,猛一看呢,就比较陌生啊,一看有点儿这个难以理解,其实,非常好理解,这也是来自于OpenTelemery的一张图,我把这张图给大家串一下,你马上就理解他在说什么,其实非常简单。

先看上面OpenTelemery的这个Architecture,他的框架,框架非常简单,你会看到它上面画了三个,Tracing、Metrics、Baggage,那旁边的字写的什么意思呢?他就是说这个OpenTelemery,实际上是有一组彼此独立的工具所组成的,所以,这些工具你用哪一个跟别的,并没有特别直接的关系,你可以独立使用,我们今天关注的就是Tracing这一部分。

那中间的一个横线下面画了两个,这个话就是说,上面这些独立的工具,实际上都用到一些基础概念,这个基础概念就是上下文Context和propagation,propagation叫做传播,传播什么呢?刚才我们说到Trace一个很重要的,你的Trace ID总的要从不同的服务,从一个服务传到一个服务器,那这就叫传播。所以,就是说底层,涉及到上下文和传播上面来实现。

再看中间这个图,中间涂我们的Tracing。那Tracing是怎么工作呢?他提出的词叫signal,你可以叫信号器,或者说叫传感器,这样比较好理解。那么这个signal,它是可以mixed到各种的库里边儿去的,换句话说呢,你可以在不同的地方埋上这些传感器来探测数据。

Telemery叫遥测,那遥测数据从哪儿来呢?就是从这个signal传感器来的。那这个传感器可以在我们的基础框架里,可以在我的应用程序里,可以在客户端的应用里埋各种各样的传感器,这些传感器帮我们采集数据。这些数据贯穿我们应用涉及的各个部分,最终构成了我们这个Tracing。所以这个Tracing,你会看到它是横穿框架应用客户端,多种服务的,这个图就是这个。

下面,就是说那这个signal传感器怎么工作呢?它这里面有个重要的概念,你看到中间有个横线,这个横线中间上面写这个API,下面写这个SDK。现在,我们软件开发已经进入现代软件开发阶段,一个很重要的概念,叫做抽象和实现分离,比如说写这个面向对象程序的时候,会有接口,会有实现基本上可以这么理解。

那这个API,怎么理解呢?这个API就是对OpenTelemery标准的抽象,那么他定义了一套使用OpenTelemery的标准API,那这批API,在不同的语言,不同的框架下保持了一致,所以说你只要理解了我今天讲的概念。无论你是写Java程序,.NET程序,JS程序,都是这个概念,这就是API的概念,它上面的Convention就是一些约定,API和约定都是对这个技术的一个抽象。

横线下面,你可以看到是一个实现,SDK就是最标准的实现。所以说,如果你在某个框架下想使用OpenTelemery的技术,那你肯定要有对应的SDK,它才能真正帮你做这个工作,光有API是不行的,所以这就是这个概念。因此,我们在用它的时候,你会看到这个API,有的时候,写到这个SDK,就是这个概念。

那我们下面再看它怎么工作。那这张图,也来自于OpenTelemery的官网。那么我们以这张图为例,先看左边这一部分,外面有个大的黄框,这是一个Host的一个宿主,你可以认为它是服务器。上面可以跑应用程序应用程序。应用程序里面就有它集成了OTel Library。

OpenTelemery念起来比较长,写起来也比较长,所以在很多地方就看到OTel就是他的简写。所以,就是说我们的应用程序可以集成某一个OTel Library的库,这里面就有了这个传感器signal帮你采集那个数据。采集的数据可以传出来,可以传出来传给OTel Collector收集器,收集器就可以往后发,发给后台,后台服务都可以发,那最终我们可以把它汇总起来。那么这边,还有个黄框就是另一个服务了,可以有很多服务,他们最终汇集到一起。

我们今天是做.NET这个云原生的分享,所以我们就不看其他的应用了,我们就看.NET。.NET下面有一部分,叫OpenTelemery .NET,官网的链接https://opentelemetry.io/docs/net/ 放在这里,它下面有个.NET分支,微软是OpenTelemery这个组织的,一大成员一大贡献者。

那么在他里面,正好对应我们刚才说到的两个概念,一个叫OpenTelemetry .NET API

,这个API,有一大堆长的说明,你就记住这个抽象就行了,也就是说光用它是不行的,它能让你OpenTelemery的概念和语法来写程序,但是,你用它是不能直接完成工作。真正有用的是OpenTelemetry .NET SDK 它是实现。所以说在我们用NuGet包的时候,你要引用这个SDK。

那么OpenTelemery,包括了刚才说三大部分。这三大部分可以看到,现在都已经稳定了,都可以应用,我们今天,关注的是Traces的一部分,所以其他部分不涉及。感兴趣的同学,可以看下面的这个.NET链接https://opentelemetry.io/docs/net/。

我先把概念说完,然后我们再看一下这个网页,然后再看这个演练。实际上,每个新技术,都有很多新名词,学习新概念我觉得一个困难点就是这些新名词一开始,对我来说是一个难点,我认为如果这些名词你都懂了,程序实际上写起来是非常简单的。

那么在.NET里面,或者说在我们OpenTelemery的环境下,我们要做这个Tracing,那么怎么做呢?那就是。用跨度来组成的,叫做死板来组成的。那么因此呢,他实际上在编程中涉及到的最多是跨度一个磁带,磁带刚才我们说到,他有这个名称,有起始时间完成时间。

那除了那些之外,还可以有什么呢,它还可以有一些特性叫Attribute属性,每个跨度可以选属性。属性来做什么呢?可以让我们对这个跨度做更详细的描述。比如,我现在记录的是一个Web API的访问过程,那么访问到了我的Web API起始到它结束我算一个跨度。

那我就想知道,这次API访问,它访问的是我的哪个API,他的这个URL地址是什么,参数是什么,客户端的IP地址是多少,那么这些东西我们都可以用属性的形式附加在这个跨度之上。从技术上讲,一个Attribute就是一组名值对,每一个Attribute需要一个名字,然后它可以有直。

另一个Event,叫做事件。Event跟这个Attribute区别,实际上就是event你可以认为,它可以有一个串做描述,比如说,我可以记下来,正在处理的这个API请求,那有点儿像log,你可以记一段话。

下面这个,稍微特殊一点,这个是Baggage 叫做传播,那传播的特点就是说有一些信息我是希望能够从一个跨度传到下一个跨度,甚至,这个跨度是在不同的微服务上,我希望能传到下一个微服务上去,那这部分就是由这个Baggage来负责传播的,放到Baggage的东西,就是能够传播到进程以外,OpenTelemery定义了一系列的叫BaggagePropagator这样的传播器,来帮助我们来解决这个问题。

那下面就可以正式进入我们这个.NET。在.NET系编程的时候,其实跟那个OpenTelemery稍微有一点点区别。为什么呢?因为微软实际上早就开始做这个事情了,所以,就导致我们现在实际上有两种API,一个就是标准的OpenTelemetry API,另一个,就是.NET Activity API,所以说,底层实现是完全一样的情况下,我们有两种调用接口,都可以完成同样的功能。

那你可能说,.NET的下用哪种合适呢?其实在.NET下,最简单的就是用这个.NET Activity API,Activity对应的是什么呢?对应的就是这个OpenTelemetry里面的Span,把它的对应关系列出来。所以说,我们在.NET下,可以通过创建span来创建activity,就这关系,有的人说,我就挺喜欢OpenTelemetry,我就愿意用这个,不用那个Activity可不可以,可以。他也有一个封装,让用纯OpenTelemetry的接口来操作,不过我觉得意义不大。

那么你要想用它的话,需要通过加一个NuGet包,就叫做System.DiagnosticSource NuGet Package,注意微软在这个诊断工具上有一大堆,注意这个名字叫Source。把他加这个包中间了,现在这个包,支持当年的framework,也支持这个现在的.NET。所以,你要加这个平台的包,然后就可以用了。那还有一些概念对应,这个那边叫Attribute,在微软这边叫Tag,下面两个都一样。

那么下面我们就可以看一段这个程序了,我觉得时间并没有那么多,所以,我就直接,把这个调好的代码,直接来说一下,我就不再去敲一遍。

好,那我就把代码先直接说一下,在.NET的环境下,我们第一个呢,你需要引用这个NuGet包,这个操作过程我就不再去介绍,那么我们需要加这个就叫做OpenTelemetry的这个包,他的版本现在已经是1.3了。

我们可以到网页上去看一下,你输一个OpenTelemetry,在NuGet上面可以看到一大堆的包,那么对我们来说,这个SDK,实际上就是头一个,API是那个接口封装,这是我们这个实现。

所以我们必须要加上这个包,为了加快速度,我觉得时间还是过得挺快的,那么直接把这个包介绍一下,那下面这个叫Exporter是什么呢?就是我们的传感器,记录的数据,它是在进程内的内存内记录的数据,那我怎么拿出来他呢?就要把它导出来,OpenTelemetry支持可以导出到很多地方去,这个官方最简单就是输到控制台。

我们来看一下代码,首先,这个我们using了一下这个Diagnostics,那这个就是微软的命名空间,我们的Activity就在这个命名空间下面,我们创建这个跨度Span,就是用他下面的类,这些,实际上就是基础的引用一下。

那下面有一些名称,serviceName在做什么呢?那刚才说到实际上我们是在云原生环境下,我们这个服务有很多的,那么不同的服务到时候记录下来一堆的跨度,我怎么知道跨度来自于哪个呢?跟踪在哪一个服务上面呢?每个服务上面你可以起一个唯一的名称这样的话,我们看跨度的时候,通过名称就可以知道来自于哪个服务,然后,你的服务现在的版本号serviceVersion,这些都可以在采集之后被记录下来。

实际上使用的时候,分两大步,一步叫做配置,另一步是创建跨度,配置实际上一个应用配置一次就行了,也非常简单,我们来看一下。当我们引入了这个OpenTelemetry库之后,它下面有一个SDK,SDK这是一个助理类,助理类他提供了一套方法,那这里面有一些设计模式,如果你用过这个.NET Core,你很容易理解,他创建了一个CreateTracerProviderBuilder构建器,这个构建器构建好之后,最后调一下这个Builder就构建出来这个TracerProvider。

那么怎么构建呢?这个AddSource就是用来把这个名传进来,这个服务名是有用的,你看AddService(serviceName: serviceName, serviceVersion: serviceVersion)这里面就有服务名、服务版本就可以帮你记下来了。而且这也加了Source,那为什么要加呢?我马上就会说到,下面叫做AddConsoleExporter,这个就是支持启用控制台的输出,采集到信息我们在控制台输出一下,看到最后Build(),这就完事了,一个应用程序启动的时候把这一部分做完就OK了。

怎么创建span呢,创业Span就特别简单,第一个你需要去创建一个叫ActivitySource,注意了,Activity对应的就是我们Span,Source就是源,每一个源需要有一个名称,叫serviceName,这个名称是字符串的名称,这个名称用来做什么呢?就是说我们的这个服务,一个应用里面,可以有很多个ActivitySource,用不同名称来区分。

以后,加到Source里头的他就会被记录,那不加到里头的它就不记录。换句话说,你可以在你的应用里面埋很多的传感器来记录这些信息,但是,我并不需要真的把他们都导出来,那这个名称就可以用来管理导出哪些不导出哪些。那有的同学可能说了,那我要加那么多不影响我的性能吗?不会的,我们接着往下看,有了ActivitySource,我们创建跨度的时候,是通过他的叫StartActivity方法来创建一个跨度的,这就是跨度的说明。

注意,创建一个跨度之后,你可以看到下面,这里面用了.NET的好几种虚拟法。那这儿,你就看到有一个语法activity?.SetTag就是一个问号。问号叫可空,什么意思呢?就是说检查一下他是不是空引用,是空引用后面就不要操作跳过这句话,不是空引用,我们在执行,那为什么要这加问号呢?它的作用就是,在这个微软框架下可以检查一下我们记录的这个信息有没有监听器来收集它,他会不会被记录下来?如果我们的配置里根本就不想把它去记录下来。这个对象就是空,它不会传出来对象,所以也不会有后面的任何操作。

所以,你是不是也明白为什么要有这个问号,所以要解答前面那个问题,就是对服务性能有影响吗?你可以看到几乎没有影响,因为你不采集这个数据的话,他直接就把这些全跳过去。

接着往下来,就是加一个Attribute,在这个.NET的环境下叫做SetTag,每一个,它都是一个名字一个值,注意这个值呢,还可以是其他的。这个程序呢,注意就写完了,那么我们来运行一下它。我们可以看到,下面输出了好多行的内容,就可以来体验一下。

看到输出内容,你就看到了这是一个跟踪单位,它会有一个TraceId ID,你会看到一长串,每一个跨度有一个唯一的标识Span Id,然后它的名称,这个跨度上面写的是什么,他的开始时间持续了多长时间,我们加的这些Tag,一个两个三个这些Tag,那这些是在这个Resource服务里面,我们加的这些,你看到这些就采集下来了。

你可能说那样采集一下也没啥用,控制台这一关啥也没了。实际上我们基本上,就不在这个控制台上来操作它的来,那通常我们在哪操作呢?比如说,我们以前可能用的是这个ZipKin,这个已经被广泛使用了,我在这做实例,这个ZipKin是我们特别常见的一个工具,我们可以RUN QUERY一下就可以查一下采集到的数据,那么这个ZipKin的思路,就是采集到数据,我们会走http协议发到这个ZipKin服务器上来,这个ZipKin让服务器,可以对所有收到的这些跨度进行整合,这已经是一个很广泛使用的工具,除了这个还有其他工具,我们今天就以这个ZipKin为例来说。那下面我们就把它发到ZipKin上,来看它是怎么来的,前面的我说过了,我们也就不再说了。

第二个事例,我们是一个.NET的Web API的服务器,我就直接用创建Web API的模板直接创建出来,所以这个是把.NET这个Web服务器构建出来,这个我们就不用管了。那在这个.NET的环境下,它怎么构建呢?注意,因为.NET是基于依赖注入服务的,所以他有点儿不一样,那么它有一个扩展方法叫做AddOpenTelemetryTracing,我们要加上这么一个这个扩展。

说到这儿,我还要再说一下涉及的包,这个包当然比那个要多一点,多加了一些包,这边你可以看到OpenTelemetry的Hosting包,为了支持AspNetCore的包。

我们在回来看代码,上面和跟刚才是一样的,有两个不一样,就是AddAspNetCoreInstrumentation这个扩展方法,这个扩展方法来自于加的包OpenTelemetry.Instrumentation.AspNetCore你要加上这个包才会有扩展方法。

那么什么叫Instrumentation?我们还是把这个概念说清楚一点,在这个OpenTelemetry下面经常见到这个词,我们把它叫做自动遥测,什么是自动遥测?自动遥测就是你自己不用写就能完成这个测量,就叫自动遥测。

你不用写谁写呢?就是我们会使用一些预定义的遥测库,叫Instrumentation library完成遥测,我们只需要使用一下就行了,比如说刚才这个例子里。我们就用了AddAspNetCoreInstrumentation(),这就叫做遥测库。因为对于微服务来说,通常我们有一些标准的测量要求,比如说API,哪个API?花了多长时间?那这是一个很标准的对跨度进行测量的一个需求。所以,像这些需求我们没必要都自己去写,用这些遥测库就好了,如果我们去NuGet上面看,你看到OpenTelemetry下面,有一堆Instrumentation相关的,有各种各样的,很多人都写好了,你可以直接拿来用,就不用自己写了,这是它的好处。

好,接着回到这,所以,我们不用像刚才控制台去创建这个Activity,就可以来实现遥测。那下面那个叫AddZipkinExporter,刚才例子里面,我们看的是控制台的叫Console,除了Console,我们有各种各样的常见已有的工具,我们都有对应的Exporter,你直接添加对应的NuGet包,直接使用就可以了,比如说在这里我就想用Zipkin,你还可以做详细配置,因为我用的都是标准端口,所以我这儿就不再去配置,想要配置,看这个官网就可以。

有同学说那个官网在哪呢?官网,你可以到这OpenTelemetry的官网https://opentelemetry.io/docs/instrumentation/ 我们看到有各种语言的instrumentation 我们要看的就是.NET这,你只要打开.NET,就可以看到详细的步骤,增加哪个包,代码怎么写,直接粘贴过来,直接RUN 就可以了,我们现在说的就是自动测量,需要引用了instrumentation别人写好的这个库。

其实下面这段的在标准模板也没有,他自己加了也为了方便一点,你看它增加了一个端点,这个叫做hello的端点。

我们刚才已经加了这么一个AddAspNetCoreInstrumentation,那这样的话,只要访问这个API,就可以测量下来了,这里面有一些默认的输出,那我们下面,就可以来看一眼,

切换到这个窗口,他还在RUN,我们直接访问地址,我直接点一下。

你看我点一下,这时候实际上就在发这个hello的API请求,我已经点了好几下。而且呢,我们启用了这个Zipkin。

然后我们到这儿来看一下,我去查询一下,你会看到下面是不是就已经出来了好几条,这就是我们记录下来的一个一个的Trace。

我们就可以看到一条一条,这一条就是一条Trace,可以去看这一条Trace里面,它有什么,你可以看到把它展开之后,有个Trace ID,下面这儿可以点的,这个我们就可以去看一下这个trip。

点完之后我点一下这个SHOW,在这就可以看到层次化的结构,我现在只有一个跨度,所以这只有一条。

你看这就是我写的这个名称,这个时间长度花了200毫秒,起点、终点,旁边有详细的说明,你看这些Tag,这是这个库自动帮我加的,他帮我加了我这个服务器的端口,什么类型的请求,成功了没有,访问的URL客户端是什么,这些就已经都帮我记录下来了,而且这里面你会看到这就是整体的Trace ID,这就是一个Span ID,跨度ID。

它就是头,所以它的Parent,是一个None,那还有一些相关信息,你把它展开,你可以看到这个开始时间、结束时间,这些都可以直接看到。

当然你可以说只有一个跨度对不对,只有一个跨度,那我们接着看一下,实际上他这个例子里边,可以有两个的,所以你看这,我可以再加上一个包,注意,这个是测量什么呢?测量http client这个跨度的,我们来用一下。

在我这个项目里,再添加一OpenTelemetry.Instrumentation.Http。这时我们包里就增加了一条。我们在代码中加AddHttpClientInstrumentation()我加了一行,那这个他的名字实际上你看到什么是针对http client来做遥测的,那他在哪儿呢?注意这个hello的API里面呢,用了一下HD client这个库来访问了一个外部的服务。

那这个时候,我们会构建一下,那他就已经RUN起来了,RUN起来了回去看看还在原来端口上,然后我们切换过来看一下。

再访一下你会发现稍微慢了一点,我点了三下,点三下回到这边来,回到Zipkin查找这,然后RUN一下查询,你会看到现在,是不是有了三个Trace。

现在我们点一个进去看一下,你会发现这个span,数量变成两个了,而且这个名称也不一样,上面是我们这个API的,下面是API里面去访问了一个外部服务,你看还有两个Span,而且你会看到他们这个持续的关系,上面比较长,里面这个比较短,因为实际上主要是靠里面去访问拿数据的,你也可以分别去点每一个对应的Span来看它对应的这个信息。

这个时候呢,我们就可以看到的这个深度,有两个Span,它是有这个上下级关系的,那这个filekey,会帮我们把他们根据这个跨度的Span ID把他们编起来,那Trace ID负责把相同的一个Trace,把它们串起来。

那这个例子呢,我们就可以看到,它在.NET环境下使用的是非常简单的,大多数情况你可能只需要引用一下这个库,你都不需要写代码就可以了,你只要找一下对应的遥测库就行了。

你如果在这个NuGet上看一眼,你就可以看到这些都是现成的,找到对应的使用就可以了。

那有的时候呢,那这些遥测库还满足不了我们的需要,那我自己想加一些这个测量点怎么办呢?这个就叫做手动遥测,实际上刚才在第一个例子控制台里面已经看过了,那么怎么样手动遥测呢?

第一步,你要创建一个叫ActivitySource每个ActivitySource可以有一个唯一的名称来区分,然后第二步,我得回到前面一下,第二步要做什么呢?注意也非常重要,就是AddSource(serviceName),如果不AddSource就自动忽略掉了,所以这个名称serviceName,要跟他对应起来,创建遥测数据StartActivity,.NET现在提供新语法using,你在一个方法用using语法创建这个对象,在这个方法结束的时候会自动的被回收,所以用这个语法,你看到他只创建,为什么他不去结束一下呢?我什么时候算结束了呢?是因为在.NET的环境下,我们一般用这个。那个父子关系,我怎么来处理掉呢,.NET下非常非常的简单,最简单的我觉得就是.NET,你看这是一个父的activity。我现在父的Activity,我可以设置一堆属性,那我要创建子了怎么办?只要在这个父的activity还没有结束之前,注意是同一个进程里,用同一个ActivitySource创建了一个新的Activity,那就是它的子,他就自动关联到上面的这个Activity的下面去了,所以你根本就不用管那个Span ID 、Trace ID的事,全自动完成,特别方便。

那下面又重新回到父的Activity下面了,你还可以再创建其他的,这样就直接构成这个父子关系。如果你写过之后,你会发现.NET实在是太方便,微软把我们常用操作尽可能的简化,你看那个using语法,这个空操作这种,就让我们这个程序写的非常简单,而且非常的高效,非常好理解。

OK,这是手动角色,刚才这个例子说的是同一个服务内部的,这种上下级关系。那要是跨服务了,我怎么样给他传过去呢?微软的有一个叫Dapr,Dapr里面用到了这个分布式的Tracing,这里面有一个部分叫W3C trace context。实际上微软现在就直接,基于这个标准来做的刚才的activity之类的内容,W3C的context怎么来做的呢?我们可以去看一下,W3C定义了一组标准的header,HTTP请求的header,这个还得来做这个上下文传播这件事,怎么传播呢?他定了一个标准的头,叫做traceparent,注意这个是标准名称,它后面跟一串值,这个就是我们想传播出去的跟踪上下文。

这一串值,前面这两个是版本,我们现在只有这个00,第二个,实际上就是我们的那个Trace ID,这个ID如果你看一下.NET里边生成的Trace ID,实际上就是这么一个形式的。第三部分,就是这个跨度Span ID,所以你会看到这个传播的时候,最重要的两个内容就是,Trace ID、Span ID,那传播到下一个服务,这个Span ID就变成下一个服务里面跨度的父Span ID。后面这个01是标志位,在这标志着什么意思呢?它标志就是叫采样,采样就是说我程序内部的测量这些数据,这些数据需要记录下来吗?是不是需要都记录下来呢?或者我只需要记录10%就行了呢?等等这个叫采样率,01就是把它记录下来的意思。

.NET环境下,如果你访问一下这个activity的ID,你会看到其实就直接得到这么一串,因为.NET现在用的就是W3C的这个标准,这个叫traceparent,还有一个叫tracestate,你可以传递一些自定义的一些vendor特定的这个数据,这个我们就不介绍了,所以如果要往上下传,就是使用刚才的那些标准,但是我觉得你大多数情况用不着,为什么呢?我们再回到上一个来叫遥测库,注意这些遥测库,基本上就帮你解决了这个传播的这些问题,比如说AspNetCore他就可以自动检测一下。

今天就介绍了,这个遥测的概念,然后遥测的几个基本概念,这些概念其实是最核心的,那介绍完之后,我们还介绍了.NET环境下怎么样使用,可以看到特别的简单,就是有一点,就是.NET环境下实际上有两种 .NET Activity API和OpenTelemetry API也可以用,但activity这个其实非常简单,而且也是针对这个标准的这个对应实现,所以我们推荐它。

再看一下这个Exporters刚才是采集到数据,我怎么拿到这个数据呢?这叫导出器,那么NuGet库里面,你直接一搜就能找到这些导出器,你看到这个我们刚才用的是这个Zipkin那种,然后其他一大堆都有。换句话说OpenTelemetry标准化了,对这个Trace这个数据的格式标准,这个标准格式不意味着,把以前我们现有的那些工具和框架给丢弃掉了,我们可以把这个标准的我们OpenTelemetry的数据转换成、适配成以前的数据格式,然后使得我们以前那些工具,可以继续使用,比如说像刚才我们看到Zipkin这个Exporter就可以无缝的让我们使用。

传播我就不用说了,就刚刚已经介绍这个概念,.NET的环境下,传播这个问题做的已经非常简单了,刚才讲了这个activity的这个ID就已经自动生成W3C的标准,非常的方便,希望今天的内容,能够帮助你的这个快速上手,OpenTelemetry为你的微服务环境提供帮助。

给TA打赏
共{{data.count}}人
人已打赏
.NET

可观察性与监控:有什么区别

2022-6-16 17:02:09

.NET

比较 EF Core & EF6

2022-6-22 10:08:58

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索