背景
在排查线上问题时,经常发现是低级错误引起,类似于NPE、变量名写错了等等。对于这种问题,我们往往只需要改动一两行代码就能验证并修复问题。实际过程中,我们需要重新打包工程,然后走一遍发布流程。在处理线上问题的时候,等待发布页会变得很漫长,运气不好,可能发布完后发现问题并没有解决,就会非常崩溃。这就需要一种方便的方式,可以直接修改运行中的代码,让我们能快速验证问题是否解决。
概念简介
热部署是指在不停止进程的情况下,重新加载新的java代码。例如,修改完方法里的代码后,将修改的逻辑重新注入到Jvm里。Arthas就给我们提供了这样的功能。
热部署过程
修改java代码
如果没有原始java文件,通过Arthas的jad命令可以反编译获得java文件:
> jad --source-only com.controller.TestController > /tmp/TestController .java
--source-only参数控制输出内容里只包含源代码。不然会一起输出ClassLoader和Location文件位置。
重新编译java代码
使用Arthas时,编译java代码需用到原class文件的类加载器。
使用`sc`命令查到原class文件的类加载器
> sc -d com.controller.TestController
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@49c2faae
+-sun.misc.Launcher$AppClassLoader@42a57993
+-sun.misc.Launcher$ExtClassLoader@2d83b924
classLoaderHash 49c2faae
在输出内容的最后一行,显示了当前类的加载器。
使用`mc`命令编译java文件
> mc -c 49c2faae /tmp/AliAdminController.java -d /tmp
Memory compiler output:
/tmp/com/controller/TestController .class
编译命令会因为各种原因而编译失败,这时候可以尝试在本地把原始java传上去,不使用反编译的数据。
下面是编译失败的提示:
Memory compiler error, exception message: Compilation Error
重新加载新的class文件
> redefine /tmp/com/dingtone/videomaster/feed/web/controller/feign/AliAdminController.class
redefine success, size: 1, classes:
com.dingtone.videomaster.feed.web.controller.feign.AliAdminController
小结
redefine命令返回redefine success就代表热部署成功。在使用热加载可以修改方法体里的代码,但是也会有一些限制,不能修改class的schema,包括新增字段/方法。