分布式链路追踪-SkyWalking
为什么需要链路追踪
在这个微服务系统中,用户通过浏览器的 H5页面访问系统,这个用 户请求会先抵达微服务网关组件,然后网关再把请求分发给各个微 服务。所以你会发现,用户请求从发起到结束要经历 很多个微服务 的处理,这里面还涉及到消息组件的集成。
存在的问题:
服务之间的依赖与被依赖的关系如何能够清晰的看到?
出现异常时如何能够快速定位到异常服务?
出现性能瓶颈时如何能够迅速定位哪个服务影响的?
链路
可以将链路理解为物理电路图
链路和电路图都有起点和终点。
链路和电路图都通过可视化得技术手段,来模拟真实组件得运行轨迹。
链路和电路图都非常复杂,并且随着系统复杂程度得增加,其可视化复杂度也会成几何倍数增加。
解决:
为了能够在分布式架构中快速定位问题,分布式链路追踪应运 而生。将一次分布式请求还原成调用链路,进行日志记录,性 能监控并将一次分布式请求的调用情况集中展示。比如各个服 务节点上的耗时、请求具体到达哪台机器上、每个服务节点的 请求状态等等。
常见链路追踪技术有那些
市面上有很多链路追踪的项目,其中也不乏一些优秀的,如下:
**Sleuth:**SpringCloud 提供的分布式系统中链路追踪解决方 案。很可惜的是阿里系并没有链路追踪相关的开源项目,我们可 以采用Spring Cloud Sleuth+Zipkin来做链路追踪的解决方案。
**zipkin:**由Twitter公司开源,开放源代码分布式的跟踪系统, 用于收集服务的定时数据,以解决微服务架构中的延迟问题,包 括:数据的收集、存储、查找和展现。该产品结合 spring-cloud-sleuth 使用较为简单, 集成很方便, 但是功能较简单。
**pinpoint:**韩国人开源的基于字节码注入的调用链分析,以及 应用监控分析工具。特点是支持多种插件,UI功能强大,接入端 无代码侵入
**Skywalking:**SkyWalking是本土开源的基于字节码注入的调用 链分析,以及应用监控分析工具。特点是支持多种插件,UI功能 较强,接入端无代码侵入。目前已加入Apache孵化器。
认识SkyWalking
什么是SkyWalking
SkyWalking是开源的可观测平台,主要用于收集,分析,聚合和可 视化服务和云原生基础设施的数据。Skywalking提供了一种简单得 方式来维护分布式系统得视图关系。
SkyWalking
SkyWalking是开源的一款分布式追踪,分析,告警的工具,现已属 于Apache旗下开源项目, SkyWalking为服务提供了自动探针代理, 将数据通过gRPC或者HTTP传输给后端平台,后端平台将数据存储 在Storage中,并且分析数据将结果展示在UI中。
优点:
多种监控手段多语言自动探针,Java,.NET Core 和 Node.JS
轻量高效,不需要大数据
模块化,UI、存储、集群管理多种机制可选
支持告警
社区活跃
缺点:
较为新兴,成熟度不够高
SkyWalking核心概念
上部分 Agent :
负责从应用中,收集链路信息,发送给 SkyWalking OAP 服务器。目前支持 SkyWalking、Zikpin、Jaeger 等提供的 Tracing 数据信息。而我们目前采用的是, SkyWalking Agent 收集 SkyWalking Tracing 数据,传递给服务器。
下部分 SkyWalking OAP :
负责接收 Agent 发送的 Tracing 数据信息,然后进行分析 (Analysis Core) ,存储到外部存储器( Storage ),最终提供查询( Query )功能。
左部分 Storage :
Tracing 数据存储。目前支持 ES、MySQL、Sharding Sphere、TiDB、 H2 多种存储器。而我们目前采用的是 ES ,主要考虑是 SkyWalking 开发团队自己的生产环 境采用 ES 为主。
右部分 SkyWalking UI :
负责提供控台,查看链路等等。
什么是探针Java Agent
探针产生的背景
在开发过程中,开发人员经常会使用IDEA的Debug功能(包含本地 和远程)调试应用,在JVM进程期间获取应用运行的JVM信息,变量 信息等。这些个技术通过Java Agent来实现的,那么Java Agent到 底是啥,为啥这么吊?
哪里有Java Agent:
idea得Debug功能
热部署 JRebel
各种线上诊断工具
代码覆盖率工具
性能分析
什么是探针
Java Agent 又叫做 Java 探针,是在 JDK1.5 引入的一种可以动态修改 Java 字节码的技术。Java 类编译之后形成字节码被 JVM 执行, 在 JVM 在执行这些字节码之前获取这些字节码信息,并且通过字节 码转换器对这些字节码进行修改,来完成一些额外的功能。
Java探针工具技术原理
流程:
在JVM加载class二进制文件的时候,利用ASM动态的修改加载的class文件,在监控的方法前 后添加计时器功能,用于计算监控方法耗时;
将监控的相关方法 和 耗时及内部调用情况,按照顺序放入处理器;
处理器利用栈先进后出的特点对方法调用先后顺序做处理,当一个请求处理结束后,将耗时 方法轨迹和入参map输出到文件中;
然后区分出耗时的业务,转化为xml格式进行解析和分析。
Java探针日志监控实现
环境搭建
引入依赖
<dependencies>
<!-- springboot 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
编写HelloController
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello java agent";
}
}
打包SpringBoot项目
探针实现
如何使用Java Agent
JDK1.5引入了java.lang.instrument包,开发者可以很方便的实现字 节码增强。其核心功能由java.lang.instrument.Instrumentation接 口提供。
Instrumentation 有两种使用方式:
在应用运行之前,通过premain()方法来实现,在应用启动时侵入并代理应用。
在应用运行之后,通过调用Attach AP和agentmain()方法来实现
注意:
javassist是一个库,实现ClassFileTransformer接口中的 transform()方法。ClassFileTransformer 这个接口的目的就是 在class被装载到JVM之前将class字节码转换掉,从而达到动态 注入代码的目的。
新建Maven项目javaagentdemo
引入依赖
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-GA</version>
</dependency>
</dependencies>
<build>
<finalName>agent</finalName>
<plugins>
<!--META-INF 下 MANIFEST.MF 文件 内容Manifest-Version: 1.0Premain-Class: com.jenson.TestAgent下面Maven插件可以自动实现-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Premain-Class>com.org.PreMainJavaAgent</Premain-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Maven插件引入失败
在IDEA中使用maven-shade-plugin插件时会提示"Plugin ‘mavenshade-plugin:’ not found"
解决方法:
File -> Setting -> Build, Execution, Deployment -> Build Tools -> Maven -> 勾选Use plugin
registry选项 File -> Invalidate Caches -> Invalidate and Restart
编写探针类TestAgent
public class PreMainJavaAgent {
/**
* 在main方法运行前,与main方法运行于同一JVM中
*
* @param agentArgs :agentArgs是premain函数得到的程序参数,随同"-javaagent"一同传入,
* 与main函数不同的是,该参数是一个字符串而不是一个字符串数组,
* 如果程序参数有多个,程序将自行解析这个字符串.
* @param instrumentation :一个java.lang.instrument.Instrumentation实例,由JVM自动传入,
* java.lang.instrument.Instrumentation是instrument包中的一个接口,
* 也是核心部分,集中了其中几乎所有的功能方法,例如类定义的转换和操作.
*/
public static void premain(String agentArgs, Instrumentation instrumentation) {
System.out.println("------------------ premain -----------------------");
instrumentation.addTransformer(new TestTransformer());
}
编写TestTransformer
public class TestTransformer implements ClassFileTransformer {
//增强类
private final String CLASSNAME="com.org.controller.HelloController";
//增强方法
private final String METHODNAME="hello";
//这个接口的目的就是在class被装载到jvm之前将class字节码文件转换掉,从而达到动态注入代码的目的。
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
//输出模式:com/org/controller/HelloController
// System.out.println("className = " + className);
//将className 的格式转换成 idea中文件路径形式
String finalClassName = className.replace("/", "."