Skip to content

Arthas实践--jad/mc/redefine线上热更新一条龙 #537

Closed
@hengyunabc

Description

@hengyunabc

背景

尽管在生产环境热更新代码,并不是很好的行为,很可能导致:热更不规范,同事两行泪。

但很多时候我们的确希望能热更新代码,比如:

线上排查问题,找到修复思路了,但应用重启之后,环境现场就变了,难以复现。怎么验证修复方案?

又比如:

本地开发时,发现某个开源组件有bug,希望修改验证。如果是自己编译开源组件再发布,流程非常的长,还不一定能编译成功。有没有办法快速测试?

Arthas是阿里巴巴开源的Java应用诊断利器,深受开发者喜爱。

下面介绍利用Arthas 3.1.0版本的 jad/mc/redefine 一条龙来热更新代码。

Arthas在线教程

下面通过Arthas在线教程演示热更新代码的过程。

arthas-online-hotswap

在例子里,访问 curl http://localhost/user/0,会返回500错误:

{
    "timestamp": 1550223186170,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "java.lang.IllegalArgumentException",
    "message": "id < 1",
    "path": "/user/0"
}

下面通过热更新代码,修改这个逻辑。

jad反编译代码

反编译UserController,保存到 /tmp/UserController.java文件里。

jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java

修改反编绎出来的代码

用文本编辑器修改/tmp/UserController.java,把抛出异常改为正常返回:

    @GetMapping(value={"/user/{id}"})
    public User findUserById(@PathVariable Integer id) {
        logger.info("id: {}", (Object)id);
        if (id != null && id < 1) {
            return new User(id, "name" + id);
            // throw new IllegalArgumentException("id < 1");
        }
        return new User(id.intValue(), "name" + id);
    }

sc查找加载UserController的ClassLoader

$ sc -d *UserController | grep classLoaderHash
 classLoaderHash   1be6f5c3

可以发现是spring boot的 LaunchedURLClassLoader@1be6f5c3 加载的。

mc内存编绎代码

保存好/tmp/UserController.java之后,使用mc(Memory Compiler)命令来编译,并且通过-c参数指定ClassLoader

$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms

redefine热更新代码

再使用redefine命令重新加载新编译好的UserController.class

$ redefine /tmp/com/example/demo/arthas/user/UserController.class
redefine success, size: 1

检验热更新结果

再次访问 curl http://localhost/user/0,会正常返回:

{
    "id": 0,
    "name": "name0"
}

总结

Arthas里 jad/mc/redefine 一条龙来线上热更新代码,非常强大,但也很危险,需要做好权限管理。

比如,线上应用启动帐号是 admin,当用户可以切换到admin,那么

  • 用户可以修改,获取到应用的任意内存值(不管是否java应用)
  • 用户可以attach jvm
  • attach jvm之后,利用jvm本身的api可以redefine class

所以:

  • 应用的安全主要靠用户权限本身的管理
  • Arthas主要是让jvm redefine更容易了。用户也可以利用其它工具达到同样的效果

最后,Arthas提醒您: 诊断千万条,规范第一条,热更不规范,同事两行泪

Arthas实践系列

Activity

hengyunabc

hengyunabc commented on Feb 20, 2019

@hengyunabc
CollaboratorAuthor

如果有一些类比较复杂,那么有可能jad命令反编绎失败。那么后面的mc命令也会失败。

这时,可以在本地修改代码,然后把.class文件上传到服务器上。

有一些服务器的权限比较严格,不允许直接上传文件,那么可以用一些技巧来绕过。比如传用base64命令:

  1. 在本地先转换.class文件为base64,再保存为result.txt
base64 < Test.class > result.txt
  1. 到服务器上,新建result.txt,复制本地的内容,粘贴再保存

  2. 把服务器上的 result.txt还原为.class

base64 -d < result.txt > Test.class
  1. 用md5命令计算哈希值,校验是否一致
CjqDy

CjqDy commented on Feb 27, 2019

@CjqDy

mc 内存编译的时候会根据类所在的包路径在服务器生成指定路径,是这样吧?
redefine也是根据这个路径同步更新包里的class文件的

hengyunabc

hengyunabc commented on Feb 27, 2019

@hengyunabc
CollaboratorAuthor

mc 内存编译的时候会根据类所在的包路径在服务器生成指定路径,是这样吧?
redefine也是根据这个路径同步更新包里的class文件的

mc 不指定输出目录,默认就是target进程的工作目录。可以用pwd命令来查看。

VanXD

VanXD commented on Mar 1, 2019

@VanXD

如果有一些类比较复杂,那么有可能jad命令反编绎失败。那么后面的mc命令也会失败。

这时,可以在本地修改代码,然后把.class文件上传到服务器上。

有一些服务器的权限比较严格,不允许直接上传文件,那么可以用一些技巧来绕过。比如传用base64命令:

  1. 在本地先转换.class文件为base64,再保存为result.txt
base64 < Test.class > result.txt
  1. 到服务器上,新建result.txt,复制本地的内容,粘贴再保存
  2. 把服务器上的 result.txt还原为.class
base64 -d < result.txt > Test.class
  1. 用md5命令计算哈希值,校验是否一致

本地修改较为复杂的情况, 检查class是否能热更新可在idea中按ctrl+shift+f9

xiexingguang

xiexingguang commented on Mar 1, 2019

@xiexingguang

nice

wanglei134

wanglei134 commented on Mar 24, 2021

@wanglei134

编译一个类 但是这个类import了其他jar包的类 会报错该怎么办呢

image

hengyunabc

hengyunabc commented on Mar 24, 2021

@hengyunabc
CollaboratorAuthor

@wangdonghello 看wiki,本地编译上传

gaochengyidlmu

gaochengyidlmu commented on Feb 24, 2023

@gaochengyidlmu

@hengyunabc
现在遇到泛型擦除的问题,通过 jad 反编译得到的java文件中,原本是 List para1 的变量,变成了 ArrayList para1,之后 mc 时,就会报错
Memory compiler error, exception message: Compilation Error
message: incompatible types: java.lang.Object cannot be converted to XXX

这个有没有什么办法可以处理,我现在想通过 arthas 的这个功能,进行批量操作,所以本地编译class行不通。

Contentsearch

Contentsearch commented on Jul 7, 2023

@Contentsearch

image
执行jad 反编译文件返回的是null 什么情况

cwdhf

cwdhf commented on Oct 11, 2023

@cwdhf

image 执行jad 反编译文件返回的是null 什么情况

写入指定文件不要用./ 如果指定目录直接使用绝对地址,如果不指定就不要填

MaidSG

MaidSG commented on Aug 5, 2024

@MaidSG
image 按流程操作未碰到问题,但是最后验证时并未变更是什么原因,用到了lombok的@builder注解,编译时生成了两个文件
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @hengyunabc@CjqDy@xiexingguang@wanglei134@VanXD

        Issue actions

          Arthas实践--jad/mc/redefine线上热更新一条龙 · Issue #537 · alibaba/arthas