Arthas 使用 retransform 热更新 Srping Boot 代码

Arthas Java Spring Boot 大约 7800 字

说明

本文基于Arthas 3.4.6

相关代码

package com.example.arthas.controller;

@Slf4j
@RestController
public class Test111Controller {

    @GetMapping("/test1")
    public Result test1(@RequestParam String key) {
        log.info("test1111111111111#{}", key);
        List<String> list = (List<String>) CollectionUtils.arrayToList(new String[]{"aaa", "bbb", "ccc"});
        return Result.builder().msg("请求成功").data(list).build();
    }

}

查看 Arthas 是否已经 attach

attach的监听3658端口。

ss -anpl | grep 3658

已监听,通过telnet进入。

telnet 127.0.0.1 3658

未监听,通过java -jar启动。

java -jar arthas-boot.jar

查看类信息

scsearch class-ddetail

找到classLoaderHash,类加载器的哈希值。

sc -d com.example.arthas.controller.Test111Controller

输出:

[arthas@89537]$ sc -d com.example.arthas.controller.Test111Controller
 class-info        com.example.arthas.controller.Test111Controller                                                                                                                             
 code-source       file:/home/root/demo/arthas-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/                                                                                                          
 name              com.example.arthas.controller.Test111Controller                                                                                                                             
 isInterface       false                                                                                                                                                                       
 isAnnotation      false                                                                                                                                                                       
 isEnum            false                                                                                                                                                                       
 isAnonymousClass  false                                                                                                                                                                       
 isArray           false                                                                                                                                                                       
 isLocalClass      false                                                                                                                                                                       
 isMemberClass     false                                                                                                                                                                       
 isPrimitive       false                                                                                                                                                                       
 isSynthetic       false                                                                                                                                                                       
 simple-name       Test111Controller                                                                                                                                                           
 modifier          public                                                                                                                                                                      
 annotation        org.springframework.web.bind.annotation.RestController                                                                                                                      
 interfaces                                                                                                                                                                                    
 super-class       +-java.lang.Object                                                                                                                                                          
 class-loader      +-org.springframework.boot.loader.LaunchedURLClassLoader@7daf6ecc                                                                                                           
                     +-sun.misc.Launcher$AppClassLoader@55f96302                                                                                                                               
                       +-sun.misc.Launcher$ExtClassLoader@1ed1993a                                                                                                                             
 classLoaderHash   7daf6ecc                                                                                                                                                                    

Affect(row-cnt:1) cost in 37 ms.

反编译已加载类的源码

--source-only:只输出源代码,重定向到/tmp目录下的Test111Controller.java文件中。

jad --source-only com.example.arthas.controller.Test111Controller > /tmp/Test111Controller.java

修改代码

vim /tmp/Test111Controller.java

编译反编译过的源码

mcMemory Compiler-c:指定类加载的哈希值,-d:存放.class文件的文件夹。

mc -c 7daf6ecc /tmp/Test111Controller.java -d /tmp

输出:最终.class路径位置/tmp/com/example/arthas/controller/Test111Controller.class

[arthas@89537]$ mc -c 7daf6ecc /tmp/Test111Controller.java -d /tmp
Memory compiler output:
/tmp/com/example/arthas/controller/Test111Controller.class
Affect(row-cnt:1) cost in 1174 ms.

如果不指定-c参数,可能出现以下错误。

[arthas@89537]$ mc /tmp/Test111Controller.java -d /tmp
Memory compiler error, exception message: Compilation Error
line: 12 , message: package org.slf4j does not exist , 
line: 13 , message: package org.slf4j does not exist , 
line: 14 , message: package org.springframework.web.bind.annotation does not exist , 
line: 15 , message: package org.springframework.web.bind.annotation does not exist , 
line: 17 , message: cannot find symbol
  symbol: class RestController , 
line: 19 , message: cannot find symbol
  symbol:   class Logger
  location: class com.example.arthas.controller.Test111Controller , 
line: 21 , message: cannot find symbol
  symbol:   class GetMapping
  location: class com.example.arthas.controller.Test111Controller , 
line: 19 , message: cannot find symbol
  symbol:   variable LoggerFactory
  location: class com.example.arthas.controller.Test111Controller , 
, please check $HOME/logs/arthas/arthas.log for more details.

热更新

加载外部的 .class 文件,替换原有JVM已加载的类。

retransform /tmp/com/example/arthas/controller/Test111Controller.class

输出:

[arthas@89537]$ retransform /tmp/com/example/arthas/controller/Test111Controller.class
retransform success, size: 1, classes:
com.example.arthas.controller.Test111Controller

retransform 列表

显示已经替换过的类

retransform -l

输出:

[arthas@89537]$ retransform -l
Id              ClassName       TransformCount  LoaderHash      LoaderClassName 
1               com.example.art 1               null            null            
                has.controller.                                                 
                Test111Controll                                                 
                er

恢复修改前代码

清除掉历史转换过的类,重新触发retransform以恢复修改之前的代码。

清除指定 retransform

清除id1entry

retransform -d 1

清除所有 retransform

retransform --deleteAll

重新触发 retransform 恢复修改之前的代码

使用--classPattern匹配需要恢复的代码。

retransform --classPattern com.example.arthas.controller.Test111Controller

输出

[arthas@89537]$ retransform --classPattern com.example.arthas.controller.Test111Controller
retransform success, size: 1, classes:
com.example.arthas.controller.Test111Controller

备注

  1. retransform过的类,在stopArthas后不会被reset,必须手动重新触发retransform
  2. 若没有清除retransform而停止了Arthas,则可以再次启动,重新jad-mc-retransformjad反编译得到的还是修改前的代码。
  3. retransform不允许新增加字段或方法。

Arthas 全量文件下载

https://github.com/alibaba/arthas/releases

参考

https://arthas.aliyun.com/doc/retransform.html

阅读 243 · 发布于 2021-04-28

————        END        ————

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批