APM - 使用JavaAgent+Javassit 插桩C3P0

本文涉及的产品
应用实时监控服务-应用监控,每月50GB免费额度
简介: APM - 使用JavaAgent+Javassit 插桩C3P0

202010081834167.png

核心思想

<bean id="dataSource"  
        class="com.mchange.v2.c3p0.ComboPooledDataSource"  
......


很熟悉吧,我们要插桩的这个对象就是 ComboPooledDataSource ,

如何很直观的展示出来的

  1. 实时获取ComboPooledDataSource的属性信息
  2. 简洁直观的展示相关属性信息


实现第一个,那就在ComboPooledDataSource构造函数后下手,将ComboPooledDataSource放到System Properties 中, 然后开启一个HTTP服务,对外提供服务访问即可。


编码实现


package com.artisan.agent.collect.c3p0;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.net.InetSocketAddress;
import java.security.ProtectionDomain;
import java.util.concurrent.Executors;
/**
 * @author 小工匠
 * @version 1.0
 * @description: C3P0插桩
 * @date 2020/8/29 9:15
 * @mark: show me the code , change the world
 */
public class C3P0Agent {
    // 要插装的类
    static String targetClass = "com.mchange.v2.c3p0.ComboPooledDataSource";
    public static void premain(String args , Instrumentation instrumentation){
        // 类转换器处理插桩逻辑
        instrumentation.addTransformer(new ClassFileTransformer() {
            public byte[] transform(ClassLoader loader,
                                    String className,
                                    Class<?> classBeingRedefined,
                                    ProtectionDomain protectionDomain,
                                    byte[] classfileBuffer)   {
                //  插桩后的对象
                byte[] result = null;
                // 条件判断
                if (className != null && className.replace("/", ".").equals(targetClass)){
                    // 实例化pool , 将当前的ClassLoader设置到pool
                    ClassPool pool = new ClassPool();
                    pool.insertClassPath(new LoaderClassPath(loader));
                    try {
                        // 获取目标对象
                        CtClass ctl = pool.get(targetClass);
                        // 获取构造函数,插桩  将对象放入系统属性中
                        ctl.getConstructor("()V") //构造函数
                                .insertAfter("System.getProperties().put(\"c3p0Source$agent\", $0);"); // $0 this本身
                        // 转成class
                        result = ctl.toBytecode();
                        // 暴漏HTTP服务
                        new C3P0Agent().openHttpServer();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                return result;
            }
        });
    }
    /**
     * 对外提供Http 服务展示DataSource当前状态
     * @throws IOException
     */
    private void openHttpServer() throws IOException {
        InetSocketAddress addr = new InetSocketAddress(7777);
        HttpServer server = HttpServer.create(addr, 0);
        // 设置上下文
        server.createContext("/", new MyHttpHandler());
        server.setExecutor(Executors.newCachedThreadPool());
        server.start();
    }
    private class MyHttpHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            Headers responseHeaders = exchange.getResponseHeaders();
            responseHeaders.set("Content-Type", "text/plain;charset=UTF-8");
            exchange.sendResponseHeaders(200, 0);
            OutputStream responseBody = exchange.getResponseBody();
            // 输出c3p0状态
            responseBody.write(C3P0Agent.this.getStatus().getBytes());
            responseBody.flush();
            responseBody.close();
        }
    }
    public String getStatus() {
        Object source2 = System.getProperties().get("c3p0Source$agent");
        if (source2 == null) {
            return "未初始任何c3p0数据源";
        }
        return source2.toString();
    }
}


打包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>artisan-apm</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>apm-agent</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.18.1-GA</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Project-name>${project.name}</Project-name>
                            <Project-version>${project.version}</Project-version>
                            <Boot-Class-Path>javassist-3.18.1-GA.jar</Boot-Class-Path>
                            <Premain-Class>com.artisan.agent.collect.c3p0.C3P0Agent</Premain-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <includes>
                                    <include>org.javassist:javassist</include>
                                </includes>
                            </artifactSet>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.artisan.agent.collect.c3p0.C3P0Agent</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>


打包的时候,连同javassist也打包进去


20200929211304815.png


20200929211404195.png


20200929231342235.png


配置验证

在启动的JVM参数中配置如下信息

-javaagent:D:\IdeaProjects\artisan-apm\apm-agent\target\apm-agent-1.0-SNAPSHOT.jar


20200929231254764.png

然后,访问 http:// localhost:7777


20200929231359934.png

相关实践学习
通过轻量消息队列(原MNS)主题HTTP订阅+ARMS实现自定义数据多渠道告警
本场景将自定义告警信息同时分发至多个通知渠道的需求,例如短信、电子邮件及钉钉群组等。通过采用轻量消息队列(原 MNS)的主题模型的HTTP订阅方式,并结合应用实时监控服务提供的自定义集成能力,使得您能够以简便的配置方式实现上述多渠道同步通知的功能。
相关文章
|
监控 Java
Idea+Jconsole实现线程监控
Idea+Jconsole实现线程监控
675 0
|
Shell Android开发
解决Android的adb命令行报错Permission denied
解决Android的adb命令行报错Permission denied
2475 0
解决Android的adb命令行报错Permission denied
Element的el-table行列错位对不齐问题处理
本文目录 1. 问题表现 2. 问题发现 3. 问题处理 4. 另一种处理方案
5372 0
Element的el-table行列错位对不齐问题处理
|
3月前
|
安全 物联网 API
Windows 11 24H2 中文版、英文版 (x64、ARM64) 下载 (2025 年 9 月更新)
Windows 11 24H2 中文版、英文版 (x64、ARM64) 下载 (2025 年 9 月更新)
562 1
Windows 11 24H2 中文版、英文版 (x64、ARM64) 下载 (2025 年 9 月更新)
|
9月前
|
Arthas 监控 Java
拥抱 OpenTelemetry:阿里云 Java Agent 演进实践
拥抱 OpenTelemetry:阿里云 Java Agent 演进实践
436 0
|
敏捷开发 前端开发 Java
敏捷开发与Scrum管理:提高团队效率的策略
【10月更文挑战第25天】敏捷开发与Scrum管理是现代软件开发的重要方法,通过灵活的迭代和高效的团队协作提升项目成功率。本文介绍如何运用敏捷开发和Scrum管理提高团队效率,并提供实际案例,如通过Sprint发布新的API接口,以及角色分工和会议设置等具体实践。
286 2
|
10月前
|
人工智能 开发框架 数据可视化
Eino:字节跳动开源基于Golang的AI应用开发框架,组件化设计助力构建AI应用
Eino 是字节跳动开源的大模型应用开发框架,帮助开发者高效构建基于大模型的 AI 应用。支持组件化设计、流式处理和可视化开发工具。
1587 27
|
Java API 持续交付
apache nifi 如何进行二次开发?
【10月更文挑战第23天】apache nifi 如何进行二次开发?
630 2
|
存储 监控 Go
面向OpenTelemetry的Golang应用无侵入插桩技术
文章主要讲述了阿里云 ARMS 团队与程序语言与编译器团队合作研发的面向OpenTelemetry的Golang应用无侵入插桩技术解决方案,旨在解决Golang应用监控的挑战。