skywalking是一个可观测性分析平台和应用性能管理系统,它也是基于OpenTracing规范、开源的AMP系统。Skywalking提供分布式跟踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。支持Java
1、Agent(探针):Agent 运行在各个服务实例中,负责采集服务实例的 Trace 、Metrics 等数据,然后通过 gRPC方式上报给SkyWalking后端。
2、OAP:SkyWalking 的后端服务,其主要责任有两个。
一个是负责接收 Agent 上报上来的 Trace、Metrics 等数据,交给 Analysis Core (涉及SkyWalkingOAP 中的多个模块)进行流式分析,最终将分析得到的结果写入持久化存储中。SkyWalking 可以使用ElasticSearch、H2、MySQL等作为其持久化存储,一般线上使用ElasticSearch 集群作为其后端存储。
另一个是负责响应 SkyWalking UI 界面发送来的查询请求,将前面持久化的数据查询出来,组成正确的响应结果返回给 UI界面进行展示。
3、UI 界面:SkyWalking 前后端进行分离,该 UI 界面负责将用户的查询操作封装为 GraphQL 请求提交给 OAP后端触发后续的查询操作,待拿到查询结果之后会在前端负责展示。
安装agent
- 多语言自动探针,Java,.NET Core和Node.JS。
- 多种监控手段,语言探针和service mesh。
- 轻量高效。不需要额外搭建大数据平台。
- 模块化架构。UI、存储、集群管理多种机制可选。
- 支持告警。
- 优秀的可视化效果。
OpenTracing中最核心的概念就是Trace
1、Trace
在广义上,一个trace代表了一个事务或者流程在(分布式)系统中的执行过程。在OpenTracing标准中,trace是多个span组成的一个有向无环图(DAG),每一个span代表trace中被命名并计时的连续性的执行片段。Trace表示一个调用链路,由全局唯一的TraceID标识。Trace由Span组成。
2、 Span
(一般)表示一个函数调用,由全局唯一的SpanID标识。Span组成的Trace实际上是一颗树结构,除了根Span外,每个Span都会有一个父Span。
Span里面的信息包括:操作的名字,开始时间和结束时间,可以附带多个 key:value 构成的 Tags(key
必须是String,value可以是 String, bool 或者数字),还可以附带 Logs 信息(不一定所有的实现都支持)
也是 key:value形式。
3、Metric和Tracing的区别
Metrics和Tracing属于开箱即用的一套API,其目的是为了监控、跟踪程序调用。下面看看二者的区别
Metric主要用来进行数据的统计,比如HTTP请求数的计算。 Metrics即度量指标。其原理是,将要监测的值记录在特定的全局变量中,然后通过HTTP的形式向外提供查询这些指标的接口,即Exporter。Prometheus会通过Exporter拉取这些数据,并存放在时序数据库中,可通过PromQL进行查询。
2、Tracing即链路跟踪。其目的是在分布式系统中,完成一个用户请求可能需要多个子系统之间的相互调用,而子系统为了实现高可用都会有多个节点,导致排查问题非常困难。Tracing相对于Metrics来说,实现会更复杂
通过注入agent的方式启动skywalking_mysql服务,注意skywalking_mysql要打成jar包
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent_mysql/skywalking-agent.jar -jar skywalking_mysql.jar
让Skywalking以指定appname的方式启动skywalking_mysql,最终的结果就是我们在skywalking后台可以看到多了一个skywalking_mysql服务
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent_mysql/skywalking-agent.jar -Dskywalking.agent.service_name=skywalking_mysql -jar skywalking_mysql.jar &
七、项目中获取traceId
<dependency>
<groupId>org.apache.skywalkinggroupId>
<artifactId>apm-toolkit-traceartifactId>
<version>${skywalking.version}version>
dependency>
引入这个就可以在系统中通过TraceContext.traceId() 来获取traceId,如果不引,只是在系统不能通过代码去获取,但是在skywalking也是可以采集到的。
有的端点不需要监控,比如Swagger相关的端点,要把他排除掉:
java -javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar
-Dskywalking.agent.service_name=skywalking_plugins
-Dskywalking.trace.ignore_path=/exclude jar skywalking_plugins.jar &
通过命令启动服务,Dskywalking.trace.ignore_path标识不监控的url
Skywalking每隔一段时间根据收集到的链路追踪的数据和配置的告警规则(如服务响应时间、服务响应时间百分比)等,判断如果达到阈值则发送相应的告警信息。发送告警信息是通过调用webhook接口完成,具体的webhook接口可以使用者自行定义,从而开发者可以在指定的webhook接口中编写各种告警方式,比如邮件、短信等。告警的信息也可以在RocketBot中查看到。
默认的告警规则配置,位于skywalking安装目录下的config文件夹下 alarm-settings.yml 文件中:
https://img-blog.csdnimg.cn/2db751d4c4de46f9b070d69ad12ecff9.png
比如定义:
- 最近3分钟内服务的平均响应时间超过1秒
- 最近2分钟服务成功率低于80%
- 最近3分钟90%服务响应时间超过1秒
- 最近2分钟内服务实例的平均响应时间超过1秒
@RestController
public class WebHooks {
private List<AlarmMessage> lastList = new ArrayList<>();
//产生告警时调用的接口
@PostMapping("/webhook")
public void webhook(@RequestBody List<AlarmMessage> alarmMessageList){
lastList = alarmMessageList;
}
//为了方便,新增一个接口展示告警的信息接口
@GetMapping("/show")
public List<AlarmMessage> show(){
return lastList;
}
}
注意一定要书写一个url=/webhook的接口
public class PreMainAgent {
/**
* 在这个 premain 函数中,开发者可以进行对类的各种操作。
* 1、agentArgs 是 premain 函数得到的程序参数,随同 “– javaagent”一起传入。与 main
函数不同的是,
* 这个参数是一个字符串而不是一个字符串数组,如果程序参数有多个,程序将自行解析这个字符串。
* 2、Inst 是一个 java.lang.instrument.Instrumentation 的实例,由 JVM 自动传入。*
* java.lang.instrument.Instrumentation 是 instrument 包中定义的一个接口,也是这
个包的核心部分,
* 集中了其中几乎所有的功能方法,例如类定义的转换和操作等等。
类中提供两个静态方法,方法名均为premain,不能拼错。
在pom文件中添加打包插件:
* @param agentArgs
* @param inst
*/
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("=========premain方法执行1========");
System.out.println(agentArgs);
}
/**
* 如果不存在 premain(String agentArgs, Instrumentation inst)
* 则会执行 premain(String agentArgs)
* @param agentArgs
*/
public static void premain(String agentArgs) {
System.out.println("=========premain方法执行2========");
System.out.println(agentArgs);
}
}
类中提供两个静态方法,方法名均为premain,不能拼错。
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-pluginartifactId>
<configuration>
<appendAssemblyId>falseappendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependenciesdescriptorRef>
descriptorRefs>
<archive>
<manifest>
<addClasspath>trueaddClasspath>
manifest>
<manifestEntries>
<Premain-Class>PreMainAgentPremain-Class>
<Agent-Class>PreMainAgentAgent-Class>
<Can-Redefine-Classes>trueCan-Redefine-Classes>
<Can-Retransform-Classes>trueCan-RetransformClasses>
manifestEntries>
archive>
configuration>
<executions>
<execution>
<id>make-assemblyid>
<phase>packagephase>
<goals>
<goal>singlegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
再把项目打包package,这样就得到一个agent的jar
在IDEA的VM options中添加代码
-javaagent:路径\java-agent-demo-1.0-SNAPSHOT.jar=HELLOAGENT
Byte Buddy致力于解决字节码操作和instrumentation API的复杂性。Byte Buddy提供了额外的API来生成Java agent,可以轻松的增强我们已有的代码。注意是增强代码,比如我们要统计方法调用时间,他可以通过类似于切面一样的东西增强我们的目标代码
在我们自己的agent项目中添加依赖
<dependencies>
<dependency>
<groupId>net.bytebuddygroupId>
<artifactId>byte-buddyartifactId>
<version>1.9.2version>
dependency>
<dependency>
<groupId>net.bytebuddygroupId>
<artifactId>byte-buddy-agentartifactId>
<version>1.9.2version>
dependency>
dependencies>
public class PreMainAgent {
public static void premain(String agentArgs, Instrumentation inst) {
//创建一个转换器,转换器可以修改类的实现
//ByteBuddy对java agent提供了转换器的实现,直接使用即可
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule javaModule) {
return builder
.method(ElementMatchers.<MethodDescription>any()) // 拦截任意方法
.intercept(MethodDelegation.to(MyInterceptor.class));//拦截到的方法委托给TimeInterceptor
}
};
//Byte Buddy专门有个AgentBuilder来处理Java Agent的场景
new AgentBuilder.Default()
.type(ElementMatchers.nameStartsWith("com.agent"))// 根据包名前缀拦截类
.transform(transformer)// 拦截到的类由transformer处理
.installOn(inst);
}
}
先生成一个转换器,ByteBuddy提供了java agent专用的转换器。转换器的条件如下
1、实现Transformer接口利用builder对象来创建一个转换器。转换器可以配置拦截方法的格式,比如用名称,本例中拦截所有方法
2、定义一个拦截器类MyInterceptor。创建完拦截器之后可以通过Byte Buddy的AgentBuilder建造者来构建一个agent对象。AgentBuilder可以对指定的包名前缀来生效,同时需要指定转换器对象。
public class MyInterceptor {
@RuntimeType
public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable)throws Exception {
long start = System.currentTimeMillis();
try {
//执行原方法
return callable.call();
} finally {
//打印调用时长
System.out.println(method.getName() + ":" + (System.currentTimeMillis() - start) + "ms");
}
}
}
MyInterceptor就是一个拦截器的实现,统计的调用的时长。参数中的method是反射出的方法对象,而callable就是调用对象,可以通过callable.call()方法来执行原方法。
链路追踪对比
https://blog.csdn.net/A123638/article/details/123117142
https://blog.csdn.net/z45351/article/details/125779241
日志采集:
https://blog.csdn.net/xiaoweite1/article/details/121865241
https://blog.csdn.net/ruocheng6/article/details/124415664