SkyWalking java-agent 是如何工作的,自己实现一个监控sql执行耗时的agent

Apache SkyWalking 是一个开源的应用性能监控 (APM) 工具,支持分布式系统的追踪、监控和诊断。SkyWalking Agent 是其中的一个重要组件,用于在服务端应用中收集性能数据和追踪信息,并将其发送到 SkyWalking 后端服务器进行处理和展示。

SkyWalking Agent 的工作原理

  1. 启动时加载 Agent

    SkyWalking Agent 通过 Java Agent 机制,在 JVM 启动时加载。用户需要在启动应用时添加 -javaagent 参数,指定 SkyWalking Agent 的 JAR 文件。

    java -javaagent:/path/to/skywalking-agent.jar -jar your-application.jar
    
  2. 字节码增强

    SkyWalking Agent 使用字节码增强技术(基于字节码操作库如 ASM)来修改应用程序的类文件,以插入监控代码。这些代码会在方法调用、数据库访问、HTTP 请求等关键点收集性能数据。

  3. 拦截方法调用

    通过字节码增强,Agent 拦截应用程序中的特定方法调用(如 HTTP 请求、数据库查询等)。在方法开始前、方法结束后和异常抛出时,Agent 会记录相关信息。

  4. 收集性能数据和追踪信息

    拦截的方法调用会生成追踪数据,这些数据包括:

    • 方法执行的开始时间和结束时间
    • 方法执行的耗时
    • 调用链上下文信息(如 Trace ID、Span ID)
    • 方法的输入输出参数和异常信息
  5. 数据缓冲和发送

    收集到的数据会暂时存储在 Agent 的内存中,并定期批量发送到 SkyWalking 后端服务器。为了减少对应用程序性能的影响,数据发送通常是异步进行的。

  6. 后端处理和展示

    SkyWalking 后端服务器接收到数据后,会对其进行处理、存储,并在 Web UI 中展示。用户可以通过 Web UI 查看服务的调用链、性能指标、错误信息等。

SkyWalking Agent 的关键组件

  • Agent Core:负责 Agent 的初始化、配置加载和生命周期管理。
  • Plugin System:SkyWalking Agent 提供了插件系统,支持不同类型的 middleware、framework 的插件,如 HTTP、Dubbo、Spring、MyBatis 等。这些插件定义了如何拦截特定的框架方法,收集性能数据。
  • Tracing Context:管理调用链上下文,包括 Trace ID 和 Span ID 的生成和传递。
  • Reporter:负责将收集到的数据发送到 SkyWalking 后端服务器。

自己实现一个java agent

Java Agent 是 Java Instrumentation API 的一个强大功能,它允许你在运行时修改 Java 应用程序的行为。它们通常用于性能分析、监控和修改应用程序的执行。

自己实现一个 实现一个 Java Agent 来监控 JDBC SQL 执行时间

步骤 1:创建 Java Agent

  1. 创建 Agent 类:
    该类将实现 premain 方法来进行类的插桩。
java 复制代码
import java.lang.instrument.Instrumentation;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;

public class SqlTimingAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("SQL Timing Agent loaded");
        inst.addTransformer(new SqlTimingTransformer());
    }
}
  1. 创建 Transformer 类:
    该类将对 java.sql.Statementjava.sql.PreparedStatementexecute* 方法进行插桩。
java 复制代码
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
import java.lang.instrument.IllegalClassFormatException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

public class SqlTimingTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        if (className.equals("java/sql/Statement") || className.equals("java/sql/PreparedStatement")) {
            return transformSQLClass(className, classfileBuffer);
        }
        return classfileBuffer;
    }

    private byte[] transformSQLClass(String className, byte[] classfileBuffer) {
        try {
            ClassPool cp = ClassPool.getDefault();
            CtClass cc = cp.get(className.replace("/", "."));
            for (CtMethod m : cc.getDeclaredMethods()) {
                if (m.getName().startsWith("execute")) {
                    addTiming(m);
                }
            }
            return cc.toBytecode();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return classfileBuffer;
    }

    private void addTiming(CtMethod method) throws Exception {
        method.addLocalVariable("startTime", CtClass.longType);
        method.insertBefore("startTime = System.currentTimeMillis();");
        method.insertAfter("System.out.println(\"SQL Execute Time: \" + (System.currentTimeMillis() - startTime) + \" ms\");");
    }
}

步骤 2:创建 Manifest 文件

创建一个 MANIFEST.MF 文件来指定代理类。

java 复制代码
Manifest-Version: 1.0
Premain-Class: SqlTimingAgent

步骤 3:将 Agent 打包为 JAR

编译你的代理类和 Transformer 类,并将它们与 manifest 文件一起打包到 JAR 文件中。

java 复制代码
javac SqlTimingAgent.java SqlTimingTransformer.java
jar cfm SqlTimingAgent.jar MANIFEST.MF SqlTimingAgent.class SqlTimingTransformer.class

步骤 4:使用 Java Agent

在启动 Java 应用程序时使用 -javaagent 选项指定代理 JAR 文件。

java 复制代码
java -javaagent:SqlTimingAgent.jar -jar YourApplication.jar
复制代码

示例解释

  • SqlTimingAgent

    • premain 方法在 JVM 启动时被调用,添加 SqlTimingTransformer 作为类文件转换器。
  • SqlTimingTransformer

    • 该类实现了 ClassFileTransformer 接口,对 java.sql.Statementjava.sql.PreparedStatement 类进行转换。
    • transform 方法中,检查类名是否是 StatementPreparedStatement,并调用 transformSQLClass 方法。
    • transformSQLClass 方法使用 Javassist 库修改类的字节码,对 execute* 方法添加时间记录代码。
  • 字节码修改

    • execute* 方法开始前记录当前时间。
    • 在方法结束后,计算并输出 SQL 执行时间。

通过这些步骤,你可以创建和使用 Java Agent 来监控 JDBC SQL 执行时间。

相关推荐
找了一圈尾巴10 分钟前
Wend看源码-Java-Collections 工具集学习
java·开发语言·学习
广而不精zhu小白3 小时前
CentOS Stream 9 安装 JDK
java·linux·centos
程序员云帆哥3 小时前
【玩转23种Java设计模式】行为型模式篇:命令模式
java·设计模式·命令模式
赵谨言3 小时前
基于 Java 大数据的旅游推荐系统的设计与实现
java·经验分享·毕业设计
NHuan^_^4 小时前
RabbitMQ基础篇之Java客户端 Topic交换机
java·rabbitmq·java-rabbitmq
中國移动丶移不动4 小时前
Java List 源码解析——从基础到深度剖析
java·后端·list
Java知识技术分享5 小时前
spring boot 异步线程池的使用
java·spring boot·spring
m0_748232925 小时前
Springboot3.x整合swagger3
java
东林知识库5 小时前
Mysql高级
java·mysql
m0_748248235 小时前
Springboot项目:使用MockMvc测试get和post接口(含单个和多个请求参数场景)
java·spring boot·后端