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 执行时间。

相关推荐
都叫我大帅哥4 分钟前
当Java遇上函数式编程:从“面条代码”到“代码诗人”的进化指南
java
都叫我大帅哥9 分钟前
Spring Batch中的JobRepository:批处理的“记忆大师”是如何工作的?🧠
java·spring
三好码农19 分钟前
深入Android 15 Zygote:ZygoteServer如何驾驭进程孵化
java·设计模式·架构
叶 落2 小时前
三种语言写 MCP
java·python·ai·typescript·mcp
桃子是唯一的水果2 小时前
spring碎片
java·spring
LUCIAZZZ2 小时前
项目拓展-Spring实现策略类统一管理
java·jvm·spring boot·后端·spring·操作系统
zhendeWD3 小时前
springboot 提供的可扩展接口
java·spring boot·后端·spring
PHASELESS4113 小时前
深入理解Spring MVC:构建灵活Web应用的基石
java·网络·后端·spring·mvc
Byte Beat3 小时前
springCloudAlibaba最新版本2023.0.3.2 与nacos3.0的连接不上问题记录
java·spring cloud·nacos
528304 小时前
容器技术入门与Docker环境部署
java·docker·容器