线上代码出现问题,我们就要修改问题。面对要求严格的停服计划,对于一些小Bug,我们可以采取不停服热更新,今天就总结一个热更新方案: 利用Arthas在线热更。

Arthas介绍、安装

介绍

Arthas是Ali出品的一款线上针对产品,官方介绍是: Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断, 包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。

安装&启动

下载arthas-boot.jar,然后用java -jar的方式启动:

curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

启动后,我们就可以在窗口里执行各种Arthas命令。包括后面我们将要学习的热更命令。

打印帮助信息:

java -jar arthas-boot.jar -h

如果下载速度比较慢,可以使用 aliyun 的镜像:

java -jar arthas-boot.jar --repo-mirror aliyun --use-http

热更预准备

  1. 热更新前,我们使用SpringBoot准备一个Controller,并建立待修复Bug的错误代码:
@RestController
public class FibonacciController {

    @RequestMapping(value ="/fib", method = RequestMethod.GET)
    public long fibonacci(int value){
        return fib(value);
    }

    private long fib(int n){
        if (n <= 1) {
            return 1;
        }
        return fib(n-1) * fib(n-2);
    }
}

对于熟悉斐波那契数列的小伙伴可能知道,我们要做的操作就是修改fib(n-1) * fib(n-2)中的乘号为加好。

  1. 启动运行SpringBoot,访问http://127.0.0.1:8080/fib?value=5

查看输出:1

热更

热更操作,我们主要是利用Arthas的jad sc mc retransform命令。下面我们将逐个讲解如何使用命令。 具体更强大的命令请直接查看官网信息

  1. jad 反编译

    jad 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,便于你理解业务逻辑; 下面我们将待修改的方法所在类反编译到指定位置:

     $ jad --source-only com.a3fun.learn.learnhotswap.FibonacciController > /Users/用户名/arthas-output/FibonacciController.java
    

    我们可以使用编辑器打开java文件,修改错误代码。 修改前:return this.fib(n - 1) * this.fib(n - 2); 修改后:return this.fib(n - 1) + this.fib(n - 2);

  2. sc 查看类加载器

    “Search-Class” 的简写,这个命令能搜索出所有已经加载到 JVM 中的 Class 信息。

    $ sc -d *FibonacciController | grep classLoaderHash
    # 输出
    classLoaderHash   5b480cf9
    
  3. mc 内存编译

    Memory Compiler/内存编译器,编译.java文件生成.class。

    小提示:

    使用mc命令来编译jad的反编译的代码有可能失败。可以在本地修改代码,编译好后再上传到服务器上。

    $ mc -c 5b480cf9 /Users/用户名/arthas-output/FibonacciController.java -d /Users/用户名/arthas-output/
    # 输出
    Memory compiler output:
    /Users/用户名/arthas-output/com/a3fun/learn/learnhotswap/FibonacciController.class
    Affect(row-cnt:1) cost in 616 ms.
    
  4. retransform 加载class

    加载外部的.class文件,retransform jvm 已加载的类。可以支持单个、多个、正则表达式。

    顺便说一下redefineretransform的区别:前者redefine 的 class 不能修改、添加、删除类的 field 和 method,包括方法参数、方法名称及返回值。所以推荐使用后者。

    $ retransform /Users/haixiangchen/arthas-output/com/a3fun/learn/learnhotswap/FibonacciController.class
    # 输出
    retransform success, size: 1, classes:
    com.a3fun.learn.learnhotswap.FibonacciController
    
  5. 重新访问:访问http://127.0.0.1:8080/fib?value=5 查看新的输出已经是正确的结果:8