Skywalking插件开发Java agent
Java agent
1、Java agent是什么
Java agent是java命令的一个参数。参数javaagent可以用于指定一个jar包。
- 这个jar包的MAINFEST.MF文件必须指定Premain-Class项。
- Premain-Class指定的那个类必须实现premain()方法。
当Java虚拟机启动时,在执行main函数之前,JVM会先运行-javaagent所指定jar包内Premain-Class这个类的premain方法。
2、如何使用Java agent
2.1、步骤
使用java agent 需要几个步骤:
- 定义一个MANIFEST.MF文件,必须包含Premain-Class选项,通常也会加入Can-Redefi
ne-Classes和Can-Retransform-Classes选项。 - 创建一个Premain-Class指定的类,类中包含premain方法,方法逻辑由户自己确定。
- 将premain的类和MANIFEST.MF文件打成jar包。
- 使用参数-javaagent: jar包路径启动要代理的方法。
2.2、premain()方法的参数
premain()方法包括两个参数:
- String agentArgs这个参数就是agent接收到的参数
- Instrumentation inst 这个参数能操作一些类的方法,提供了一些类转化的方法
public static void premain(String agentArgs, Instrumentation inst); //【1】
public static void premain(String agentArgs); //【2】
其中,【1】和【2】同时存在时,【1】会优先被执行,而【2】则会被忽略。
具体使用如下代码:
import java.lang.instrument.Instrumentation;
public class MyAgent {public static void premain(String agentArgs, Instrumentation inst) {System.out.println("this is an agent.");System.out.println("args:" + agentArgs + "\n");}
}
2.3、MANIFEST.MF文件
首先,需要在代码的resources目录下添加META-INF/MANIFEST.MF文件。其目的是指定Premain-Class的类。
Manifest-Version: 1.0
Premain-Class: agent.MyAgent
Can-Redefine-Classes: true
2.4、pom.xml文件中配置打包的相关配置
pom.xml文件中添加打包插件和打包的相关配置,指定Premain-Class:
<build><plugins><plugin><artifactId>maven-assembly-plugin</artifactId><configuration><appendAssemblyId>false</appendAssemblyId><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs><archive><manifest><addClasspath>true</addClasspath></manifest><manifestEntries><Premain-Class>PreMainAgent</Premain-Class><Agent-Class>PreMainAgent</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries></archive></configuration><executions><execution><id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin></plugins></build>
注释:maven-assembly-plugin是maven的一个组装插件
在PreMainAgent中要指定实现premain()方法的类
jar-with-dependencies依赖打包,jar包会包含所有依赖
true自动生成MANIFEST.MF文件
2.5、jar打包
执行mvn clean package,就能生成一个my-agent.jar
2.6、Agent运行
使用如下设置运行Agent
-javaagent: 文件位置 [=参数]
备注:如果是运行多个agent,进行罗列即可。-javaagent: 文件位置 [=参数] -javaagent: 文件位置 [=参数],其中的参数就是premain函数中的agentArgs。
2.7、Agent使用
①首先,新建一个测试类
public class AgentTest {public static void main(String[] args) {System.out.println("this is main");}
}
②命令行运行:java -javaagent: 文件位置 [=参数]
idea运行:如果是Idea中,在VM options中配置。
-javaagent: 文件位置 [=参数]
2.8、结果
this is an agent.
args:firstthis is main
3、ByteBuddy
3.1、ByteBuddy是什么
Byte Buddy是开源的、基于Apache2.0许可证的库,它致力于解決字节码操作和instrumentation API的复杂性,ByteBuddy所声称的目标是将显式的字节码操作隐藏在一个类型安全的领域特定语言背后,通过使用ByteBuddy,任何熟java编程语言的人都有望非常容易地进行字节码操作,ByteBuddy提供了额外的API来生成javaagent,可以轻松的増强我们已有的代码
3.2、为什么要用ByteBuddy
例如之前我们要对方法的调用时间来进行统计,是利用spring的AOP技术来进行统计的,但是由于探针并没有集成spring类似的框架,所以不能利用这个技术来实现,这就需要对字节码进行增强,增强后,通过拦截器进行拦截,从而达到统计方法时间的目的。
3.3、添加依赖
引入ByteBuddy的依赖
<dependencies>
<dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy</artifactId><version>1.9.2</version>
</dependency>
<dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy-agent</artifactId><version>1.9.2</version>
</dependency>
</dependencies>
3.4、实现一个Agent
在premain处进行处理,通过AgentBuilder方法,生成一个Agent。这里有两点需要特别说明:其一是在AgentBuilder.type处,这里可以指定需要拦截的类;其二是在builder.method处,这里可以指定需要拦截的方法。当然其API支持各种isStatic、isPublic等等一系列方式。
public class MyAgent {public static void premain(String agentArgs, Instrumentation inst) {System.out.println("this is an perform monitor agent.");AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {@Overridepublic DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,TypeDescription typeDescription,ClassLoader classLoader) {return builder.method(ElementMatchers.<MethodDescription>any()) // 拦截任意方法.intercept(MethodDelegation.to(TimeInterceptor.class)); // 委托}};new AgentBuilder.Default().type(ElementMatchers.nameStartsWith("com.example.demo")) // 指定需要拦截的类.transform(transformer).with(listener).installOn(inst);}
}
3.5、实现一个用来委托的Interceptor
上一步实现Transformer的过程中,委托了一个TimeInterceptor.class。下面是其实现方式,整个的try语句是原有的代码执行,我们在之前打了时间戳,并在其结束后,计算并打印了其调用耗时。
public class TimeInterceptor {@RuntimeTypepublic static Object intercept(@Origin Method method,@SuperCall Callable<?> callable) throws Exception {long start = System.currentTimeMillis();try {// 原有函数执行return callable.call();} finally {System.out.println(method + ": took " + (System.currentTimeMillis() - start) + "ms");}}
}
3.6、运行
这里需要注意的是,我们定义的包路径要和Agent中定义的相同,否则Agent无法Hook到这个类及其方法。
package com.example.demo;public class AgentTest {private void fun1() throws Exception {System.out.println("this is fun 1.");Thread.sleep(500);}private void fun2() throws Exception {System.out.println("this is fun 2.");Thread.sleep(500);}public static void main(String[] args) throws Exception {AgentTest test = new AgentTest();test.fun1();test.fun2();}
}
3.7、运行结果
可以看到,我们的Agent成功Hook并增强了其调用方法。
this is an perform monitor agent.
this is fun 1.
private void com.example.demo.AgentTest.fun1() throws java.lang.Exception: took 501ms
this is fun 2.
private void com.example.demo.AgentTest.fun2() throws java.lang.Exception: took 500ms
public static void com.example.demo.AgentTest.main(java.lang.String[]) throws java.lang.Exception: took 1001ms
4、使用Java-Agent实现一个JVM监控工具
4.1、实现一个Agent
Agent的构造比较简单,我们只需要在premain方法中,加入一个线程池。其功能是每隔5秒输出一次内存信息和GC信息。
public class MyAgent {public static void premain(String agentArgs, Instrumentation inst) {System.out.println("this is an perform monitor agent.");Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {public void run() {Metric.printMemoryInfo();Metric.printGCInfo();}}, 0, 5000, TimeUnit.MILLISECONDS);}
}
4.2、获取JVM 内存以及GC信息
public class Metric {private static final long MB = 1048576L;public static void printMemoryInfo() {MemoryMXBean memory = ManagementFactory.getMemoryMXBean();MemoryUsage headMemory = memory.getHeapMemoryUsage();//head堆、初始(M)、最大(上限)(M)、当前(已使用)(M)、提交的内存(已申请)(M)、使用率String info = String.format("\ninit: %s\t max: %s\t used: %s\t committed: %s\t use rate: %s\n",headMemory.getInit() / MB + "MB",headMemory.getMax() / MB + "MB", headMemory.getUsed() / MB + "MB",headMemory.getCommitted() / MB + "MB",headMemory.getUsed() * 100 / headMemory.getCommitted() + "%");System.out.print(info);MemoryUsage nonheadMemory = memory.getNonHeapMemoryUsage();non-head非堆、初始(M)、最大(上限)(M)、当前(已使用)(M)、提交的内存(已申请)(M)、使用率info = String.format("init: %s\t max: %s\t used: %s\t committed: %s\t use rate: %s\n",nonheadMemory.getInit() / MB + "MB",nonheadMemory.getMax() / MB + "MB", nonheadMemory.getUsed() / MB + "MB",nonheadMemory.getCommitted() / MB + "MB",nonheadMemory.getUsed() * 100 / nonheadMemory.getCommitted() + "%");System.out.println(info);}public static void printGCInfo() {//垃圾收集器、名称、次数、时间、内存区名称List<GarbageCollectorMXBean> garbages = ManagementFactory.getGarbageCollectorMXBeans();for (GarbageCollectorMXBean garbage : garbages) {String info = String.format("name: %s\t count:%s\t took:%s\t pool name:%s",garbage.getName(),garbage.getCollectionCount(),garbage.getCollectionTime(),Arrays.deepToString(garbage.getMemoryPoolNames()));System.out.println(info);}}
}
4.3、运行
public class AgentTest {public static void main(String[] args) throws Exception {boolean is = true;while (is) {List<Object> list = new ArrayList<Object>();list.add("hello world");}}
}
4.4、运行结果
init: 256MB max: 3641MB used: 2MB committed: 245MB use rate: 1%
init: 2MB max: 0MB used: 5MB committed: 7MB use rate: 72%name: PS Scavenge count:0 took:0 pool name:[PS Eden Space, PS Survivor Space]
name: PS MarkSweep count:0 took:0 pool name:[PS Eden Space, PS Survivor Space, PS Old Gen]init: 256MB max: 3641MB used: 8MB committed: 245MB use rate: 3%
init: 2MB max: 0MB used: 5MB committed: 7MB use rate: 77%name: PS Scavenge count:0 took:0 pool name:[PS Eden Space, PS Survivor Space]
name: PS MarkSweep count:0 took:0 pool name:[PS Eden Space, PS Survivor Space, PS Old Gen]
Skywalking插件开发Java agent相关推荐
- SkyWalking之安装Java agent
参考官方文档: document-cn-translation-of-skywalking/README.md at master · SkyAPM/document-cn-translation-o ...
- Apache SkyWalking Java Agent 05-插件加载机制(下)
基于 SkyWalking Java Agent 8.8.0 版本 上一篇文章中我们重点分析了自定义类加载器 AgentClassLoader.initDefaultLoader() 部分,Agent ...
- Skywalking(11): java agent原理
1 java agent原理 上文中我们知道,要使用Skywalking去监控服务,需要在其 VM 参数中添加 "-javaagent:/usr/local/skywalking/apach ...
- 搭建skywalking对Java应用进行监控
APM概述 APM (Application Performance Management) 即应用性能管理系统,是对企业系统即时监控以实现 对应用程序性能管理和故障管理的系统化的解决方案.应用性能管 ...
- 写那么多年Java,还不知道啥是Java agent 的必须看一下!
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者信息:张帅,花名洵澈,国际化中台事业部高级开发工程师,负责物流 ...
- Skywalking应用实战 Agent探针、Rocketbot以及告警
3 Skywalking应用 相关术语: skywalking-collector:链路数据归集器,数据可以落地ElasticSearch/H2 skywalking-ui:web可视化平台,用来展示 ...
- 基于Java Agent实现APM
一.APM概述 APM系统(Application Performance Management,即应用性能管理),用于对应用系统做实时监控,目的是实现对应用性能管理和故障定位. 1.1.为什么需要A ...
- Java 探针技术:java agent
什么是java agent? 在JVM中运行中,类是通过classLoader加载.class文件进行生成的.在类加载器加载.class文件生成对应的类对象之前时,我们可以通过修改.class文件内容 ...
- 字节码插桩之Java Agent
字节码插桩之Java Agent 本篇文章将详细讲解有关Java Agent的知识,揭开它神秘的面纱,帮助开发人员了解它的黑魔法,帮助我们完成更多业务需求 What is Java Agent Jav ...
最新文章
- 【疯狂的消化之旅】消化系统简介
- 轮滑---1、动作和杂记
- boost::gil::static_transform用法的测试程序
- 后端选型中不同语言及对应的Web框架
- .NET Core开发实战(第35课:MediatR:让领域事件处理更加优雅)--学习笔记
- 聊聊rocketmq的ConsumerManageProcessor
- Digital root(数根)
- AspNetPager使用方法
- ebs oracle pl sql开发_ORACLEERP开发基础之EBS开发基础
- Echarts数据可视化series-map地图,开发全解+完美注释
- 腾讯电脑管家怎么阻止软件自动安装
- paypal无法提现?最新解决办法(实战教程)!
- 抖音服务器升级中无法修改名字,抖音名字改不了怎么回事
- matplotlib 配色之内置 colormap
- ps怎么将图片制作成ico图标? ps制作ico图标的教程
- LabVIEW32位和64位的兼容性
- Sa-Token中接口的限流
- 哥尼斯堡的“七桥问题“(并查集)
- 飞机躲子弹小游戏案例
- [预训练语言模型专题] 银色独角兽GPT家族
热门文章
- 键盘鼠标显示器是计算机硬件吗,计算机系统由硬件和软件两部分组成。键盘、鼠标、显示器等都是计算机的硬件。 答案:√...
- 100盏灯泡的开关问题
- OpenTelemetry系列 (三)| 神秘的采集器 - Opentelemetry Collector
- sublime text 3支持中文输入法
- Android APP之间的跳转
- DB307S-ASEMI整流桥DB307S
- JAVA学习实例(一)——三个数排序
- [esp32 + LVGL]物理按键控制屏幕上开关和LED闪烁
- qrcode生成二维码插件
- php 实现邮件定时发送,PHP 定时发送邮件