Java线上解决方案1:JAVA热更新

线上问题的解决一直是java程序员头疼的一个问题。有的应用很敏感,例如游戏行业,可能需要做到1个月都不能停服,那线上问题出了问题怎么办呢?谁也不能保证更新线上的逻辑百分百不出问题,这就需要我们在不停应用的情况下在线解决。随着技术的逐渐成熟,java社区也逐渐提供了一些线上解决方案,比如说下面3个方面:

  • Java热更新(热部署):不停应用的情况下,动态热更java的类,以替换线上运行逻辑
  • Java代码片段执行:就是编写一段代码,然后可以线上执行。可用于恢复某些异常数据,或者执行某些规整等。当然如果代码做一些调整,也可以做到代码的替换执行,略等于代码热更新
  • Java在线Debug:在线上打断点,当逻辑执行到断点之后,打印当前的线程、调用堆栈、当前类的成员变量、当前行的局部变量等信息,一切就和在本地debug一样。关键是不会影响线上的运行逻辑

本篇主要介绍方案1:Java热更新(热部署)

顺便提一句:阿里的arthas框架的热更新就是用的这个方式

Instrumentation功能

从JDK6开始,Java提供了一个新特性:Instrumentation功能,虽然这个接口包含的内容不多,但功能却很强大,这个接口主要提供了2个核心方法:

  1. retransformClasses():类似给method()穿了一层外衣,把内容content给覆盖了,这样每次执行都是执行的外衣的内容,既然是衣服,则可以脱掉removeTransformer(),这样代码又恢复原始状态。一般用这个方法来进行动态方法注入。
  2. redefineClasses(): 直接修改了方法内容content

当然这2个修改类的实现方式是有限制的,例如不允许修改方法签名,不允许增加方法参数等等。

有关于Instrumentation,网上介绍也比较多,有兴趣的朋友可以再深入研究下,许多知名的开源框架都是基于这个类进行动态的代码修改和注入的,比如阿里著名的arthas、jvm-sanbox,去哪儿旅行网的bistoury等等。这里由于篇幅,就不展开叙说了。

如何进行Java热更新呢

有了Instrumentation的接口,那如何调用它呢?简单点说,我们如何获取Instrumentation的实现?这里就不得不提到JDK的“代理”(agent)。JDK的agent简单点就是说在应用启动前或者应用运行时,JDK可以加载外部的一个agent包的代码,来动态修改或增强现有的代码逻辑。Instrumentation就是在agent过程中,由JVM提供的,通过 Instrumentation就可以修改代码。

Agent的两种方式:

  • jvm启动前:premain方式,必须在命令行指定代理jar,并且代理类必须在main方法前启动,它要求开发者在应用启动前就必须确认代理的处理逻辑和参数内容等等
  • jvm启动后:agentmain方式,在应用程序的VM启动后再动态添加代理的方式

因为我们需要热更线上代码,所以需要采用agentmain的方式,这种方式需要提供一个agent jar,并且这个jar需要满足2个条件:

  1. 在manifest中指定Agent-Class属性,值为代理类全路径
  2. 代理类需要提供public static void agentmain(String args, Instrumentation inst)或public static void agentmain(String args)方法。并且再二者同时存在时以前者优先。args和inst和premain中的一致。

如何加载Agent的jar包呢

通过JDK提供的tools.jar(存放在$JAVA_HOME/lib/tools.jar),里面有个VirtualMachine类,代码如下:

VirtualMachine vm = VirtualMachine.attach(当前进程ID);
vm.loadAgent("对应Agent的jar包路径");

注意:tools.jar在windows和linux环境下是不同的,所以如果程序跑在Linux下,需要添加Linux的tools.jar

如果Agent的jar包符合上面所说的2个条件,则虚拟机loadAgent的时候会调用到agentmain()方法

总结下代码Java热更新流程

  1. 首先项目需要添加当前进程对应的jdk的tools.jar包,因为 tools.jar提供了VirtualMachine类
  2. VirtualMachine类在加载agent的jar包时会触发agentmain方法,这个方法里面提供了Instrumentation
  3. 程序获取到Instrumentation之后,可以通过它进行代码的redefine或者retransform,从而实现对代码的热更新

最后

说了半天,大家可能想说的是:谁想听你瞎BB,我们更想知道怎么用。好吧,下面贴出了源码和使用说明

源码下载

使用说明

  • 添加依赖:$JAVA_HOME/lib/tools.jar、javaagent-1.0.1.jar
  • 把JavaAgent类放到自己的项目中
  • 调用JavaAgent类的 javaAgent(String[] classArr)方法,输入class的全路径即可热更此类

观云

本博客所有文章均为独立创作。 原创不易,转载请注明来源并保留原文链接,转载自观云 | yeas.fun

You may also like...

发表评论

电子邮件地址不会被公开。 必填项已用*标注