序言
对于个大型的几十个、 几百个微服务构成的微服务架构系统, 通常会遇到下面一些问题,比如:
- 如何捋清各个微服务间的依赖关系?
- 如何跟踪整个业务流程的调用处理顺序?
- 如何进行各个微服务接口的性能分折?
- 如何串联整个调用链路日志,快速定位问题?
要处理这些问题,就需要分布式链路追踪框架出手了,本文要谈的 Skywalking 就是一款分布式链路追踪框架。
简介
Skywalking 是一款分布式系统的应用程序性能监视工具, 专为微服务、云原生架构和基于容器(Docker、K8s、Mesos) 架构而设计。它是一款优秀的 APM (Application Performance Management) 工具,包括了分布式追踪、性能指标分析、应用和服务依赖分析等。
为什么选择 Skywalking?
目前,市面上的链路追踪框架大概有以下几种:
- Zipkin:Twitter 开源的调用链分析工具,目前基于 SpringCloud sleuth 得到了广泛的使用,特点是轻量,使用部署简单
- Pinpoint:韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI 功能强大,接入端无代码侵入
- SkyWalking:国产开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI 功能较强,接入端无代码侵入
- CAT:大众点评开源的基于编码和配置的调用链分析,应用监控分析,日志采集,监控报警等一系列的监控平台工具
功能对比
支持功能\种类 | Cat | Zipkin | Skywalking |
---|---|---|---|
调用链可视化 | ✅ | ✅ | ✅ |
聚合报表 | 非常丰富 | 少 | 较丰富 |
服务依赖图 | 简单 | 简单 | 好 |
埋点方式 | 侵入式 | 侵入式 | 非侵入字节码增强 |
VM监控指标 | ✅ | ❌ | ✅ |
支持语言 | Java/.Net | 丰富 | Java/.Net/Nodejs/php/Go |
存储机制 | MySQL(报表)、本地文件/HDFS | 内存、ES. MySQL 等 | H2、ES |
社区支持 | 主要在国内 | 国外主流 | Apache 支持 |
使用案例 | 美团、携程 | 京东、阿里定制后不开源 | 华为、小米、当当、 微众银行 |
APM | ✅ | ❌ | ✅ |
开发基础 | eBay cal | Google Dapper | Google Dapper |
是否支持 web flux | ❌ | ✅ | ✅ |
性能对比
通过 Jmeter 测试,各指标配置:
- 采样率为 1,即 100%
- 分别模拟并发用户数:500,750,1000
- 每个线程发送 30 个请求,设置思考时间为 10ms
- pinpoint默认的采样率为20,即 50%
- 通过设置agent的配置文件改为 100%。 zipkin 默认也是1
组合起来,一共有 12 种,测试得到结果汇总表:
APM | 采样率 | 线程数 | 请求总数 | 平均请求时间 ms | 最小请求时间ms | 最大请求时间ms | 90%Line | 错误率% | CPU | memory | Throughput /sec |
---|---|---|---|---|---|---|---|---|---|---|---|
none | 1 | 500 | 15000 | 17 | 9 | 824 | 21 | 0 | 45% | 50% | 1385 |
Zipkin | 1 | 500 | 15000 | 117 | 10 | 2101 | 263 | 0 | 56% | 55% | 990 |
Skywallking | 1 | 500 | 15000 | 22 | 10 | 1026 | 23 | 0 | 50% | 52% | 1228 |
Pinpoint | 1 | 500 | 15000 | 201 | 10 | 7236 | 746 | 0 | 48% | 52% | 774 |
none | 1 | 750 | 22500 | 321 | 10 | 15107 | 991 | 0 | 56% | 48% | 956 |
Zipkin | 1 | 750 | 22500 | 489 | 10 | 27614 | 1169 | 0 | 63% | 55% | 582 |
Skywalking | 1 | 750 | 22500 | 396 | 10 | 16478 | 941 | 0 | 55% | 50% | 908 |
Pinpoint | 1 | 750 | 22500 | 681 | 10 | 28138 | 1919 | 0 | 56% | 48% | 559 |
none | 1 | 1000 | 30000 | 704 | 10 | 39772 | 1621 | 0 | 59% | 53% | 557 |
Zipkin | 1 | 1000 | 30000 | 1021 | 10 | 36836 | 1978 | 0 | 63% | 55% | 533 |
Skywalking | 1 | 1000 | 30000 | 824 | 10 | 25983 | 1758 | 0 | 62% | 55% | 667 |
Pinpoint | 1 | 1000 | 30000 | 1148 | 10 | 40971 | 2648 | 0 | 60% | 52% | 514 |
从上表可以看出,在三种链路监控组件中,Skywalking 的探针对吞吐量的影响最小, Zipkin 的吞吐量居中,Pinpoint 的探针对吞吐量的影响较为明显, 在 500 并发用户时,测试服务 的吞吐量从 1385 降低到 774,影响很大。
另外,观测 CPU 和 memory 的影响, 可看到压测时对 CPU 和 memory 的影响都差不多在 10% 之内。
功能特性
- 支持告警
- 优秀的可视化解决方案
- 轻量高效,无需大数据平台和大量的服务器资源
- 模块化,UI、 存储、集群管理都有多种机制可选
- 支持多种语言自动探针,包括 Java, .NET Core 和 Node.JS
- 多种监控手段,可以通过语言探针和 Service Mesh 获得监控的数据
工作原理
从上图克制,Skywalking 核心由四个组件构成:
skywalking-agent
:和业务应用(SpringBoot)绑定在一起,负责收集各种监控数据skywalking-oapservice
:通常以集群的形式存在,主要负责:- 处理监控数据:接收
skywalking agent
绑定应用的监控数据,并存储在数据库中; - 处理查询请求:接收
skywalking webapp
前端请求,从数据库查询数据返回给前端
- 处理监控数据:接收
skywalking-webapp
:前端界面,用于展示数据- 存储端:使用相关数据库(MySQL、ES 等)存储监控数据
环境搭建
下载
从 Skywalking 下载地址 获取即可。
注意:Skywalking 在 8.8.0 后skywalking-agent.jar
从skywalking-apm
包中额外拆分出来了,需要点此下载。
目录结构
apache-skywalking-apm-8.9.0.tar.gz
压缩包解压后目录结构如下:
bin
目录:存放各种启动脚本,一般使用脚本startup.sh
来启动 web 页面和对应的后台应用;oapService.*
:默认使用的后台程序的启动脚本; (使用的是默认模式启动,还支持其他模式,各模式区别见启动模式)oapServicelnit.*
:init
模式启动;在此模式下,OAP 服务器启动以执行初始化工作,然后退出oapServiceNolnit.*
:使用no init
模式启动;在此模式下,OAP 服务器不进行初始化webappService.*
: UI前端的启动脚本startup.*
: 组合脚本,同时启动oapService.*. webappService.*
脚本
config
: 启动后台应用程序的配置文件, 是使用的各种配置oap-libs
:后台应用的 jar 包,以及它的依赖 jar 包,里边有一个server-starter-*jar
就是启动程序;webapp
: 存放 UI 前端的 jar 包和配置文件
apache-skywalking-java-agent-8.14.0.tgz
压缩包解压后目录结构如下:
skywalking-agent.jar
:代理服务 jar 包config
:代理服务启动时使用的配置文件plugins
: 包含多个插件,代理服务启动时会加载改目录下的所有插件(实际是各种 jar 包)optional-plugins
:可选插件,当需要支持某种功能时,比如 SpringCloud Gateway,则需要把对应的 jar 包拷贝到此目录
启动
在apm
的bin
目录下,执行startup.sh
脚本成功后,会启动两个服务:
skywalking-oap-server
:启动后会暴露 11800 端口(收集监控数据)和 12800 端口(接受前端请求),若要修改上述端口,可在config/applicaiton.yml
文件进行配置,此服务可单独执行oapService.sh
脚本启动skywalking-web-ui
:Skywalking 的 UI 控制台,启动后会暴露 8080 端口,若端口其他应用冲突,可以在webapp/webapp.yml
文件配置新的端口,此服务可单独执行webappService.sh
脚本启动
停止
1 | jps |
功能集成
Skywalking 的 UI 界面导航栏包含系列菜单支撑其功能:
- 仪表盘
- 拓扑图
- 追踪
- 性能剖析
- 日志
- 告警
当然,要想使用这些功能,前提是在对应服务集成 Skywalking。
前提:微服务端集成
Skywalking 可以跨越多个微服务跟踪相关指标,只需要每个微服务启动时添加javaagent
参数即可,因此配置 Skywalking 的 JVM 参数后,再分别启动相关微服务即可:
1 | -javaagent:/Users/wk/software/skywalking/skywalking-agent/skywalking-agent.jar -DSW_AGENT_NAME=user-service |
仪表盘
仪表盘中可以查看一些指标数据:
拓扑图
拓扑图中可以查看一些服务的状态:
追踪
追踪可以查看一个请求接口调用的整个服务的状况:
业务追踪
如果我们希望对项目中的业务方法, 实现链路追踪,方便我们排查问题, 可以使用如下的代码引入依赖:1
2
3
4
5<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>8.9.0</version>
</dependency>
业务方法加入追踪链路
如果一个业务方法想在 UI 界面的跟踪链路上显示出来, 只需要在业务方法上加上@Trace
注解即可。
记录业务方法信息
Skywalking 可以为追踪链路增加其他额外的信息,比如记录参数和返回信息。
实现方式:在方法上增加@Tag
或者@Tags
,具体见下面代码:1
2
3
4
5
6
7
8
9
10
11
12
"list", value = "returned0bj" ) (key =
public List<User> list(){
return userMapper.list();
}
"param",value = "arg[0]"), ({ (key =
"user", value = "returnedObj") } ) (key =
public User getById(Integer id){
return userMapper.getById(id);
}
性能剖析
性能剖析可以针对指定接口方法进行监控,寻找出执行缓慢的代码。
若想使用此功能,在性能剖析菜单新建任务,之后配置即可。
性能剖析使用时需要注意亮点:
- 一个服务在监控持续时间内只能设置一个端点监控任务
- 监控的端点前缀需要增加 HTTP 请求类型:如
/dict/type/simpleList
是无法监控的,需要使用GET:/dict/type/simpleList
才行,因此上面的图配置是不对的 - 剖析端点的响应时间必须大于监控间隔时间,如果响应时间小于监控间隔时间,则无法进行采集监控。若想进行测试,可以将相关接口使用线程睡眠 500ms,这么做之后在性能剖析中新建对应任务后就能观察到
日志
如何跟踪一个请求调用后端应用后的整个方法流程?
在单体项目中,我们会使用 MDC 在日志增加一个请求的全局唯一 ID 以跟踪整个调用方法栈,但是 MDC 内部使用的是 ThreadLocal 进行实现,所以只有本线程才有效,子线程和下游的服务 MDC 里的值会丢失。
对于微服务项目,由于一个请求可能涉及到多个服务的调用,ThreadLocal 实现的 MDC 将不太适用了。
SkyWalking 是基于服务的追踪框架,服务追踪的实现思路是通过某些手段给目标应用注入追踪探针(Probe),因此 SkyWalking 非常适用微服务项目。
引入依赖
1 | <dependency> |
配置日志文件
logback.xml
文件按如下配置即可:
1 | <property name="SKYWALKING_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] %-5p ${PID:- } --- [%t] %logger{36} : %m%n"/> |
以上配置主要是进行了如下操作:
- 控制台日志变更为 skywalking 格式配置输出
- 记录 grpc-log 并异步输出到 skywalking 的存储端
详细配置见 Skywalking 与 Logback 集成说明。
注意不要使用异步日志,否则 UI 界面会没有追踪 ID。
配置 Agent
1 | vim agent.config |
末尾增加以下配置:1
2
3
4
5
6
7
8
9
10# 接收skywalking trace数据的后端地址
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:192.168.1.22:11800}
# 指定要向其报告日志数据的grpc服务器的主机,SW_GRPC_LOG_SERVER_HOST 需要修改
plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:192.168.1.22}
# 指定要向其报告日志数据的grpc服务器的端口
plugin.toolkit.log.grpc.reporter.server_port=${SW_GRPC_LOG_SERVER_PORT:11800}
# 指定grpc客户端要报告的日志数据的最大大小
plugin.toolkit.log.grpc.reporter.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760}
# 客户端向上游发送数据时将超时多长时间。单位是秒
plugin.toolkit.log.grpc.reporter.upstream_timeout=${SW_GRPC_LOG_GRPC_UPSTREAM_TIMEOUT:30}
注意:plugin.toolkit.log.grpc.reporter.server_host
和 collector.backend_service
是两个不同的配置项,具有不同的作用:
plugin.toolkit.log.grpc.reporter.server_host
: 用于配置 SkyWalking Agent 中日志收集器(LogCollector)将数据上报到的 SkyWalking OAP 服务地址。这个配置项只影响日志收集器的行为,不会影响 Tracing 数据的上报。collector.backend_service
: 用于配置 SkyWalking Agent 上报 Trace 和 Metric 数据的 SkyWalking OAP 服务地址。这个配置项影响整个 SkyWalking Agent 的行为,包括 Tracing 和 Metrics 数据的上报。
简单来说,plugin.toolkit.log.grpc.reporter.server_host
只是影响日志收集器上报数据的地址,而 collector.backend_service
影响整个 SkyWalking Agent 上报数据的地址,包括 Tracing 和 Metrics 数据。
所以,上述配置可以优化为:1
2
3
4
5
6# 接收 skywalking 数据的后端地址,可以配置多个,按 , 隔开
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:192.168.1.22:11800,192.168.1.23:11800}
# 指定grpc客户端要报告的日志数据的最大大小
plugin.toolkit.log.grpc.reporter.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760}
# 客户端向上游发送数据时将超时多长时间。单位是秒
plugin.toolkit.log.grpc.reporter.upstream_timeout=${SW_GRPC_LOG_GRPC_UPSTREAM_TIMEOUT:30}
告警
SkyWalking 告警功能在 6.x 版本引入, 其核心由一组规则驱动,规则触发后通知相关服务。
告警功能定义分为两部分:
- 告警规则:定义什么条件下触发度量警报
- Webhook(网络钩子): 定义当警告触发时, 哪些服务终端需要被告知
告警规则
告警规则定义了什么条件下触发度量警报。
默认的告警规则
SkyWalking 的发行版预先定义了一些默认的告警规则:
- 过去 3 分钟内服务平均响应时间超过 1 秒
- 过去 2 分钟服务成功率低于 80%
- 过去 3 分钟内服务响应时间超过 1s 的百分比
- 服务实例在过去 2 分钟内平均响应时间超过 1s, 并且实例名称与正则表达式匹配
- 过去 2 分钟内端点平均响应时间超过 1 秒
- 过去 2 分钟内数据库访问平均响应时间超过 1 秒
- 过去 2 分钟内端点关系平均响应时间超过 1 秒
这些预定义的告警规则, 在config/alarm-settings.yml
文件中可以看到。
告警规则配置说明
告警规则配置项的说明:
Rule name
: 规则名称,也是在告警信息中显示的唯一名称。 必须以_rule
结尾,前缀可自定义Metrics name
: 度量名称, 取值为oal脚本中的度量名,目前只支持 long、 double 和 int 类型Include names
: 该规则作用于哪些实体名称,比如服务名, 终端名(可选,默认为全部)Exclude names
: 该规则作不用于哪些实体名称,比如服务名, 终端名(可选,默认为空)Threshold
: 阈值OP
:操作符,目前支持 >、<、=Period
:多久告警规则需要被核实一下。 这是一个时间窗口, 与后端部署环境时间相匹配Count
:在一个Period窗口中, 如果values超过Threshold值 (按op), 达到Count值, 雲要发送警报Silence period
:在时间N中触发振警后,在TN-> TN + period这个阶段不告警。默认情况下, 它和Period- 样,这意味看相同的告警(在同一个Metrics name拥有相同的ld)在同一个Period内只会触发一次message
:告警消息
Webhook (网络钩子)
Webhook(网络钩子),定义了当警告触发时, 哪些服务终端需要被告知。
Webhook 可以简单理解为是一种 Web 层面的回调机制, 通常由一些事件触发,与代码中的事件回调类似,只不过是 Web 层面的。由于是 Web 层面的,所以当事件发生时,回调的不再是代码中的方法或函数,而是服务接口。例如,在告警这个场景,告警就是一个事件。当该事件发生时, SkyWalking 就会主动去调用一 个配好的接口,该接口就是所谓的 Webhook。
SkyWalking 的告警消息会通过 HTTP 请求进行发送,请求方法为 POST,Content-Type 为application/json
最佳实践
版本建议
Skywalking APM 建议使用 8.9.1 版本,9.X 版本存在大改,但大多功能用不到,若一定要用 9.X 版本,建议使用 9.3.0,因为后续版本不支持 JDK 8.
Skywalking Agent 建议使用 8.14.0 版本,因为低版本存在一些 Bug.
持久化
默认情况下,Skywalking 数据是存放到内存当中,若要持久化到其他数据库中需要修改config/application.yml
文件,之后重启即可。
基于 ES 持久化(常用)
1 | storage: |
踩坑记录
1 | Elasticsearch exception [type=index_not_found_exception, reason=no such index] |
重启 oap 服务或者删除 ES Skywalking 对应索引数据。
基于 MySQL 持久化(不推荐)
1 | storage: |
踩坑记录
由于oap-libs
目录下没有 MySQL 相关驱动包,所以 Skywalking 无法重启成功,解决方法是将 MySQL 的驱动包复制一份过来。
高可用
在大多数生产环境中,后端应用需要支持高吞吐量并且支持高可用来保证服务的稳定,所以在生产环境需要搭建应用集群。
Skywalking 集群是将多个 OAP 作为服务注册到 Nacos 上,只要 OAP 服务没有全部宕机,保证有一个在运行,就能进行链路跟踪。
搭建一个 skywalking oap 集群需要:
- 至少一个 Nacos(亦可用集群)
- 至少一个 ES(建议)/MySQL(亦可用集群)
- 至少两个 OAP 服务
- 至少一个 WEB-UI 服务
- 修改
config/application.yml
文件使用nacos
作为注册中心1
2
3
4
5
6
7
8
9
10
11
12
13
14
15cluster:
#selector: ${SW_CLUSTER:standalone}
# 修改为 Nacos,更改具体配置信息即可
selector: ${SW_CLUSTER:nacos}
nacos:
serviceName: ${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}
hostPort: ${SW_CLUSTER_NACOS_HOST_PORT:localhost:8848}
# Nacos Configuration namespace
namespace: ${SW_CLUSTER_NACOS_NAMESPACE:"public"}
# Nacos auth username
username: ${SW_CLUSTER_NACOS_USERNAME:""}
password: ${SW_CLUSTER_NACOS_PASSWORD:""}
# Nacos auth accessKey
accessKey: ${SW_CLUSTER_NACOS_ACCESSKEY:""}
secretKey: ${SW_CLUSTER_NACOS_SECRETKEY:""}
过期策略
在 SkyWalking 中,有两种类型的可观察性数据:
recordData
:包括 traces, logs, topN sampled statements、alarm,即追踪、日志、topN 采样语句和告警数据metricsData
:包括 metrics for service, instance, endpoint, topology map(即服务、实例、端点和拓扑图的所有度量)。Metadata(lists of services, instances,endpoints,即元数据(服务、实例或端点的列表)也属于度量
这些数据在 Skywalking 的存储端不会永久保存,存在过期时间,其配置对应application.yml
文件的以下配置:1
2
3
4
5
6
7
8
9
10core:
......
# 是否开启数据过期清理定时任务,默认开启
enableDataKeeperExecutor: ${SW_CORE_ENABLE_DATA_KEEPER_EXECUTOR:true}
# 数据过期清理定时任务执行频率,默认 5 分钟执行一次
dataKeeperExecutePeriod: ${SW_CORE_DATA_KEEPER_EXECUTE_PERIOD:5}
# recordDataTTL 作用于 recordData,默认过期时间是 3 天
recordDataTTL: ${SW_CORE_RECORD_DATA_TTL:3}
# metricsDataTTL 作用于 metricsData,默认过期时间是 7 天
metricsDataTTL: ${SW_CORE_METRICS_DATA_TTL:7}
上述配置可针对具体生产情况优化,建议都调大点。
忽略部分接口
很多应用包中包含心跳请求,导致 APM 上传了很多不需要监控的路径,可以通过安装插件进行配置,以忽略这些路径减少上传分析的数据。
相关插件为apm-trace-ignore-plugin-8.14.0.jar
,因此我们若有相关需求可以使用:1
cp optional-plugins/apm-trace-ignore-plugin-8.14.0.jar plugins/
之后在agent/config
下面新建一个配置文件apm-trace-ignore-plugin.config
,1
vim apm-trace-ignore-plugin.config
配置其内容为:1
trace.ignore_path=${SW_AGENT_TRACE_IGNORE_PATH:/actuator/health/**,/eureka/**,Lettuce/**,Gson/**}
兼容 Spring Cloud Gateway
若集成 Skywalking 的应用包含 Spring Cloud Gateway 组件,会发现 Gateway 模块无法加入到链路中。
为什么呢?
原因在于,Skywalking 默认情况下是不支持 Spring Cloud Gateway 组件的,那么,怎么让其支持呢?
其实,skywalking-agent
目录下存在两个插件目录:
plugins
:skywalking-agent.jar
在启动时会加载其下的所有 jar 包optional-plugins
:可选的插件 jar 包,skywalking-agent.jar
在启动时不会加载其下 jar 包
Skywalking 不支持 Gateway 的真实原因就在于,plugins
目录下是没有相关支持的 jar 包的,但是!!!optional-plugins
下是存在的,若想让 SkyWalking 支持 Gateway,只需要将可选插件下的相关 jar 包复制到plugins
目录下即可。1
2
3
4
5# Spring Cloud Gateway 基于WebFlux实现,必须搭配上apm-spring-cloud-gateway-x.x.x-plugin 和 apm-spring-webflux-x.x-plugin 两个插件
# Gateway 版本不同,存在 2.x 和 3.x, 按自己版本选择即可
cp optional-plugins/apm-spring-cloud-gateway-3.x-plugin-8.14.0.jar plugins/
cp optional-plugins/apm-spring-webflux-5.x-plugin-8.14.0.jar plugins/
UI 兼容 Nginx
默认的 Skywalking Web UI 不兼容 Nginx 子目录代理,需要重新编译源码包。
从 Github 下载对应源码包后,修改vue.config.js
,在module.exports
中增加一行publicPath: "/skywalking"
,这个是前端部署在服务器上之后需要配置的子路径。但如果只修改这一行,部署之后会发现,前端要去请求一个graphql
地址,这个地址不会被publicPath
所影响,因此其请求的路径还是域名根路径,为了兼容这个,需要在代码中全局搜索/graphql
,相关位置都改成/skywalking/graphql
,然后使用编译打包丢到服务器上,之后进行 Nginx 配置。
1 | location /skywalking { |
参考
- Skywalking 官网
- Skywalking 下载界面
- Skywalking Github
- Skywalking 官方文档
- Skywalking 中文文档
- Skywalking Booster UI前置 Nginx 代理部署过程和坑点记录
文章信息
时间 | 说明 |
---|---|
2022-11-05 | 初版 |