ssm如何支持热部署_Java 调式、热部署、JVM 背后的支持者 Java Agent

2e0144f09b185cd27e7cd22f3aacc215.png
我们平时写 Java Agent 的机会确实不多,也可以说几乎用不着。但其实我们一直在用它,而且接触的机会非常多。下面这些技术都使用了 Java Agent 技术,看一下你就知道为什么了。

-各个 Java IDE 的调试功能,例如 eclipse、IntelliJ ;

-热部署功能,例如 JRebel、XRebel、 spring-loaded;

-各种线上诊断工具,例如 Btrace、Greys,还有阿里的 Arthas;

-各种性能分析工具,例如 Visual VM、JConsole 等;

Java Agent 直译过来叫做 Java 代理,还有另一种称呼叫做 Java 探针。首先说 Java Agent 是一个 jar 包,只不过这个 jar 包不能独立运行,它需要依附到我们的目标 JVM 进程中。我们来理解一下这两种叫法。

代理:比方说我们需要了解目标 JVM 的一些运行指标,我们可以通过 Java Agent 来实现,这样看来它就是一个代理的效果,我们最后拿到的指标是目标 JVM ,但是我们是通过 Java Agent 来获取的,对于目标 JVM 来说,它就像是一个代理;

探针:这个说法我感觉非常形象,JVM 一旦跑起来,对于外界来说,它就是一个黑盒。而 Java Agent 可以像一支针一样插到 JVM 内部,探到我们想要的东西,并且可以注入东西进去。

拿上面的几个我们平时会用到的技术举例子。拿 IDEA 调试器来说吧,当开启调试功能后,在 debugger 面板中可以看到当前上下文变量的结构和内容,还可以在 watches 面板中运行一些简单的代码,比如取值赋值等操作。还有 Btrace、Arthas 这些线上排查问题的工具,比方说有接口没有按预期的返回结果,但日志又没有错误,这时,我们只要清楚方法的所在包名、类名、方法名等,不用修改部署服务,就能查到调用的参数、返回值、异常等信息。

上面只是说到了探测的功能,而热部署功能那就不仅仅是探测这么简单了。热部署的意思就是说再不重启服务的情况下,保证最新的代码逻辑在服务生效。当我们修改某个类后,通过 Java Agent 的 instrument 机制,把之前的字节码替换为新代码所对应的字节码。

Java Agent 结构

1f004820ebef4339e9e6227e11ab6790.png

Java Agent 最终以 jar 包的形式存在。主要包含两个部分,一部分是实现代码,一部分是配置文件。

配置文件放在 META-INF 目录下,文件名为 MANIFEST.MF 。包括以下配置项:

Manifest-Version: 版本号 Created-By: 创作者 Agent-Class: agentmain 方法所在类 Can-Redefine-Classes: 是否可以实现类的重定义 Can-Retransform-Classes: 是否可以实现字节码替换 Premain-Class: premain 方法所在类

入口类实现 agentmainpremain 两个方法即可,方法要实现什么功能就由你的需求决定了。

Java Agent 实现和使用

接下来就来实现一个简单的 Java Agent,基于 Java 1.8,主要实现两点简单的功能:

1、打印当前加载的所有类的名称;

2、监控一个特定的方法,在方法中动态插入简单的代码并获取方法返回值;

在方法中插入代码主要是用到了字节码修改技术,字节码修改技术主要有 javassist、ASM,已经 ASM 的高级封装可扩展 cglib,这个例子中用的是 javassist。所以需要引入相关的 maven 包。

<dependency><groupId>javassist</groupId><artifactId>javassist</artifactId><version>3.12.1.GA</version>
</dependency>

实现入口类和功能逻辑

入口类上面也说了,要实现 agentmainpremain 两个方法。这两个方法的运行时机不一样。这要从 Java Agent 的使用方式来说了,Java Agent 有两种启动方式,一种是以 JVM 启动参数 -javaagent:xxx.jar 的形式随着 JVM 一起启动,这种情况下,会调用 premain方法,并且是在主进程的 main方法之前执行。另外一种是以 loadAgent 方法动态 attach 到目标 JVM 上,这种情况下,会执行 agentmain方法。

代码实现如下:

package kite.lab.custom.agent;import java.lang.instrument.Instrumentation;public class MyCustomAgent {/*** jvm 参数形式启动,运行此方法* @param agentArgs* @param inst*/public static void premain(String agentArgs, Instrumentation inst){System.out.println("premain");customLogic(inst);}/*** 动态 attach 方式启动,运行此方法* @param agentArgs* @param inst*/public static void agentmain(String agentArgs, Instrumentation inst){System.out.println("agentmain");customLogic(inst);}/*** 打印所有已加载的类名称* 修改字节码* @param inst*/private static void customLogic(Instrumentation inst){inst.addTransformer(new MyTransformer(), true);Class[] classes = inst.getAllLoadedClasses();for(Class cls :classes){System.out.println(cls.getName());}}
}

我们看到这两个方法都有参数 agentArgs 和 inst,其中 agentArgs 是我们启动 Java Agent 时带进来的参数,比如-javaagent:xxx.jar agentArgs。Instrumentation Java 开放出来的专门用于字节码修改和程序监控的实现。我们要实现的打印已加载类和修改字节码也就是基于它来实现的。其中 inst.getAllLoadedClasses()一个方法就实现了获取所以已加载类的功能。

inst.addTransformer方法则是实现字节码修改的关键,后面的参数就是实现字节码修改的实现类,代码如下:

public class MyTransformer implements ClassFileTransformer {@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {System.out.println("正在加载类:"+ className);if (!"kite/attachapi/Person".equals(className)){return classfileBuffer;}CtClass cl = null;try {ClassPool classPool = ClassPool.getDefault();cl = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));CtMethod ctMethod = cl.getDeclaredMethod("test");System.out.println("获取方法名称:"+ ctMethod.getName());ctMethod.insertBefore("System.out.println(" 动态插入的打印语句 ");");ctMethod.insertAfter("System.out.println($_);");byte[] transformed = cl.toBytecode();return transformed;}catch (Exception e){e.printStackTrace();}return classfileBuffer;}
}

以上代码的逻辑就是当碰到加载的类是 kite.attachapi.Person的时候,在其中的 test 方法开始时插入一条打印语句,打印内容是"动态插入的打印语句",在test方法结尾处,打印返回值,其中$_就是返回值,这是 javassist 里特定的标示符。

MANIFEST.MF 配置文件

在目录 resources/META-INF/ 下创建文件名为 MANIFEST.MF 的文件,在其中加入如下的配置内容:

Manifest-Version: 1.0
Created-By: fengzheng
Agent-Class: kite.lab.custom.agent.MyCustomAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Premain-Class: kite.lab.custom.agent.MyCustomAgent

配置打包所需的 pom 设置

最后 Java Agent 是以 jar 包的形式存在,所以最后一步就是将上面的内容打到一个 jar 包里。

在 pom 文件中加入以下配置

<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><configuration><archive><manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile></archive><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs></configuration></plugin></plugins>
</build>

用的是 maven 的 maven-assembly-plugin 插件,注意其中要用 manifestFile 指定 MANIFEST.MF 所在路径,然后指定 jar-with-dependencies ,将依赖包打进去。

上面这是一种打包方式,需要单独的 MANIFEST.MF 配合,还有一种方式,不需要在项目中单独的添加 MANIFEST.MF 配置文件,完全在 pom 文件中配置上即可。

<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><executions><execution><goals><goal>attached</goal></goals><phase>package</phase><configuration><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs><archive><manifestEntries><Premain-Class>kite.agent.vmargsmethod.MyAgent</Premain-Class><Agent-Class>kite.agent.vmargsmethod.MyAgent</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries></archive></configuration></execution></executions></plugin></plugins>
</build>

这种方式是将 MANIFEST.MF 的内容全部写作 pom 配置中,打包的时候就会自动将配置信息生成 MANIFEST.MF 配置文件打进包里。

运行打包命令

接下来就简单了,执行一条 maven 命令即可。

mvn assembly:assembly

最后打出来的 jar 包默认是以「项目名称-版本号-jar-with-dependencies.jar」这样的格式生成到 target 目录下。

运行打包好的 Java Agent

首先写一个简单的测试项目,用来作为目标 JVM,稍后会以两种方式将 Java Agent 挂到这个测试项目上。

package kite.attachapi;import java.util.Scanner;public class RunJvm {public static void main(String[] args){System.out.println("按数字键 1 调用测试方法");while (true) {Scanner reader = new Scanner(System.in);int number = reader.nextInt();if(number==1){Person person = new Person();person.test();}}}
}

以上只有一个简单的 main 方法,用 while 的方式保证线程不退出,并且在输入数字 1 的时候,调用 person.test()方法。

以下是 Person 类

package kite.attachapi;public class Person {public String test(){System.out.println("执行测试方法");return "I'm ok";}
}

以命令行的方式运行

因为项目是在 IDEA 里创建的,为了省事儿,我就直接在 IDEA 的 「Run/Debug Configurations」里加参数了。

-javaagent:/java-agent路径/lab-custom-agent-1.0-SNAPSHOT-jar-with-dependencies.jar

16e4d562d98f0e47049130ceece5c89f.png

然后直接运行就可以看到效果了,会看到加载的类名称。然后输入数字键 "1",会看到字节码修改后的内容。

351dae3ba4fce8c497118cb43bad36d7.png

以动态 attach 的方式运行

测试之前先要把这个测试项目跑起来,并把之前的参数去掉。运行后,找到这个它的进程id,一般利用jps -l即可。

动态 attach 的方式是需要代码实现的,实现代码如下:

public class AttachAgent {public static void main(String[] args) throws Exception{VirtualMachine vm = VirtualMachine.attach("pid(进程号)");vm.loadAgent("java-agent路径/lab-custom-agent-1.0-SNAPSHOT-jar-with-dependencies.jar");}
}

运行上面的 main 方法 并在测试程序中输入“1”,会得到上图同样的结果。

发现了没,我们到这里实现的简单的功能是不是和 BTrace 和 Arthas 有点像呢。我们拦截了指定的一个方法,并在这个方法里插入了代码而且拿到了返回结果。如果把方法名称变成可配置项,并且把返回结果保存到一个公共位置,例如一个内存数据库,是不是我们就可以像 Arthas 那样轻松的检测线上问题了呢。当然了,Arthas 要复杂的多,但原理是一样的。

sun.management.Agent 的实现

不知道你平时有没有用过 visualVM 或者 JConsole 之类的工具,其实,它们就是用了 management-agent.jar 这个Java Agent 来实现的。如果我们希望 Java 服务允许远程查看 JVM 信息,往往会配置上一下这些参数:

-Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=192.168.1.1
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.rmi.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

这些参数都是 management-agent.jar 定义的。

我们进到 management-agent.jar 包下,看到只有一个 MANIFEST.MF 配置文件,配置内容为:

Manifest-Version: 1.0
Created-By: 1.7.0_07 (Oracle Corporation)
Agent-Class: sun.management.Agent
Premain-Class: sun.management.Agent

可以看到入口 class 为 sun.management.Agent,进到这个类里面可以找到 agentmain 和 premain,并可以看到它们的逻辑。在这个类的开始,能看到我们前面对服务开启远程 JVM 监控需要开启的那些参数定义。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/344164.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

war3必须安装的游戏组件_在单独的WAR组件中对SPA资源和API实现进行分区

war3必须安装的游戏组件单页应用程序正Swift吸引人们的注意力&#xff0c;以实现基于Web的丰富&#xff0c;健壮和移动友好的应用程序。 从本质上讲&#xff0c;这需要更改应用程序体系结构&#xff0c;在该体系结构中&#xff0c;整个应用程序用户界面都是使用JavaScript来实现…

[渝粤教育] 西南科技大学 西方经济学 在线考试复习资料

西方经济学——在线考试复习资料 一、单选题 1.市场失灵指的是( ) A.在私人部门和公共部门之间资源配置不均 B.不能产生任何有用成果的市场过程 C.以市场为基础的对稀缺资源的无效率配置 D.收入分配不均 2.通常由总成本曲线的变动而引起的通货膨胀称为( ) A.需求拉动型通货膨胀…

[渝粤教育] 西南科技大学 财务管理与分析 在线考试复习资料(4)

财务管理与分析——在线考试复习资料 一、单选题 1.某投资方案年营业收入240万元,年销售成本170万元,其中折旧70万元,所得税率40%,则该方案年营业现金净流量为( )。 A.70万元 B.112万元 C.140万元 D.84万元 2.如果投资规模不同的两个独立投资项目的评价,应优先选择( )。 A.净现…

避免在Cassandra中使用清单

Apache Cassandra是一种快速且可扩展的数据库&#xff0c;多年来&#xff0c;它变得与传统SQL数据库一样容易使用。 至少在表面上。 您使用类似SQL的查询&#xff0c;但是它们有很多限制&#xff1b; 您有一个模式&#xff0c;但是修改它并不像在SQL数据库中那样灵活&#xff…

matlab 矩阵jocobi迭代_高校MATLAB被禁用,掀起中国本土软件脆弱的冰山一角

近日哈工大、哈工程高校被美国禁止用正版的MATLAB软件&#xff0c;此消息一出可谓使得全体用过MATLAB的工作者为之震撼。因为少了这款软件&#xff0c;无异于缺失了有力的左膀右臂&#xff0c;如何才能游刃有余地遨游在科研和工作领域呢&#xff1f;然而对于外行人却不知道这款…

[渝粤教育] 西南科技大学 高级英语(2) 在线考试复习资料

高级英语&#xff08;2&#xff09;——在线考试复习资料 一、单选题 1. The speaker went on and on, she had to ________ herself to stay awake. A. punch B. pink C. pinch D. pin 2. The two daughters’ sweet words _______ King Lear into believing that they reall…

【渝粤教育】电大中专学前儿童发展心理学作业 题库

1学前儿童心理学是()的分支。 A发展心理学 B儿童发展心理学 C儿童教育心理学 D儿童心理学 正确 正确答案&#xff1a;左边查询 学生答案&#xff1a;A 2提出儿童的心理最初只是一块白板的是()。 A卢梭 B洛克 C福禄贝尔 D维果斯基 错误 正确答案&#xff1a;左边查询 学生答案&a…

controll层跳转页面_java web开发中的各种层

java后端开发前&#xff0c;还是应该将各种层划分好&#xff0c;知道各自的作用&#xff0c;今天做一下总结。Dao层、Entity层、Service层、Controller层、view层1、Dao层(持久层、数据访问层)功能&#xff1a;只负责与数据库的数据进行交互&#xff0c;dao层是在Mybatis框架下…

【渝粤教育】电大中专审计原理与实务_1作业 题库

1.下列关于国家审计的表述&#xff0c;正确的是() A.是对综合经济管理部门和专业监督部门的监督工作实施再监督 B.审计主体是中央审计机关&#xff0c;不包括地方审计机关 C.只对财政预算执行的情况和决算进行审计 D.审计机关做出的审计决定&#xff0c;被审计单位和有关人员可…

【渝粤教育】电大中专幼儿园课程论 (1)作业 题库

作业视频教务托管&#xff0c;壹叁路路贰陆陆壹〇肆〇 显性课程是 ( )的课程 A.指定的 B.随机的 C.无计划的 D.有计划的 错误 正确答案&#xff1a;左边查询 学生答案&#xff1a;未作答 2.幼儿园课程最核心的要素是( ) A.教育方法 B.教育理念 C.教学原则 D.课程内容 错误 正确…

java 内存同步_Java中的硬件事务性内存,或者为什么同步将再次变得很棒

java 内存同步总览 硬件事务内存有可能允许多个线程同时以推测方式访问相同的数据结构&#xff0c;并使缓存一致性协议确定是否发生冲突。 HTM旨在为您提供细粒度锁定的可伸缩性&#xff0c;粗粒度锁定的简单性以及几乎没有锁定的性能。 如果JVM支持&#xff0c;则您的程序或库…

图像的灰度级数越多越好_MATLAB-数字图像处理 图像直方图归一化

图像直方图归一化图像直方图概念&#xff1a;图像直方图是反映一个图像像素分布的统计表&#xff0c;其实横坐标代表了图像像素的种类&#xff0c;可以是灰度的&#xff0c;也可以是彩色的。纵坐标代表了每一种颜色值在图像中的像素总数或者占所有像素个数的百分比。图像是由像…

POE交换机隐藏指标是什么?

POE交换机有一个非常重要的隐藏指标&#xff0c;只有大神才知道&#xff01;我只告诉您一次哦&#xff01;记住喽&#xff01;接下来跟着飞畅科技的小编一起来看看POE交换机隐藏指标是什么吧&#xff01; POE交换机隐藏指标是什么&#xff1f; POE交换机一个非常重要的隐藏指标…

【渝粤教育】电大中专新媒体营销实务 (11)作业 题库

1.第四媒体发展到宽带互联网阶段成为&#xff08; &#xff09; A.网络新媒体 B.移动新媒体 C.数字新媒体 错误 正确答案&#xff1a;左边查询 学生答案&#xff1a;未作答 2.新媒体形式是指新媒体的使用模式、变现形式与应用形式。该说法&#xff08; &#xff09; A.正确 B.错…

JDK 13:什么是AggressiveOpts?

JVM 11中已弃用Java VM标志-XX:AggressiveOpts [请参见JDK-8199777和JDK-8199778 ]“因为其行为是不明确的。” JDK-8199778的“问题”部分进一步说明了&#xff08;我添加了重点 &#xff09;&#xff1a; AggressiveOpts已被用作实现各种实验性能功能的包罗万象的方法&#…

【渝粤教育】电大中专消费者行为学 (3)作业 题库

1.消费行为学的研究对象是市场商品交换活动中消费者各种心理现象和行为的产生、发展及其规律。主要表现在以下哪几方面&#xff08;&#xff09; A.消费活动中营销环境 B.以上选项全选 C.消费者行为的发展变化趋势 D.消费者在消费过程中的行为和规律 E.消费者在购买行为中的心理…

擦地机器人修理_自带眼睛还有嘴,喷水式擦地机器人效果实测

小时候家里大扫除时&#xff0c;我最喜欢抢着扫把扫地&#xff0c;因为扫地相对比较轻松&#xff0c;快速扫完灰尘和垃圾就能跑出去玩。而擦地板相比扫地就麻烦多了&#xff0c;大面积的地板需要蹲下来一块一块擦&#xff0c;还要来回清洗擦布&#xff0c;现在父母年龄大了&…

PoE交换机不供电了该怎么办?

交换机供电方式有很多&#xff0c;有集中供电&#xff0c;独立供电&#xff0c;220V交流电供电&#xff0c;PoE供电&#xff0c;随着不同场合的使用&#xff0c;特别是poe交换机使用非常多&#xff0c;但是出现的问题也不少。其中比较常见的就是PoE交换机供电突然不供电了&…

【渝粤教育】电大中专电大中专沟通技巧考试考核试题 (2)作业 题库

1可以通过&#xff08; &#xff09;的方法来对抗讲话时的紧张感。 A心理暗示 B自我鼓励 C运用合适的肢体语言 D以上方法均可 错误 正确答案&#xff1a;左边查询 学生答案&#xff1a;A 2以下说法正确的是&#xff08; &#xff09; A沟通要有明确的目标 B沟通不需要形成协议 …

pv实现前趋图_Excel 数据透视图实现简易交互式数据面板

美国Ebay公司成立于1995年的圣何塞&#xff0c;是一家可让全球民众上网买卖物品的线上拍卖及购物网站。本文将结合Ebay&#xff08;某类体育用品的&#xff09;在线拍卖数据&#xff0c;主要使用Excel数据透视表为工具&#xff0c;来实现简易的交互式数据面板以求对数据进行可视…