SkyWalking快速入门
参考课程:2023深入理解SkyWalking微服务链路追踪实战_哔哩哔哩_bilibili
SkyWalking 主要用于分布式系统的性能监控和故障排查,它通过收集和分析各个服务之间的调用关系、性能指标等数据,帮助开发者和运维人员快速了解系统的运行状态,定位性能瓶颈和故障。常用于用户指标分析,性能指标分析,告警功能,服务依赖关系可视化
SkyWalking相关概念
基本了解
分布式调用链标准-openTracing
Span-节点
跟踪树结构
不同系统之间RPC,HTTP,MQ之间的调用是有区别的,那我们应该如何区分这些数据呢?
有一些特定的变量,SpanName SpanId traceId spanParentId
植入点:我们监控的地方,我们要尽可能零侵入进行代码采集
采样率:采集我们的信息,为了知道哪里出现了问题从而尽可能提升性能
Opentracing标准详解
我们要根据规范来设计我们的Tag的key和value
一个调用链是由多个Span组成的,span和span之间的关系是reference(调用)
标准调用结构图
分为父子关系和兄弟关系
父子关系是A和B,C
兄弟关系是F,G,H,我们按照顺序来进行调用,先F然后G然后H
Span节点必须包含的东西
OperationName:操作名称
BeiginTime:开始时间
EndTime:结束时间
SpanTag:是一组键值对构成的Span的标签集合(key必须是String类型,value可以是String,Boolean和数字类型),这个的目的是为Span添加更多的描述信息
SpanLog:一组Span的日志集合,是键值对,记录日志信息
SpanContext:是一个上下文对象,会从上一个节点传递到下一个节点,里面包含了traceId,SpanId,Baggage(这是一个跨Span集合,上一个节点往Baggage加信息下一个节点可以拿到,不要放太多信息不然会导致占用空间大影响效率),我们通过上下文对象去进行跨进程传递
OpenTacingApi
opentracing-specification-zh/specification.md at master · opentracing-contrib/opentracing-specification-zh · GitHub
一些基本API的使用
Tracer接口
Tracer接口用来创造Span,以及如何处理Inject和Extract,用于跨进行边界传递
创建一个新的Span
必填参数:OperationName,操作名
可选参数:
- 零个或者多个关联(references)的
SpanContext
,如果可能,同时快速指定关系类型,ChildOf
还是FollowsFrom
。 - 一个可选的显性传递的开始时间BeginTime;如果忽略,当前时间被用作开始时间。
- 零个或者多个tag。
返回值,返回一个已经启动Span
实例(已启动,但未结束。译者注:英语上started和finished理解容易混淆)
将SpanContext
上下文Inject(注入)到carrier
必填参数
SpanContext
实例- format(格式化)描述,一般会是一个字符串常量,但不做强制要求。通过此描述,通知
Tracer
实现,如何对SpanContext
进行编码放入到carrier中。 - carrier,根据format确定。
Tracer
实现根据format声明的格式,将SpanContext
序列化到carrier对象中
将SpanContext
上下文从carrier中Extract(提取)
必填参数
- format(格式化)描述,一般会是一个字符串常量,但不做强制要求。通过此描述,通知
Tracer
实现,如何从carrier中解码SpanContext
。 - carrier,根据format确定。
Tracer
实现根据format声明的格式,从carrier中解码SpanContext
。
返回值,返回一个SpanContext
实例,可以使用这个SpanContext
实例,通过Tracer
创建新的Span
注意,对于Inject(注入)和Extract(提取),format是必须的
Inject(注入)和Extract(提取)依赖于可扩展的format参数。format参数规定了另一个参数"carrier"的类型,同时约束了"carrier"中SpanContext
是如何编码的。所有的Tracer实现,都必须支持下面的format。
- Text Map: 基于字符串:字符串的map,对于key和value不约束字符集。
- HTTP Headers: 适合作为HTTP头信息的,基于字符串:字符串的map。(RFC 7230.在工程实践中,如何处理HTTP头具有多样性,强烈建议tracer的使用者谨慎使用HTTP头的键值空间和转义符)
- Binary: 一个简单的二进制大对象,记录
SpanContext
的信息
Span
当Span
结束后(span.finish()
),除了通过Span
获取SpanContext
外,下列其他所有方法都不允许被调用
获取Span
的SpanContext
不需要任何参数。
返回值,Span
构建时传入的SpanContext
。这个返回值在Span
结束后(span.finish()
),依然可以使用
复写操作名(operation name)
必填参数
- 新的操作名operation name,覆盖构建
Span
时,传入的操作名。
结束Span
可选参数
- 一个明确的完成时间;如果省略此参数,使用当前时间作为完成时间。
为Span
设置tag
必填参数
- tag key,必须是string类型
- tag value,类型为字符串,布尔或者数字
注意,OpenTracing标准包含**"standard tags,标准Tag"**,此文档中定义了Tag的标准含义。
Log结构化数据
必填参数
- 一个或者多个键值对,其中键必须是字符串类型,值可以是任意类型。某些OpenTracing实现,可能支持更多的log值类型。
可选参数
- 一个明确的时间戳。如果指定时间戳,那么它必须在span的开始和结束时间之内。
注意,OpenTracing标准包含**"standard log keys,标准log的键"**,此文档中定义了这些键的标准含义
设置一个baggage(随行数据)元素
Baggage元素是一个键值对集合,将这些值设置给给定的Span
,Span
的SpanContext
,以及所有和此Span
有直接或者间接关系的本地Span
。 也就是说,baggage元素随trace一起保持在带内传递。(译者注:带内传递,在这里指,随应用程序调用过程一起传递)
Baggage元素具有强大的功能,使得OpenTracing能够实现全栈集成(例如:任意的应用程序数据,可以在移动端创建它,显然的,它会一直传递了系统最底层的存储系统),同时他也会产生巨大的开销,请小心使用此特性。
再次强调,请谨慎使用此特性。每一个键值都会被拷贝到每一个本地和远程的下级相关的span中,因此,总体上,他会有明显的网络和CPU开销。
必填参数
- baggage key, 字符串类型
- baggage value, 字符串类型
获取一个baggage元素
必填参数
- baggage key, 字符串类型
返回值,相应的baggage value,或者可以标识元素值不存在的返回值(译者注:如Null)
SpanContext
相对于OpenTracing中其他的功能,SpanContext
更多的是一个“概念”。也就是说,OpenTracing实现中,需要重点考虑,并提供一套自己的API。 OpenTracing的使用者仅仅需要,在创建span、向传输协议Inject(注入)和从传输协议中Extract(提取)时,使用SpanContext
和references,
OpenTracing要求,SpanContext
是不可变的,目的是防止由于Span
的结束和相互关系,造成的复杂生命周期问题。
遍历所有的baggage元素
遍历模型依赖于语言,实现方式可能不一致。在语义上,要求调用者可以通过给定的SpanContext
实例,高效的遍历所有的baggage元素
NoopTracer
所有的OpenTracing API实现,必须提供某种方式的NoopTracer
实现。NoopTracer
可以被用作控制或者测试时,进行无害的inject注入(等等)。例如,在 OpenTracing-Java实现中,NoopTracer
在他自己的模块中。
可选 API 元素
有些语言的OpenTracing实现,为了在串行处理中,传递活跃的Span
或SpanContext
,提供了一些工具类。例如,opentracing-go
中,通过context.Context
机制,可以设置和获取活跃的Span
原理和架构设计
原理和架构设计
1.自动采集&无侵入:我们使用插件化方式 JavaAegnt启动
2.跨进程传递Context:针对不同协议将信息加入到协议头里面,方便进行区分
3.TraceId的唯一性
4.性能影响:我们不能所有数据都采集这样子数据量太大了还影响效率,我们只采样必要数据
Agent和Plugin插件
Agent:我们项目启动时自动加载我们的插件进行字节码增强,我们的Agent里面由很多插件,分为自带插件和可选插件。我们要启动Agent的话就在JVM参数那指定一下就行了
Plugin插件:创建Span加入Trace调用链,数据传输时我们将数据加入到Header协议头。我们的Skywalking里面把很多的第三方调用组件封装起来了,所以够用
TraceSegment和TraceId原理
TraceSegment:
1.指的是一个进程中所有的Span的集合
2.如果多个线程协同产生同一个Trace(例如多个RPC调用不同的方法),它们只会共同创建一个TraceSegment
3.支持多入口,所以Skywalking去除了树节点RootSpan的概念,提出了三种Span模型
TraceId:
1.TraceId应该是全局唯一的
2.我们的TraceId是根据时间错+算法生成的,所以会有时间回拨问题
3.我们有个变量lastTimeStamp保存上次TraceId生成的时间,然后在生成TraceId前进行比较,如果CuurentTimeMills比lastTimeStamp时间小,说明时间回拨了,我们就不生成Id,这样来保证TraceId全局唯一
JavaAgent可以对字节码做静态修改和动态修改,我们的SkyWalklng是对字节码静态修改的
SkyWalking基本使用
图片介绍
服务和服务之间也可能存在各种调用
skywalking是以插件的形式实现相关东西
他能实现很多应用的无侵入式的监控
关键概念:
服务
服务实例
端点
这个就是服务,然后我们有9个服务实例
这个用户的URL请求地址我们称作端点
搭建OAP服务和UI服务
Downloads | Apache SkyWalking
去到download界面
Skywalking APM已经集成了 BoosterUI和Skywalking Website
下载Agents,下载我们自己需要进行代理的一个探针
这种探针才是用来监控我们自己的应用的,我们上面的那个是服务
skywalking是Java写的
三种启动命令
startup.sh
oapServic.sh
webappService.sh
tar -zxvf 开始解压
我们进入apache-skywalking-apm-bin的bin目录
oapService是直接启动
oapServiceInit是初始化启动
oapServiceNoInit是不初始化启动
我们启动
sh startup.sh
这样子就说明我们两个都启动成功了
我们还可以进入日志目录,看看我们是否有日志
opa.log是我们的服务端的日志
console.log是我们的web界面的一个日志
其实有4个日志,如果是两个日志就说明Java环境有问题
查看我们的日志
tail -f -n200 skywalking-oap-server.log
我们有内存型的数据库,这种数据库安全性不是太高
所以我们打算把日志存到我们的ES里面
我们看看我的的另一个日志skywalking-webapp
tail -f -n200 skywalking-webapp.log
启动失败了,因为我服务器里已经有8080的项目在用了
我们启动服务我们没有给它指定端口
我们看webapp目录里面其实它是一个SpringBoot项目
它的默认端口是8080
vim application.yml
然后我们把我们的端口改成18080
我们重新启动一下我们的webappService
sh webappService.sh
进入日志目录,重新看一下我们的日志
tail -f -n200 skywalking-webapp.log
成功了,我们的端口是18080
然后我们访问一下我们的18080端口
就进入了我们的skywalking界面
结构解析与设计目标
receiver Cluster
receiver Cluster是负责收集数据的
我们从每一个不同的监控服务中去获取相关信息,通过推送或者拉取去获取一些相关的数据,包括通过http,rpc等等各种不同的形式去收集
Aggregator集群
然后我们把数据给我们的Aggregator集群
它可以做我们的数据的保存
可以把我们的数据持久化存储到各个不同的服务器里面
Alram告警服务
还有一块就是我们的Alarm,我们的监控告警服务
JavaAgent监控SpringBoot应用
我们的Agent探针是为了收集我们的对应服务的数据,然后上传到我们的skywalking服务器上去
我们把文件解压成skywalking-agent目录解压到我们的IDEA的工作目录中
这个是我们的JVM参数
-javaagent:探针java所在的位置
-DSW_AGENT_NAME=skywalking-demo
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=106.55.55.56:11800
-D表示我们的JVM参数
SW_AGENT_NAME是我们的代理的名字
SW_AGENT_COLLECTOR_BACKEND_SERVICES是我们的服务上传到什么地方去
之前我们的服务HTTP的端口是12800,grpc的端口是11800
我们重新启动我们的SpringBoot项目,发现上面打印了一堆日志
这就是帮我们找到我们的skywalking插件
然后我们的服务就出来了
ES持久化存储
我们的Skywalking默认是在内存型数据库h2存储的,我们这里有数据只是因为我们的skywalking没有重启过
我们重启后这些数据就没有了
我们进入application.yml文件
我们有个storage
我们有个selector,下面有个es相关配置
你看我们默认选中的是h2
我们下面有h2的配置信息,有mysql的配置信息
因为我们只是用来持久化存储,所以我们不用关心太多的配置信息
我们只用关心
clusterNodes:我们的ES的连接地址
user:用户名
password:密码
看看我们是否配置了用户名和密码
我们默认是不支持mysql的,我们要把mysql的驱动包复制到这个目录下来,然后我们才能成功连接
SpringCloud全链路追踪
我不只希望他记录我们的微服务之间的信息
我们还想他记录更多的信息
这个服务访问了数据库,访问了redis,访问了mq等等信息我都给他体现
这里没有网关
skywalking默认我们使用这个SpringCloud的这个情况下,它是没有那种直接支持我们的网关的那种能力的
我们目录里面有已经支持的插件plugins和可选支持的插件optional-plugins
这个可选的插件其实没有把我们的网关的插件引进去
我们搜gateway版本
然后把jar包弄进去
我们的SpringCloud是依赖于webfulx,所以我们要把webfulx引入进去
把这两个jar包粘贴到我们的plugins里面
看看我们的Skywalking界面
我们这会有数据库,缓存,和mq的数据
日志采集GRPC导出logback日志
我们要把我们的日志弄到我们的ES里面去
但是我们单纯的存储这些日志的话,这些日志和我们的链路追踪是没有关系的
看不出我们的日志到底是属于哪条链路的
所以我们希望我们对我们的日志格式做一个定制化
我们有个链路追踪ID,我们希望他能够在这里体现出来
我们要引入依赖
然后我们加上我们的logback相关配置
我们重新启动项目,这时候变成了TID:N/A,这是因为我们还没开始链路追踪
我们不是请求发过来,我们是不会帮我们追踪的
利用grpc把日志信息输出到我们的oap服务
我们是info级别的我们用grpc-log来追加进去
或者我们可以只把警告和错误日志追踪上去
Webhook实现告警通知
通过修改alram-setting.yml文件的配置规则
告警规则
我们弄一下我们的alarm-settings.yml文件
我们触发报警后,我们就会自动调用我们的webhooks里面的这个请求地址
这里有告警信息
然后我们配置的url那个地方我们会把报警通知发过去
教程是写了个Controller来进行输出和接收
你看我们的接收类型是List<AlramMessage>
告警通知消息通知到钉钉机器人
记得我们要找对应版本的官方文档
看看官方文档,这个alram警告通知支持微信,钉钉,飞书通知
我们弄个钉钉机器人
url
secret密钥
我们把配置粘贴进来
这样我们告警信息就能进到钉钉了
自定义追踪-细粒追踪Service方法
我们之前追踪的是各个服务之间也就是Controller和Controller之间
我们想细致化到Service和Service这种具体方法之间的调用
引入依赖
这样我们就能访问skywalking中给我们暴露的API了
TraceContext方法
我们通过TraceContext的方法来往追踪上下文对象中绑定key/value
可以从从上下文获取数据
Trace注解
我们用个Trace注解来表示当前的这个方法是我们要用来追踪的
tag注解
我们可以通过tag注解来放我们的key和value
如果value是returnedObj,说明这个value是这个方法的返回对象
tags注解
里面有多tag