1. 什么是 Skywalking
1.1 架构图
整体架构包含如下三个组成部分
- 探针 (agent) 负责进行数据的收集,包含了 Tracing 和 Metrics 的数据,agent 会被安装到服务所在的服务器上,以方便数据的获取。
- 可观测性分析平台 OAP(Observability Analysis Platform),接收探针发送的数据,并在内存中使用分析引擎 (Analysis Core) 进行数据的整合运算,然后将数据存储到对应的存储介质上,比如 Elasticsearch、MySQL 数据库、H2 数据库等。同时 OAP 还使用查询引擎 (Query Core) 提供HTTP查 询接口。
- Skywalking 提供单独的 UI 进行数据的查看,此时 UI 会调用 OAP 提供的接口,获取对应的数据然后进行展示。
主要概念
服务(service):服务是一个独立的应用(Application)
端点(Endpoint):服务对外提供的 Http 接口就是一个端点
实例(Instance):相同服务部署的多个节点就是实例
1.2 环境搭建
1.2.1 源码编译
官方地址 https://github.com/apache/skywalking 也可以使用我编译后带源码的地址 https://github.com/masteryourself-opensource/skywalking.git
打包完成后在 dist
目录得到产物 apache-skywalking-apm-bin.tar.gz
解压后得到 apache-skywalking-apm-bin
git clone https://github.com/masteryourself-opensource/skywalking
git checkout -b masteryourself-v6.5.0 origin/masteryourself-v6.5.0
cd skywalking/
git submodule init
git submodule update
mvn clean package -Dmaven.test.skip
1.2.2 启动 OAP
OAP 服务的配置文件在 config/application.yml
,默认是 H2 数据库,可以在这里进行修改
1.2.3 启动 UI
UI 服务的配置文件在 webapp/webappp.yml
中,可以定义服务端口号,OAP 服务地址列表等
通过命令 sh bin/webappService.sh
启动
可以通过 sh startup.sh
同时启动 OAP 和 UI
1.3 属性覆盖
优先级顺序是:探针配置 > 系统配置 > 系统环境变量 > 配置文件中的值
1.3.1 系统配置
使用 skywalking.
+ 配置文件中的配置名作为系统配置项来进行覆盖
如想覆盖 agent.service_name
配置
-Dskywalking.agent.service_name=skywalking_mysql
1.3.2 探针配置
如想覆盖 agent.service_name
配置,特殊字符可以使用 ''
引号包裹
-javaagent:/path/to/skywalking-agent.jar=[option1]=[value1],[option2]=[value2]
1.3.3 系统环境变量
如想覆盖 agent.service_name
配置,只需要在环境变量中配置 SW_AGENT_NAME
属性即可
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
2. SkyWalking 使用
2.1 Agent 目录
+-- agent # agent 目录
+-- activations # 工具包
apm-toolkit-log4j-1.x-activation.jar
apm-toolkit-log4j-2.x-activation.jar
apm-toolkit-logback-1.x-activation.jar
...
+-- bootstrap-plugins # 需要用 Bootstrap Classloader 加载的插件
+-- config # 配置文件
agent.config
+-- plugins # 生效的插件包
apm-dubbo-plugin.jar
apm-feign-default-http-9.x.jar
apm-httpClient-4.x-plugin.jar
.....
skywalking-agent.jar # 使用 -javaagent 指定的 agent 包
2.2 Agent 使用
在编译器 IDE 中启动
-javaagent:/Users/ruanrenzhao/environment/apache-skywalking-apm-bin/agent/skywalking-agent.jar
使用命令行启动
java -javaagent:/Users/ruanrenzhao/environment/apache-skywalking-apm-bin/agent/skywalking-agent.jar jar xxx.jar
在 Tomcat Web 容器中修改 tomcat/bin/catalina.sh
的第一行
CATALINA_OPTS="$CATALINA_OPTS -javaagent:/path/to/skywalking-agent/skywalking-agent.jar"; export CATALINA_OPTS
2.3 RocketBot 使用
2.3.1 仪表盘
仪表盘分为两大块
- 服务仪表盘,展示服务的调用情况
- 数据库仪表盘,展示数据库的响应时间等数据
选中服务仪表盘,有四个维度的统计数据可以进行查看
- 全局,查看全局接口的调用,包括全局响应时长的百分比,最慢的端点,服务的吞吐量等
- 服务,显示服务的响应时长、SLA、吞吐量等信息
- 端点,显示端点的响应时长、SLA、吞吐量等信息
- 实例,显示实例的响应时长、SLA、吞吐量等信息,还可以查看实例的JVM的GC信息、CPU信息、 内存信息
2.3.2 拓扑图
可以详细看到应用之间的调用关系,上图表示 user 调用了 Your_ApplicationName,Your_ApplicationName 调用了数据库2.3.3 追踪
在 SkyWalking 中,每一次用户发起一条请求,就可以视为一条追踪数据,每条追踪数据携带有一个 ID 值。追踪数据在追踪页面中可以进行查询
每条追踪记录有三种展示效果,列表,树结构,表格
点击每个 Span 可以查看详细的 Span 信息,如 SQL、异常堆栈等
2.4 工具包使用
2.4.1 引入 Jar 包
<!--skywalking trace工具包-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>6.5.0</version>
</dependency>
2.4.2 API 使用
使用 TraceContext.traceId()
可以打印出当前追踪的ID,方便在 RocketBot 中进行搜索
ActiveSpan 提供了三个方法进行信息的打印:
- error 方法会将本次调用变为失败状态,同时可以打印对应的堆栈信息和错误提示。
- info 方法打印 info 级别的信息。
debug 方法打印 debug 级别的信息。
@GetMapping("/plugin")
public String plugin() {
//使当前链路报错,并且提示报错信息
ActiveSpan.error(new RuntimeException("Test-Error-Throwable"));
//打印info信息
ActiveSpan.info("Test-Info-Msg");
// 打印debug信息
ActiveSpan.debug("Test-debug-Msg");
return TraceContext.traceId();
}
2.4.3 查看追踪面板
异常堆栈有如下内容
事件类型为 error
- 调用方法时传递的异常类型
- 调用方法时的异常 message 信息
异常堆栈内容
2.5 查看增强后的字节码内容
Java 实时反编译工具 https://github.com/zifeihan/friday
git clone https://github.com/zifeihan/friday
mvn clean install -Dmaven.test.skip
cd starter/target
tar -zxvf friday-starter.tar.gz
sh bin/run.sh
选择一个 Java 进程后,连上去后可以查看到实时运行的字节码文件
/*
* Decompiled with CFR 0.149.
*
* Could not load the following classes:
* org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ConstructorInter
* org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance
* org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter
* org.masteryourself.tutorial.skywalking.demo.controller.DemoController
* org.masteryourself.tutorial.skywalking.demo.controller.DemoController$auxiliary$9g06w93P
* org.masteryourself.tutorial.skywalking.demo.controller.DemoController$auxiliary$pucJ4sy3
* org.springframework.web.bind.annotation.GetMapping
* org.springframework.web.bind.annotation.RequestMapping
* org.springframework.web.bind.annotation.RestController
*/
package org.masteryourself.tutorial.skywalking.demo.controller;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ConstructorInter;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter;
import org.masteryourself.tutorial.skywalking.demo.controller.DemoController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value={"/demo"})
public class DemoController
implements EnhancedInstance,
EnhancedInstance {
// 这里多了一个字段
private volatile Object _$EnhancedClassField_ws;
public static volatile /* synthetic */ InstMethodsInter delegate$o59nu91;
public static volatile /* synthetic */ InstMethodsInter delegate$3p5b3r0;
public static volatile /* synthetic */ ConstructorInter delegate$hut6vg1;
private static final /* synthetic */ Method cachedValue$fzikR5Vm$epivfa1;
public static volatile /* synthetic */ InstMethodsInter delegate$l1upa31;
public static volatile /* synthetic */ InstMethodsInter delegate$72rosd1;
public static volatile /* synthetic */ ConstructorInter delegate$bggua40;
private static final /* synthetic */ Method cachedValue$tbOTwlWY$epivfa1;
public DemoController() {
this(null);
delegate$bggua40.intercept((Object)this, new Object[0]);
}
private /* synthetic */ DemoController(auxiliary.pucJ4sy3 pucJ4sy32) {
}
@GetMapping(value={"/hello"})
public String hello() {
// 目标方法已经被代理
return (String)delegate$l1upa31.intercept((Object)this, new Object[0], (Callable)new auxiliary.9g06w93P(this), cachedValue$tbOTwlWY$epivfa1);
}
private /* synthetic */ String hello$original$c0h8eDbn() {
// 原方法会保留, 替换方法名称
return "hello skywalking";
}
static {
ClassLoader.getSystemClassLoader().loadClass("org.apache.skywalking.apm.dependencies.net.bytebuddy.dynamic.Nexus").getMethod("initialize", Class.class, Integer.TYPE).invoke(null, DemoController.class, -830093591);
cachedValue$tbOTwlWY$epivfa1 = DemoController.class.getMethod("hello", new Class[0]);
}
final /* synthetic */ String hello$original$c0h8eDbn$accessor$tbOTwlWY() {
return this.hello$original$c0h8eDbn();
}
}