V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hengyunabc
V2EX  ›  程序员

少年,想线上热更新代码不?

  •  
  •   hengyunabc ·
    hengyunabc · 2019-02-19 15:57:23 +08:00 · 3625 次点击
    这是一个创建于 2104 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景

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

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

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

    又比如:

    本地开发时,发现某个开源组件有 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 实践系列

    公众号

    欢迎关注横云断岭的专栏,专注 Java,Spring Boot,Arthas,Dubbo。

    横云断岭的专栏

    4 条回复    2019-02-19 21:13:23 +08:00
    murmur
        1
    murmur  
       2019-02-19 15:58:34 +08:00
    这个东西跟 idea 的热部署插件有什么区别
    hengyunabc
        2
    hengyunabc  
    OP
       2019-02-19 16:15:14 +08:00
    @murmur 没有用过,idea 的更多可能是本地开发用的。Arthas 是线上命令行工具。
    realkenshinji
        3
    realkenshinji  
       2019-02-19 18:34:20 +08:00 via iPhone
    用 Elixir 不就行了么
    hellowes
        4
    hellowes  
       2019-02-19 21:13:23 +08:00
    阿里 Java 就是比前端 NB 100 背
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2419 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 15:57 · PVG 23:57 · LAX 07:57 · JFK 10:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.