微服务链路追踪-SkyWalking
SkyWalking官网地址:https://skywalking.apache.org/
SkyWalking官方文档:https://skywalking.apache.org/docs/main/v8.6.0/readme/
SkyWalking中文文档:https://skyapm.github.io/document-cn-translation-of-skywalking/
SkyWalking是 一个开源的可观测平台, 用于从服务和云原生基础设施收集, 分析, 聚合及可视化数据。
一、SkyWalking环境搭建部属
1. 下载
下载地址:https://skywalking.apache.org/downloads/
这里下载的是SkyWalking APM 8.6.0的版本。
下载解压。
2. 环境搭建
SkyWalking包含:
- agent和业务系统绑定在一起,负责收集各种监控数据。
- oap服务,是负责处理监控数据的,比如接受skywalking agent的监控数据, 并存储在数据库中;接受skywalking webapp的前端请求,从数据库查询数据,并返回数据给前端。Skywalking oapservice通常以集群的形式存在。
- webapp的UI服务,前端界面,用于展示数据。
2.1 config/application.yml文件
Skywalking启动后,会有两个服务(oap与UI),oap服务会暴露两个端口号:11800用于收集监控数据的端口,12800用于接收前端请求。
SkyWalking的默认存储方式为h2,为了持久化可以修改为mysql,elasticsearch等。
这里我们使用MySQL的存储方式,然后相应的修改下面对应的mysql的配置信息(地址、账号密码等信息)。
2.2 webapp/webapp.yml
SkyWalking UI服务端口,默认是8080。
这里修改为8868后,启动访问的地址为 http://127.0.0.1:8868/。
2.3 MySQL持久化
将存储方式修改为MySQL方式后,需要新建对应的数据库,这里时swtest,在SkyWalking启动时会自动建表,需要注意的是,启动脚本中没有mysql驱动包,需要下载后放在oap-libs目录下。mysql-connector-java.jar可以前往https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.21下载,也可以从有依赖mysql的项目仓库中找一个。
项目启动时可能会遇到time zone的服务器时区问题,需要在config/application.yml的mysql信息配置中设置时区,jdbc:mysql://localhost:3306/swtest?serverTimezone=UTC
。
项目成功启动后数据库如下:
2.4 日志文件
启动后会生成logs文件夹,日志文件存储在那里。
2.5 SkyWalking接入微服务
SkyWalking是无侵入性的,无需在项目代码中进行配置。只需在项目启动时做些配置。
用IDEA配置:在Configurations中配置VM options。配置如下:
-javaagent:D:/skywalking/apache-skywalking-apm-bin-es7/agent/skywalking-agent.jar
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
-DSW_AGENT_NAME=gateway-server
第一行是指定skywalking-agent.jar的文件路径,第二行是指定收集监控数据的地址,第三行是该项目的服务名(自定义,一般使用spring.application.name)。
用jar包启动配置:
java -javaagent:D:/skywalking/apache-skywalking-apm-bin-es7/agent/skywalking-agent.jar
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
-DSW_AGENT_NAME=gateway-server -jar xxx.jar
Linux下启动配置: 可以通过上述java -jar的方式,也可以配置启动脚本:
#!/bin/sh
# SkyWalking agent配置
export SW_AGENT_NAME=gateway-server
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
export SW_AGENT_SPAN_LIMIT=2000 #配置链路的最大Span数量,默认为 300。
export JAVA_AGENT=‐javaagent:/usr/local/src/apache‐skywalking‐apm‐bin‐es7/agent/skywalking‐agent.jar
java $JAVA_AGENT ‐jar xxx.jar
2.6 Gateway接入微服务不显示问题
需将agent\optional-plugins目录下的gateway包移至agent/plugins目录。
项目中我是用的是gateway 2.0的版本,但是移入2.0的还是不显示,2.1的可以显示。
3. 自定义SkyWalking链路追踪
SkyWalking链路追踪的为请求接口,不会追踪接口下所涉及到的方法,为了方便排查问题,可做如下操作:
3.1 引入依赖
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<!--与下载的skywalking版本一致-->
<version>8.6.0</version>
</dependency>
3.2 追踪方法
通过@Trace注解加在业务方法上可以将该方法在追踪面板中显示出来。
@Trace
public String hello() {
return "hello";
}
通过@Tags或@Tag注解可以为追踪链路增加额外的信息。
@Tag注解中有两个属性,key为自定义名称,一般设置为方法名,value 可以是表示返回值的returnedObj
或者表示参数的arg[参数索引]
。
示例:
①、@Tag-方法返回值示例
@Trace
@Tag(key = "hello",value = "returnedObj")
public String hello(Integer id) {
return "hello world";
}
②、@Tags与@Tag-返回方法参数与方法返回值
@Trace
@Tags({
@Tag(key = "helloParam",value = "arg[0]")
,@Tag(key = "helloReturn",value = "returnedObj")})
public String hello(Integer id) {
return "hello world";
}
4. SkyWalking集成日志
4.1 logback配置
引入依赖:
<!--skywalking集成日志-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>8.6.0</version>
</dependency>
logback-spring.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 引入 Spring Boot 默认的 logback XML 配置文件 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志的格式化 -->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern>-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} [%tid] %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}</Pattern>
</layout>
</encoder>
</appender>
<!--Skywalking通过grpc上报日志-->
<appender name="grpc-log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
</layout>
</encoder>
</appender>
<!-- 设置 Appender -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="grpc-log"/>
</root>
</configuration>
项目启动后请求接口会生成带有“[TID:1a418fc3c3b94aa6949800cc67191854.136.16529817427060001]” TID的日志,可根据TID及追踪ID在前台UI中进行搜索。
注意:
当SkyWalking没有部署在本地时,需要将agent\config\agent.config文件添加如下配置:
plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:127.0.0.1}
plugin.toolkit.log.grpc.reporter.server_port=${SW_GRPC_LOG_SERVER_PORT:11800}
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.transmit_formatted | 是否以格式化或未格式化的格式传输记录的数据 | true |
plugin.toolkit.log.grpc.reporter.server_host | 指定要向其报告日志数据的grpc服务器的主机 | 127.0.0.1 |
plugin.toolkit.log.grpc.reporter.server_port | 指定要向其报告日志数据的grpc服务器的端口 | 11800 |
plugin.toolkit.log.grpc.reporter.max_message_s ize | 指定grpc客户端要报告的日志数据的最大大小 | 10485760 |
plugin.toolkit.log.grpc.reporter.upstream_time out | 客户端向上游发送数据时将超时多长时间 | 单位是秒 30 |
4.2 log4j 配置
4.2 log4j2j 配置
5. SkyWalking告警功能
官方文档:https://github.com/apache/skywalking/blob/v8.6.0/docs/en/setup/backend/backend-alarm.md
告警规则都定义在config/alarm-setting.yml中。报警规则定义分为三个部分:
- 报警规则:它们定义了应如何触发指标警报以及应考虑哪些条件。
- 网络钩子(Webhook):Web 服务终结点的列表,应在触发警报后调用。
- gRPCHook:远程 gRPC 方法的主机和端口,应在触发警报后调用。
5.1 默认报警规则
为了方便起见,我们在版本中提供了默认值。它包括以下规则:
- 过去 3 分钟内超过 1 秒的服务平均响应时间。
- 最近 2 分钟内服务成功率低于 80%。
- 过去 3 分钟内超过 1 秒的服务响应时间百分位数
- 服务实例在过去 2 分钟内的平均响应时间超过 1 秒,并且实例名称与正则表达式匹配。
- 终结点在过去 2 分钟内超过 1 秒的平均响应时间。
- 数据库访问过去 2 分钟内超过 1 秒的平均响应时间。
- 过去 2 分钟内超过 1 秒的终结点关系平均响应时间。
5.2 网络钩子
可以理解为Web层面的回调机制,当触发报警时,SkyWalking的告警消息会通过HTTP请求进行发送,请求方式为POST,Content-Type 为 application/json,其JSON 数据实基于List<org.apache.skywalking.oap.server.core.alarm.AlarmMessage>
进行序列化的,JSON数据示例:
[{
"scopeId": 1,
"scope": "SERVICE",
"name": "serviceA",
"id0": "12",
"id1": "",
"ruleName": "service_resp_time_rule",
"alarmMessage": "alarmMessage xxxx",
"startTime": 1560524171000,
"tags": [{
"key": "level",
"value": "WARNING"
}]
}, {
"scopeId": 1,
"scope": "SERVICE",
"name": "serviceB",
"id0": "23",
"id1": "",
"ruleName": "service_resp_time_rule",
"alarmMessage": "alarmMessage yyy",
"startTime": 1560524171000,
"tags": [{
"key": "level",
"value": "CRITICAL"
}]
}]
- scopeId, scope:所有作用域都在 中定义。
org.apache.skywalking.oap.server.core.source.DefaultScopeDefine
- name:目标 scope 的实体名称。请遵循实体名称定义。
- id0:与名称匹配的 scope 的 ID。使用关系范围时,它是源实体 ID。
- id1:使用关系作用域时,它是目标实体 ID。否则,它是空的。
- ruleName::在alarm-settings.yml中配置的规则名称。
- alarmMessage:警报文本消息。
- startTime:告警时间,格式为时间戳。
- tags:在alarm-settings.yml中配置的标记。
5.3 告警功能实现
创建网络钩子所需实体类:
@Data
public class SkyWalkingAlarm {
private Integer scopeId;
private String scope;
private String name;
private String id0;
private String id1;
private String ruleName;
private String alarmMessage;
private Long startTime;
}
SkyWalking回调接口,需要在config/alarm-setting.yml中结尾添加回调路径:
webhooks:
- http://127.0.0.1:8800/test/alarm
然后为该请求实现业务:
@RestController
@RequestMapping("/test")
@Slf4j
public class SkyWalkingController {
@PostMapping("/alarm")
public String alarm(@RequestBody List<SkyWalkingAlarm> alarmList) {
log.info("=====告警信息提醒=====");
StringBuilder sb = new StringBuilder();
for (SkyWalkingAlarm alarm : alarmList) {
sb.append("scopeId: ").append(alarm.getScopeId())
.append("\nscope: ").append(alarm.getScope())
.append("\n目标 Scope 的实体名称: ").append(alarm.getName())
.append("\nScope 实体的 ID: ").append(alarm.getId0())
.append("\nid1: ").append(alarm.getId1())
.append("\n告警规则名称: ").append(alarm.getRuleName())
.append("\n告警消息内容: ").append(alarm.getAlarmMessage())
.append("\n告警时间: ").append(alarm.getStartTime())
.append("\n\n‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n\n");
}
System.out.println(sb);
return "告警信息提醒";
}
}