学习arthas(十五)无侵入的热部署
Arthas
1.基本介绍
arthas提供了热部署功能,可以在不停止应用程序的情况下修改代码并立即生效。所以在前面也实践了它的诸多命令都是为在分析问题的原因上,若问题分析清除了,能够直接在不影响用户使用的前提下修正解决,就显得非常高端了。在arthas里提供了jad、mc、redefine、retransform几个命令专门可协同的作用于程序的热部署。
所谓的热部署是指在不重启Java应用程序进程的前提下覆盖现有的程序版本代码达到运行生效的目的,同时热部署也不是无脑的代码更新,通常像一些框架应用的配置文件的修改后不会被更新(一般框架对于配置文件只读取一次),修改了全局静态全局常量也基本不会生效,新增加的类和方法也不会生效,仅在修改了方法内部的代码会生效,当然了,对于无限循环的场景也不会生效。闲扯几句JRebel实在是太高端了,它可以支持新创建的类、新增加的方法、常量的修改、SpringBoot项目中的定时任务Cron表达式、JPA的@Query注解都能支持到位。
2.Jad
反编译指定已加载类的源码,一般对于无源码的类需要使用到反编译,有时也需要在服务器上临时查看一些代码的实现时也需要临时来看上一看,所以需要反编译。jad 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,便于你理解业务逻辑;如需批量下载指定包的目录的 class 字节码可以参考 dump。
在 Arthas Console 上,反编译出来的源码是带语法高亮的,阅读更方便;
当然,反编译出来的 java 代码可能会存在语法错误,但不影响你进行阅读理解;
在有源码的情况下,还是直接在源码上进行修改更合适;
3.mc
Memory Compiler/内存编译器,编译.java文件生成.class。
注意,mc 命令有可能失败(被编译的类依赖了项目中使用到的第三方库)。如果编译失败可以在本地编译好.class文件,再上传到服务器。
4.redefine
加载外部的.class 文件,redefine jvm 已加载的类。
推荐使用 retransform 命令。
redefine 的 class 不能修改、添加、删除类的 field 和 method,包括方法参数、方法名称及返回值。
如果 mc 失败,可以在本地开发环境编译好 class 文件,上传到目标系统,使用 redefine 热加载 class
目前 redefine 和 watch/trace/jad/tt 等命令冲突,以后重新实现 redefine 功能会解决此问题
注意, redefine 后的原来的类不能恢复,redefine 有可能失败(比如增加了新的 field),参考 jdk 本身的文档。
reset命令对redefine的类无效。如果想重置,需要redefine原始的字节码。
redefine命令和jad/watch/trace/monitor/tt等命令会冲突。执行完redefine之后,如果再执行上面提到的命令,则会把redefine的字节码重置。 原因是 jdk 本身 redefine 和 Retransform 是不同的机制,同时使用两种机制来更新字节码,只有最后修改的会生效。
`redefine 的限制`
jad的反编译和mc的编译命令都是可以在合适顺手的应用环境中进行操作,而redefine的加载更新被官方不建议使用,所以这三个命令可以不用特别掌握。
5.retransform
加载外部的.class文件,retransform jvm 已加载的类。
如果多次执行 retransform 加载同一个 class 文件,则会有多条 retransform entry.
TransformCount 统计在 ClassFileTransformer#transform 函数里尝试返回 entry 对应的 .class 文件的次数,但并不表明 transform 一定成功。
TransformCount 统计在 ClassFileTransformer#transform 函数里尝试返回 entry 对应的 .class 文件的次数,但并不表明 transform 一定成功。
查看 retransform entry
删除指定 retransform entry
显式触发 retransform
消除 retransform 的影响
如果不清除掉所有的 retransform entry,并重新触发 retransform ,则 arthas stop 时,retransform 过的类仍然生效。
结合 jad/mc 命令使用
上传 .class 文件到服务器的技巧
retransform 的限制
(1)不允许新增加 field/method
(2)正在跑的函数,没有退出不能生效,比如下面新增加的System.out.println,只有run()函数里的会生效.
6.热部署实践
原始代码
/**
* 启动类
*
* @author chendd
* @date 2023/9/22 14:08
*/
@SpringBootApplication
@RestController
public class BootstrapArthas {
public static void main(String[] args) {
SpringApplication.run(BootstrapArthas.class , args);
}
@GetMapping
public String sayHello() {
return "hello, chendd";
}
}
假设该代码中存在一些代码逻辑,本次实践模拟操作耗时5秒钟的代码为基础,参考如下:
/**
* 启动类
*
* @author chendd
* @date 2023/9/22 14:08
*/
@SpringBootApplication
@RestController
public class BootstrapArthas {
public static void main(String[] args) {
SpringApplication.run(BootstrapArthas.class , args);
}
@GetMapping
public String sayHello() {
try {
TimeUnit.SECONDS.sleep(2L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("问题已解决,第 [1] 个版本");
return "hello, chendd";
}
}
假设发现代码的问题耗时根本在于休眠了2秒钟,当我们删除2秒钟的休眠,问题得以解决,以下是我的实践步骤:
(1)启动arthas-tunnel-server模式的arthas架构,参考如下表格:
ip地址 | 机器智能 | 说明 |
192.168.244.1 | Windows主机 | 访问 WebConsole,操作139机器的java进程 |
192.168.244.138 | Linux | 部署 arthas-tunnel-server |
192.168.244.139 | Linux | 部署 自定义程序 |
(1)将修改后的class文件上传至服务器139,因为139是实际部署应用程序的项目所在服务器,为了方便可以直接通过xftp等工具上传;
(2)本次为了实践,特在Windows主机上又开启arthas,使用base64命令将class文件转换生成base64的字符串文件,再将文件中的内容粘贴至139的服务器文件中,所谓的某些服务器允许剪切板里粘贴文本,却不允许粘贴文件时的小技巧,参考如下图所示:
[arthas@26752]$ base64 -d --input E:/ArthasBootstrap.base64.txt --output E:/ArthasBootstrap.class
(3)当把文件拷贝至139服务器上之后,使用arthas-tunnel-server的WebConsole控制台base64命令,将该文件还原,参考如下图所示:
(4)如果第(2)和(3)没有看清楚,可忽略,直接转至此处的加载外部class文件的命令,参考如下图所示:
(5)使用retransform -l查看所有已经加载过的class列表;retransform -d id可按id删除已经加载过的class;retransform --deleteAll可以删除全部加载过的class列表;
(6)被retransform加载过的类,当arthas的server重启后,仍然生效;在尝试删除被加载过的id和deleteAll后加载过的类仍然生效;
(7)消除 retransform 的影响,删除这个类对应的 retransform entry后发现并未生效(热加载被还原),当然只有重新再retransform一次代码的版本才会恢复到某个版本;
(8)要加载的class文件名称可以随意命名,并不限制必须为实际的public static class名称;
(9)直接运行程序耗费2秒钟,通过retransform命令热部署后解决问题耗费时间正常,参考如下图所示:
(10)另一个热部署的效果图(gif图片略大)下载自行查看:热部署-多个版本.gif;
(11)retransform 的限制:不允许新增加 field/method、正在跑的函数,没有退出不能生效,与正常开发中的热部署一样有此限制;
点赞
发表评论
-
1 验证码为Gif图片,共4个字符和4帧图像交替闪烁,循环闪烁;
-
2 验证码每搁1秒闪烁一次,每次只显示3个字符和1个问号;
-
3 验证码为动态图片,问号出现位置交替变化;
-
4 验证码输入不区分字母大小写,参考以下为A123的验证码:
① 示例图片中没有字符角度旋转;
② 示例图片中没有干扰线;
③ 示例图片中没有网址信息;
-
5 再三权衡,还是将问号字符去除了,后台登录入口我个人使用予以保留;
1 如果您觉得验证码很难接收的话,欢迎给我反馈,我会继续改善,见顶部菜单栏的
作者介绍;
2 访问我站的朋友十有八九都是IT技术类大佬,这样式的验证码看起来不至于太复杂;
3 此处验证码前身使用的是腾讯防水墙免费验证码组件,由于一些原因切换为个人自定义的验证码;
4 所爱隔山海,山海界可平,欢迎留言评论;
评论列表