javaagent很普遍的运行在apm系统中。他可以通过字节码注入等方式实现指标的暴露。下面主要讲一些主流的用法。
运行暴露的java代码
javaagent可以在目标java进程上执行额外的java代码,这个就给了我们很大的想象空间。我们可以新启动一个线程,执行一些操作,完全实现额外的功能。例如开启http端口。一个典型的应用是jmx_exporter。
jmx_exporter是一个开源项目,他的主要功能就是把jmx的数据转化成prometheus的格式,并且通过http协议进行指标暴露。这就是一个典型的利用javaagent执行额外代码的例子。我们看看他的启动方式。
public static void agentmain(String agentArgument, Instrumentation instrumentation) throws Exception {
premain(agentArgument, instrumentation);
}
public static void premain(String agentArgument, Instrumentation instrumentation) throws Exception {
// Bind to all interfaces by default (this includes IPv6).
String host = "0.0.0.0";
try {
Config config = parseConfig(agentArgument, host);
new BuildInfoCollector().register();
new JmxCollector(new File(config.file)).register();
DefaultExports.initialize();
server = new HTTPServer(config.socket, CollectorRegistry.defaultRegistry, true);
}
catch (IllegalArgumentException e) {
System.err.println("Usage: -javaagent:/path/to/JavaAgent.jar=[host:]<port>:<yaml configuration file> " + e.getMessage());
System.exit(1);
}
}
上面的操作很简单,这里要注意两个方法。一个是agentmain,一个是premain。premain是通过-javaagent启动参数是会调用的方法,这个方法在main方法之前运行。这就有个要求,就是premain中执行的代码不能时间太长,否则会阻塞main方法的运行,如果代码比较多的话,建议开启线程执行。
agentmain方法,是通过vm attach进程的时候执行的方法。这个就没有顺序的这个要求。
我们看上面的操作在HTTPServer就结束了,后面的操作都是异步执行的了,http访问的时候,进行数据的拉取。
bci
bci操作有很多框架,例如javassist,asm。jvmti提供了一种方式在类加载的时候进行事件拦截。
new ClassFileTransformer() {
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {}
}
我们可以编写一个ClassFileTransformer,只要是类加载就会触发transform。我们就可以拿到加载的字节码。然后利用字节码工具,进行修改以后,然后加载到内存。
我们利用addTransformer有两个选择。就是第二个参数,支持retransform。
inst.addTransformer(new ClassFileTransformer() {},true}
这里是retransform和redefine的操作是否触发ClassFileTransformer。
这里是有一个限制的。
The retransformation may change method bodies, the constant pool and attributes. The retransformation must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance. These restrictions maybe be lifted in future versions. The class file bytes are not checked, verified and installed until after the transformations have been applied, if the resultant bytes are in error this method will throw an exception.
retransform的操作可以改方法体,常量,但是不能增加删除方法和成员变量。也就是说我们不能直接加方法代理。如果不是retransform则没有这种限制。
方法代理,改变方法
public void hello(){
int i =0;
}
第一步重命名
public void helloxx(){
int i =0;
}
然后重新加一个方法
public void hello(){
int j =0;
helloxx();
}
以此达到一个代理的手段。
如果是retransform,则必须直接改完。
public void hello(){
int j =0;
int i =0;
}
做retransform的时候,对字节码的框架操作必须更熟练。
共同学习,写下你的评论
评论加载中...
作者其他优质文章