在一次迭代中,出现了一个低级错误, if
语句中的判断逻辑出现了错误,刚好这个功能场景在开发和测试过程中很少触发,使用的用户也不多,不过的确影响到了少部分用户,所以还是需要进行修复。
想着只是更新一行代码,如果走正常的发布流程,需要通过以下步骤:
提交代码 -> 提测打包 -> 测试环境验证 -> Release 环境验证 -> 预发环境验证 -> 线上环境
Java
:-O 之前使用过 Alibaba
开源的诊断工具 Arthas
,下图是官方文档中提到的功能:
redefine
进行热更新。 一条龙更新
,所以就开始了尝试,先从本地开发测试开始。 开发时写下的 java
程序是高级语言,需要通过编译生成 .class
文件才能在 jvm
中运行。
jad
[Java decompile]反编译,修改 .java
文件后使用 mc
[Memory complile] 编译出 .class
文件,最后使用 redefine
命令更新虚拟机中的程序。 首先可以跟着教程来一次尝试 alibaba.github.io/arthas/arth…
# 反编译 $ jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java # 修改文件 $ vim /tmp/UserController.java # 查找加载的 ClassLoader $ $ sc -d *UserController | grep classLoaderHash classLoaderHash 6bc28484 # 编译 $ mc -c 6bc28484 /tmp/UserController.java -d /tmp # 热更新 $ redefine /tmp/com/example/demo/arthas/user/UserController.class redefine success, size: 1 复制代码
跟着教程 demo
,发现代码逻辑被修改,返回我修改后的结果,心里在狂喜,可以不用喝这些多咖啡!
但在工作中的项目中使用,发现出现了这个问题: 反编译后的类不完整
查看通过 jad
命令反编译后的文件:
/* * Decompiled with CFR. * * Could not load the following classes: * .... */ 复制代码
在不完整的文件中进行修改后,进行 mc
命令编译,将会提示如下:
[arthas@7281]$ mc -c 6bc28484 /tmp/xxxx.java -d /tmp Memory compiler error, exception message: Compilation Error ...... , please check $HOME/logs/arthas/arthas.log for more details. Affect(row-cnt:0) cost in 884 ms. 复制代码
可以看到,如果有复杂的类,并一定能够成功反编译,遭遇了失败,开始排查原因
开源的好处的是,大家可以一起参与到其中,提出问题和解决问题,在 github
项目 arthas
的 issue
中,通过关键字 jad 反编译
找到了原因
横云断岭 Arthas源码分析--jad反编译原理 :
当时对于 jad
机制是有点不了解,所以想先通过上面的提示的工具 dumpclass
去精确获取 java
字节码,但奈何有些难用,尝试了几遍还是没能拿下来,于是开始换种思路。
.class
文件 既然前面的操作,获取修改后的 .class
文件,都是为了最后一步 redefine
所服务,那只要获取精确的 .class
文件就可以了,跳过前面的步骤也可以。
IDEA
工具编译后的 .class
文件 于是将本地代码进行修改,进行打包编译,得到想要的 .class
文件,然后将这个文件上传到测试环境,进行替换。
文件地址类似下图:
[arthas@63]$ redefine /tmp/xxxxxx.class redefine success, size: 1 复制代码
通过上面说的,正常来说只需要简单四步就能进行热更新
jad
反编译出 .java
文件 mc
编译修改后的文件 redefine
热更新 当然,也可能遇到 jad
反编译失败的场景,可以参考方案二,直接拿到修改后的 .class
文件,然后继续进行操作。
标题最后一个字为什么叫【误】呢,是因为我经过测试,跟前辈们讨论后,在发布流程规范、 IDEA
提供的 .class
文件不确定性还有工具误用的把控上考虑,觉得目前不适合使用,于是还是走了正常的发布流程。
Java
:-(