SkyWalking链路追踪
# 1 SkyWalking是什么?
SkyWalking是一个国产开源框架,2015年由吴晟开源 , 2017年加入Apache孵化器。SkyWalking是分布式系统的应用程序性能监视 工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。SkyWalking是观察性分析平台和应用性能管理系统,提供分布式追踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。
官网: http://skywalking.apache.org/ (opens new window)
下载: http://skywalking.apache.org/downloads/ (opens new window)
Github: https://github.com/apache/skywalking (opens new window)
文档: https://skywalking.apache.org/docs/main/v8.4.0/readme/ (opens new window)
中文文档: https://skyapm.github.io/document-cn-translation-of-skywalking/ (opens new window)
# 1.1 调用链选型
- Zipkin是Twitter开源的调用链分析工具,目前基于springcloud sleuth得到了广泛的使用,特点是轻量,使用部署简单。
- Pinpoint是韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具。支持多种插件,UI功能强大,接入端无代码侵入。
- SkyWalking是本土开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI功能较强,接入 端无代码侵入。目前已加入Apache孵化器。
- CAT是大众点评开源的基于编码和配置的调用链分析,应用监控分析,日志采集,监控报警等一系列的监控平台工具。
基本原理
Zipkin | Pinpoint | SkyWalking | CAT |
---|---|---|---|
拦截请求,发送数据(HTTP,MQ)到zipkin服务 | java探针,字节码增强 | java探针,字节码增强 | 代码埋点(连接器,注解,过滤器等) |
探针性能对比
模拟了三种并发用户:500,750,1000。使用jmeter测试,每个线程发送30个请求,设置思考时间为10ms。使用的采样率为1,即 100%,这边与生产可能有差别。pinpoint默认的采样率为20,即50%,通过设置agent的配置文件改为100%。zipkin默认也是1。组 合起来,一共有12种。下面看下汇总表:
从上表可以看出,在三种链路监控组件中,skywalking的探针对吞吐量的影响最小,zipkin的吞吐量居中。pinpoint的探针对吞吐 量的影响较为明显,在500并发用户时,测试服务的吞吐量从1385降低到774,影响很大。然后再看下CPU和memory的影响,在内 部服务器进行的压测,对CPU和memory的影响都差不多在10%之内。
# 1.2 SkyWalking整体架构
整个架构分成四部分:
- 上部分 Agent : 负责从应用中,收集链路信息,发送给 SkyWalking OAP 服务器;
- 下部分 SkyWalking OAP : 负责接收Agent发送的Tracing数据信息,然后进行分析(Analysis Core),存储到外 部存储器(Storage),最终提供查询(Query)功能;
- 右部分 Storage: Tracing数据存储,目前支持ES、MySQL、Sharding Sphere、TiDB、H2多种存储器,目前采 用较多的是ES,主要考虑是SkyWalking开发团队自己的生产环境采用ES为主;
- 左部分 SkyWalking UI: 负责提供控制台,查看链路等等;
SkyWalking支持三种探针:
- Agent – 基于ByteBuddy字节码增强技术实现,通过jvm的agent参数加载,并在程序启动时拦截指定的方法来 收集数据。
- SDK – 程序中显式调用SkyWalking提供的SDK来收集数据,对应用有侵入。
- Service Mesh – 通过Service mesh的网络代理来收集数据。
# 1.3 SkyWalking环境部署
- skywalking agent和业务系统绑定在一起,负责收集各种监控数据
- skywalking oapservice是负责处理监控数据的,比如接受skywalking agent的监控数据,并存储在数据 库中;接受skywalking webapp的前端请求,从数据库查询数据,并返回数据给前端。skywalking oapservice通常以集群的形式存在。
- skywalking webapp,前端界面,用于展示数据。
- 用于存储监控数据的数据库,比如mysql、elasticsearch等。
# 2 安装SkyWalking
# 2.1 下载并解压安装包
# 解压到 /louis 目录
tar -zvxf kibana-7.6.1-linux-x86_64.tar.gz -C /louis
# 重命名
mv apache-skywalking-apm-bin-es7 skywalking-7
2
3
4
# 2.2 搭建SkyWalking OAP 服务
先使用默认的H2数据库存储,不用修改配置
config/application.yml
storage:
selector: ${SW_STORAGE:h2}
2
启动脚本bin/startup.sh
[root@localhost skywalking-7]# bin/startup.sh
SkyWalking OAP started successfully!
SkyWalking Web Application started successfully!
2
3
日志信息存储在logs目录下
[root@localhost skywalking-7]# cd logs/
[root@localhost logs]# ll
总用量 152
-rw-r--r--. 1 root root 0 4月 16 01:26 oap.log
-rw-r--r--. 1 root root 116700 4月 16 01:27 skywalking-oap-server.log
-rw-r--r--. 1 root root 0 4月 16 01:26 webapp-console.log
-rw-r--r--. 1 root root 23563 4月 16 01:27 webapp.log
2
3
4
5
6
7
启动成功后会启动两个服务,一个是skywalking-oap-server,一个是skywalking-web-ui skywalking-oap-server服务启动后会暴露11800 和 12800 两个端口,分别为收集监控数据的端口11800和接受前 端请求的端口12800。
提示
修改端口可以修改config/applicaiton.yml
skywalking-web-ui服务会占用 8080 端口, 修改端口可以修改webapp/webapp.yml
server:
port: 8080
collector:
path: /graphql
ribbon:
ReadTimeout: 10000
# Point to all backend's restHost:restPort, split by ,
listOfServers: 127.0.0.1:12800
2
3
4
5
6
7
8
9
提示
此次先修改端口为 8182
访问 http://172.16.227.132:8182/
# 2.3 SkyWalking中的三个概念
- 服务(Service) :表示对请求提供相同行为的一系列或一组工作负载,在使用Agent时,可以定义服务的名字;
- 服务实例(Service Instance) :上述的一组工作负载中的每一个工作负载称为一个实例, 服务实例就是操作系统上的一个真实进程;
- 端点(Endpoint) :对于特定服务所接收的请求路径, 如HTTP的URI路径和gRPC服务的类名 + 方法签名;
# 3 SkyWalking快速开始
在IDEA中使用SkyWalking
# 3.1 拷贝安装包中agent目录到应用机器
[root@localhost agent]# pwd
/louis/skywalking-7/agent
[root@localhost agent]# ls
activations bootstrap-plugins config logs optional-plugins optional-reporter-plugins plugins skywalking-agent.jar
2
3
4
提示
skywalking-agent.jar启动时需读取config目录下的agent.config配置文件。
# 3.2 IDEA应用中添加启动参数
- 指定本机拷贝好的skywalking-agent.jar。
- 指定服务名。
- 指定SkyWalking的服务端ip。
-javaagent:/Users/luoxiaofeng/louisprojects/agent/skywalking-agent.jar
-DSW_AGENT_NAME=springboot-skywalking-demo
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=172.16.227.132:
2
3
# 3.3 启动应用
# 4 SkyWalking告警通知
skywalking告警的核心由一组规则驱动,这些规则定义在config/alarm-settings.yml文件中,告警规则的定义分为三部分:
告警规则:它们定义了应该如何触发度量警报,应该考虑什么条件;
网络钩子(Webhook}:当警告触发时,哪些服务终端需要被通知;
gRPC钩子:远程gRPC方法的主机和端口,告警触发后调用; 为了方便,skywalking发行版中提供了默认的alarm-setting.yml文件,包括一些规则,每个规则有英文注释,可以 根据注释得知每个规则的作用:
1) 在最近10分钟的3分钟内服务平均响应时间超过1000ms 2) 在最近10分钟的3分钟内服务平均响应时间超过1000ms 3) 最近10分钟内,服务成功率在2分钟内低于80% 4) 服务实例的响应时间在过去10分钟的2分钟内超过1000ms 5) 数据库访问{name}的响应时间在过去10分钟的2分钟内超过1000ms
1
2
3
4
5
只要我们的服务请求符合alarm-setting.yml文件中的某一条规则会触发告警。
比如service_resp_time_rule规则:
service_resp_time_rule:
metrics-name: service_resp_time
op: ">"
threshold: 1000
period: 10
count: 3
silence-period: 5
message: Response time of service {name} is more than 1000ms in 3 minutes of last 10 minutes.
2
3
4
5
6
7
8
回调接口
在config/alarm-settings.yml中配置回调接口,并重启skywalking服务
webhooks:
# - http://127.0.0.1/notify/
# - http://127.0.0.1/go-wechat/
- http://192.168.1.3:8000/notify
2
3
4
[root@localhost config]# jps
13184 OAPServerStartUp
13200 skywalking-webapp.jar
13508 Jps
3272 Elasticsearch
[root@localhost config]# kill -9 13184 13200
2
3
4
5
6
@RestController
public class IndexController {
@RequestMapping("/")
public String hello(){
return "hello louis";
}
@RequestMapping("/notify")
public String notify(@RequestBody Object obj){
//TODO 告警信息,给技术负责人发短信,钉钉消息,邮件,微信通知等
System.err.println(obj.toString());
return "notify successfully";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[
{
"scopeId": 1,
"scope": "SERVICE",
"name": "springboot-skywalking-demo",
"id0": "c3ByaW5nYm9vdC1za3l3YWxraW5nLWRlbW8=.1",
"id1": "",
"ruleName": "service_sla_rule",
"alarmMessage": "Successful rate of service springboot-skywalking-demo is lower than 80% in 2 minutes of last 10 minutes",
"startTime": 1652701044636
},
{
"scopeId": 1,
"scope": "SERVICE",
"name": "springboot-skywalking-demo",
"id0": "c3ByaW5nYm9vdC1za3l3YWxraW5nLWRlbW8=.1",
"id1": "",
"ruleName": "service_resp_time_percentile_rule",
"alarmMessage": "Percentile response time of service springboot-skywalking-demo alarm in 3 minutes of last 10 minutes, due to more than one condition of p50 > 1000, p75 > 1000, p90 > 1000, p95 > 1000, p99 > 1000",
"startTime": 1652701044636
}
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 5 基于elasticsearch持久化跟踪数据
修改config/application.yml配置文件
storage:
selector: ${SW_STORAGE:elasticsearch7}
... ...
elasticsearch7:
nameSpace: ${SW_NAMESPACE:""}
# 配置es地址
clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:172.16.227.132:9200}
protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"}
trustStorePath: ${SW_STORAGE_ES_SSL_JKS_PATH:""}
trustStorePass: ${SW_STORAGE_ES_SSL_JKS_PASS:""}
dayStep: ${SW_STORAGE_DAY_STEP:1} # Represent the number of days in the one minute/hour/day index.
indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:1} # Shard number of new indexes
indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:1} # Replicas number of new indexes
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
重启SkyWalking
启动时会向elasticsearch中创建大量的index索引用于持久化数据,每天会产生一个新的索引文件。
# 6 自定义SkyWalking链路追踪
如果我们希望对项目中的业务方法,实现链路追踪,方便我们排查问题,可以使用如下的代码 引入依赖
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm‐toolkit‐trace</artifactId>
<version>8.4.0</version>
</dependency>
2
3
4
5
# 6.1 traceId
在业务方法中可以TraceContext获取到traceId
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@RequestMapping("/list")
public List<User> list() {
//TraceContext可以绑定key‐value
TraceContext.putCorrelation("name", "louis");
Optional<String> op = TraceContext.getCorrelation("name");
log.info("name = {} ", op.get());
//获取跟踪的traceId
String traceId = TraceContext.traceId();
log.info("traceId = {} ", traceId);
return userService.list();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
测试 http://192.168.1.3:8000/user/list
在Skywalking UI中查询tranceId
# 6.2 @Trace将方法加入追踪链路
- 如果一个业务方法想在ui界面的跟踪链路上显示出来,只需要在业务方法上加上@Trace注解即可.
- 我们还可以为追踪链路增加其他额外的信息,比如记录参数和返回信息。实现方式:在方法上增加@Tag或者 @Tags。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Trace
@Tag(key = "list", value = "returnedObj")
@Override
public List<User> list(){
return userMapper.list();
}
@Trace
@Tags({@Tag(key = "param", value = "arg[0]"),
@Tag(key = "user", value = "returnedObj")})
@Override
public User getById(Integer id){
return userMapper.getById(id);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 7 安装包下载
百度网盘
https://pan.baidu.com/s/1pvGbnkMpdqs3ICMpMsrVow (opens new window)
提取码 : cr1x