APM - Hello Javaagent

本文涉及的产品
应用实时监控服务-应用监控,每月50GB免费额度
简介: APM - Hello Javaagent

20200716134102384.png

什么是javaagent


简单来说, javaagent 是在class 被装在到ClassLoader之前对其拦截,插入自定义的监听字节码,可实现零侵入的监控,是APM的核心技术

Java1.5之后引入的特性

JavaAgent 运行在 main方法之前 ,内置的方法名为premain,即先执行premain方法,然后再执行main方法。通过premain方法,可实现一个JavaAgent。

javaagent 应用场景:监控、代码覆盖率分析 、JProfiler、应用破解等等等


javaagent的jar包 和 普通jar包的区别


javaagent 其实就是一个jar 包,通过-javaagent:xxx.jar 引入监控目标应用。那这个jar 和普通的jar 的区别在哪里呢?

我们来先看个结论

20200926075226650.png


从零搭建第一个javaagent

maven搭建 编译

【pom.xml】

 <?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">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.artisan</groupId>
    <artifactId>javaagent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <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>
                            <Premain-Class>com.artisan.ssist.JavaAgentDemo</Premain-Class>
                            <Boot-Class-Path>javassist-3.18.1-GA.jar</Boot-Class-Path>
                            <Can-Redefine-Classes>false</Can-Redefine-Classes>
                        </manifestEntries>
                    </archive>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.18.1-GA</version>
        </dependency>
    </dependencies>
</project>


  • Premain-Class:指定包含 premain 方法的类名 ,改成自己的类
  • Can-Redefine-Class:是否能重新定义此代理所需的类,默认为 false。

【Agent Code】

package com.artisan.ssist;
import java.lang.instrument.Instrumentation;
public class JavaAgentDemo {
    public  static void premain(String args ,Instrumentation instrumentation){
        System.out.println("premain  first agent demo");
    }
}


【编译成jar】


20200716141153637.png


点击 M, 执行 mvn clean package


20200716141519580.png

查看jar包中的 MANIFEST.MF文件 , MANIFEST.MF文件用于描述Jar包的信息,例如指定入口函数等。


20200716142217545.png

【引入agent jar 为当前应用启动前插入premain逻辑】

jvm参数指定

-javaagent:E:\IdeaProjects\javaagent\target\javaagent-1.0-SNAPSHOT.jar


20200716142610567.png


执行,观察我们引入的这个jar包中的premain方法是否优先于这个测试类的main方法执行


20200716142706429.png



OK ,这个就是Java Agent的 简单小栗子, 更强大的功能继续开篇


javaagent 流程示意图


20200926080457644.png


进阶Demo

public class AgentMain {
    public static void premain(String args, Instrumentation instrumentation)
            throws Exception, ClassNotFoundException {
        // 实例化对象
        UserService userService = new UserService();
        // 类比ClassLoader
        ClassPool classPool = new ClassPool();
        // 追加系统ClassLoader
        classPool.appendSystemPath();
        // 获取一个类
        CtClass ctClass = classPool.get("com.artisan.agent.UserService");
        // 获取方法
        CtMethod sayHello = ctClass.getDeclaredMethod("sayHello");
        // 在方法执行之后插入下面这行语句
        sayHello.insertAfter("System.out.println(\"I am fine\");");
        // 重新定义一个类
        instrumentation.redefineClasses(new ClassDefinition(UserService.class,ctClass.toBytecode()));
        // 调用服务   这里的userservice 已经是被重新定义的 对象了
        userService.sayHello();
    }


总结


1.instrumentation addTransformer 类装载拦截

2.只能拦截未装载过的类

3.instrumentation#retransformClasses方法 重新装载类 ,必须开启相关参数

4.instrumentation.redefineClasses 重新定义一个类 ,不能添加新方法 ,必须开启相关参数


开启参数


20200926092522977.png

agent 依懒包逗号分割
Boot-Class-Path: javassist-3.18.1-GA.jar
是否允许重定义
Can-Redefine-Classes: true
允许重载
Can-Retransform-Classes:true

Javassist 引入


既然是搞字节码,有没有类库 ?


其实上面的栗子 其实已经使用了Javassist 类库了~


Javassist是一个开源的分析、编辑和创建Java字节码的类库。


关于java字节码的处理, 目前有很多开源工具可用,比如asm,bcel, 不过这些都需要直接跟虚拟机指令打交道,实在是太难。。。。。


如果不想了解虚拟机指令,可以采用javassist。


javassist是jboss的一个子项目,优点简单 快速 ,直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

相关实践学习
通过轻量消息队列(原MNS)主题HTTP订阅+ARMS实现自定义数据多渠道告警
本场景将自定义告警信息同时分发至多个通知渠道的需求,例如短信、电子邮件及钉钉群组等。通过采用轻量消息队列(原 MNS)的主题模型的HTTP订阅方式,并结合应用实时监控服务提供的自定义集成能力,使得您能够以简便的配置方式实现上述多渠道同步通知的功能。
相关文章
|
NoSQL 关系型数据库 MySQL
多人同时导出 Excel 干崩服务器?怎样实现一个简单排队导出功能!
业务诉求:考虑到数据库数据日渐增多,导出会有全量数据的导出,多人同时导出可以会对服务性能造成影响,导出涉及到mysql查询的io操作,还涉及文件输入、输出流的io操作,所以对服务器的性能会影响的比较大;结合以上原因,对导出操作进行排队; 刚开始拿到这个需求,第一时间想到就是需要维护一个FIFO先进先出的队列,给定队列一个固定size,在队列里面的人进行排队进行数据导出,导出完成后立马出队列,下一个排队的人进行操作;还考虑到异步,可能还需要建个文件导出表,主要记录文件的导出情况,文件的存放地址,用户根据文件列表情况下载导出文件。
多人同时导出 Excel 干崩服务器?怎样实现一个简单排队导出功能!
|
移动开发 JavaScript 小程序
uView Tabbar 底部导航栏
uView Tabbar 底部导航栏
331 0
|
8月前
|
人工智能 数据可视化 数据处理
2025低代码前瞻:平台赋能的无限可能
低代码平台正逐渐成为企业数字化转型的核心工具,通过高效、灵活、智能的特点改变传统开发模式。展望2025年,低代码技术将推动可视化开发普及,支持全员参与应用构建;核心引擎升级,提升开发效率与灵活性;模型驱动开发更加成熟,实现自动化代码生成和智能逻辑优化;数据处理能力增强,应对复杂业务需求;AI深度融合,优化开发体验;插件生态丰富,覆盖多行业场景;架构更开放,支持开源与高性能需求;企业功能强化,赋能运营与决策。低代码平台不仅将成为开发工具,更是企业数字化生态的重要组成部分,为企业带来更高的效率、更低的成本和更快的创新能力。
2025低代码前瞻:平台赋能的无限可能
|
小程序 前端开发 程序员
微信小程序开发入门教程-小程序账号注册及开通
微信小程序开发入门教程-小程序账号注册及开通
|
Python
python导入错误(ImportError)
【5月更文挑战第2天】python导入错误(ImportError)
735 1
|
存储 测试技术 开发者
FastAPI异步处理的神奇之处:如何用Python打造高性能Web应用,让你的项目一鸣惊人?
【8月更文挑战第31天】在现代Web开发中,高性能至关重要。FastAPI作为一款高性能Python Web框架,支持多种异步处理方式,包括非阻塞I/O、异步函数(async/await)及异步上下文管理器(async with),能够大幅提升应用性能。本文通过示例代码详细介绍了FastAPI中的异步处理方法,并分享了最佳实践,帮助开发者构建高效的Web应用。
629 0
|
SQL 流计算
Flink SQL 在快手实践问题之CUMULATE窗口的划分逻辑如何解决
Flink SQL 在快手实践问题之CUMULATE窗口的划分逻辑如何解决
240 2
|
存储 前端开发 Java
SpringMVC 文件上传和下载
SpringMVC 文件上传和下载
147 0
|
缓存 并行计算 Java
重温JAVA线程池精髓:Executor、ExecutorService及Executors的源码剖析与应用指南
重温JAVA线程池精髓:Executor、ExecutorService及Executors的源码剖析与应用指南
|
消息中间件 Shell
rabbitmq安装erlang环境后没生效
rabbitmq安装erlang环境后没生效
1237 7