内容简介:文中代码示例工程如下,更多参考btrace和arthas:原创文章,转载注明出处 (http://sayhiai.com)5版本以后,jdk有一个包叫做
文中代码示例工程如下,更多参考btrace和arthas:
https://github.com/sayhiai/example-javaagent
原创文章,转载注明出处 (http://sayhiai.com)
5版本以后,jdk有一个包叫做 instrument
,能够实现一些 非常酷 的功能。市面上一些 APM
工具,就是通过它来进行的增强。
这是基础架构的必备技能,但对业务开发来说并不是。许多面试会问到这个知识点,并不是因为将来会用到,而是因为你说对 jdk
比较熟悉,他想杀杀你的威风。
不会用没问题,但你要说不知道,就过分了点。
javaagent介绍
我们通常的 java 入口都是一个 main
方法,而 javaagent
的入口方法叫做 premain
,表明是在main运行之前的一些操作。javaagent就是一个jar包,定义了一个标准的premain()方法,并不需要继承或者实现任何其他的类。
这是一个约定,并木有什么其他的理由。这个方法,无论是第一次加载,还是每次新的ClassLoader加载,都会执行。
我们可以在这个前置的方法里,对字节码进行一些修改,来增加功能或者改变代码的行为。这种方法没有侵入性,只需要在启动命令中加上-javaagent参数就可以。Java6以后,甚至可以通过attach的方式,动态的给运行中的程序设置加载代理类。
有经验的同学肯定要提出异议了。其实,instrument有两个main方法,一个是 premain
,一个是 agentmain
,在一个JVM中,只会调用一个;前者是main执行之前的修改,后者控制类运行时的行为。它们还是有一些区别的,agentmain因为比较危险,限制会更大一些。
有什么用
获取统计信息
许多apm产品,比如Pinpoint、SkyWalking等,就是使用javaagent对代码进行的增强。通过在方法执行前后动态加入的统计代码,进行监控信息的收集;通过兼容OpenTracing协议,可以实现分布式链路追踪的功能。
它的原理类似于aop,最终以字节码存在,性能损失取决于你的代码逻辑。
热部署
通过自定义的ClassLoader,可以实现代码的热替换。使用agentmain,实现热部署功能会更加便捷。通过agentmain获取到Instrumentation以后,就可以对类进行动态重定义。
诊断
配合 JVMTI
技术,可以 attach
到某个进程进行运行时统计和调试,比较流行的 btrace
和 arthas
,底层就是这种技术。
如何做
大体分为以下步骤:
- 构建agent jar包,编写增强代码
- 在manifest中指定Premain-Class/Agent-Class属性
- 使用参数加载或者attach方式使用
编写Agent
javaagent最终的体现方式是一个jar包。使用idea创建一个默认的maven工程即可。
创建一个普通java类,添加 premain
或者 agentmain
方法,它们的参数完全一样。
编写Transformer
此部分,要借助额外jar包的功能。
实际的代码逻辑需要实现 ClassFileTransformer
接口。假如我们要统计某个方法的执行时间。我们使用 javaassist
来增强字节码,则可以通过以下代码来实现。
MainRun hello _begin
别忘了加入maven依赖
<dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.24.1-GA</version> </dependency>
字节码增强也可以使用Cglib、asm等其他工具。
MANIFEST.MF文件
那么我们编写的代码是如何让外界知晓呢?那就是 MANIFEST.MF
文件。具体路径在
src/main/resources/META-INF/MANIFEST.MF
Manifest-Version: 1.0 premain-class: com.sayhiai.example.javaagent.AgentApp
一般的,maven打包会覆盖这个文件,所以我们需要指定需要哪一个。
<build><plugins><plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile> </archive> </configuration></plugin></plugins></build>
然后,在命令行,执行 mvn install
安装到本地代码库,或者使用 mvn deploy
发布到私服上。
附,MANIFEST.MF参数清单:
Premain-Class
Agent-Class
Boot-Class-Path
Can-Redefine-Classes
Can-Retransform-Classes
Can-Set-Native-Method-Prefix
使用
使用方式取决于你使用的premain还是agentmain。
premain
直接在启动命令行中加入参数即可,在jvm启动时启用代理。
java -javaagent:agent.jar MainRun
在idea中,可以将参数附着在jvm options里。
接下来看一下测试代码。
这是我们的执行类。执行后,直接输出hello world。通过增强以后,还额外的输出了执行时间,以及一些debug信息。其中,debug信息在main方法执行之前输出。
agentmain
一般用在一些诊断 工具 上。使用jdk/lib/tools.jar中的功能,可以动态的为运行中的程序加入功能。主要有以下步骤:
- 获取机器上运行的所有jvm的进程id
- 选择要诊断的jvm
- 将jvm使用attach函数链接上
- 使用loadAgent函数加载agent,动态修改字节码
- 卸载jvm
这些代码都是比较危险的,这就是为什么Btrace说了这么多年,还是只在小范围内被小心使用。相对来说,arthas显的友好而且安全的多。
注意点
一、jar包依赖方式
一般,agent的jar包会以fatjar的方式提供,即将所有的依赖打包到一个大的jar包中。
如果你的功能复杂,依赖多,那么这个jar包将会特别的大。
使用独立的bom文件维护这些依赖是另外一种方法。使用方自行管理依赖问题,但这通常会发生一些找不到jar包的错误。更糟糕的是,大多数在运行时才发现。
二、类名称重复
不要使用和jdk以及instrument包中相同的类名(包括包名),有时候你能够侥幸过关,但也会陷入无法控制的异常中。
三、做有限的功能
可以看到,给系统动态的增加功能是非常酷的,但大多数情况下非常耗费性能。你会发现,一些简单的诊断工具,占用你1核的cpu,是稀松平常的事情。
四、ClassLoader
如果你用的jvm比较旧,频繁的生成大量的代理类,会造成perm区的膨胀,容易发生OOM。
ClassLoader有双亲委派机制,如果你想要替换相应的类,一定要搞清楚它的类加载器应该用哪个。否则替换的类,是不生效的哦。
End
将你的增强代码,加入类似zk的主动通知功能,可以通过管理后台动态的调整应用的行为。如果再集成一个类似groovy的脚本语言,理论上,你能够干任何事情。
所以,使用 -javaagent
参数引入的 jar
包,或者使用 attach
方式提供的一些诊断工具,小姐姐都不敢随便使用。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- WEB 前端冷门知识
- 一些冷门但却非常好用的Python数据科学库
- 涨涨姿势 13 个比较冷门但很实用的编程语言
- 涨涨姿势 13 个比较冷门但很实用的编程语言
- Go语言实现本地脚本功能,带服务器功能
- 禅道 9.8.stable 发布,增强待办功能和消息通知功能
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Developing Large Web Applications
Kyle Loudon / Yahoo Press / 2010-3-15 / USD 34.99
As web applications grow, so do the challenges. These applications need to live up to demanding performance requirements, and be reliable around the clock every day of the year. And they need to withs......一起来看看 《Developing Large Web Applications》 这本书的介绍吧!
HTML 编码/解码
HTML 编码/解码
SHA 加密
SHA 加密工具