什么热部署
热部署,即用户在使用服务或软件时,可以在无感知的情况下对系统或软件进行升级,以修复BUG或实现功能的迭代。在Java项目中,热部署是在运行时执行Java类文件,触发Spring或其他第三方框架以更新源代码的过程。在热部署过程中,Java程序无需重启,服务可以持续提供服务,并且程序代码在热部署后能够立即更新。
为什么需要热部署
对于后端开发人员来说,最重要的任务是自测、联调和上线。在自测阶段,我们需要根据代码质量进行适当的调试;在联调阶段,我们需要与前端同学进行不断的适配和调试;在上线阶段,我们要确保项目万无一失,尽可能减少项目出现的问题。
在上述的3个场景中,后端开发人员需要不断地进行排查、修复bug和重启的过程。在本地IDEA中,重启项目的时间可以暂时忽略。然而,一旦进入联调和测试阶段,由于需要使用公司的pack平台和公司内部研发平台进行打包、部署的CI/CD流程,这部分时间将大大增加。
热部署的目的在于尽可能降低项目的重启和部署次数,减少部署过程中消耗的大量无效时间,节省碎片化时间,为开发同事提供额外的开发时间,以便更加关注于程序的开发与优化。
热部署的难度
目前,市面上有许多Java热部署的方案,但是它们的功能差异较大。一般而言,开发人员在开发项目时并不会过多考虑热部署的相关技术方案。这主要是因为热部署不同于重启,它不仅需要兼容各种第三方框架,还需要开发人员对字节码有深入的了解。同时,考虑热部署时还需要考虑到Spring和HOTSWAP等相关限制。
对于公司内部的服务,我们可以方便地进行项目部署,因此热部署的技术方案可以暂时不考虑。然而,对于客户定制项目,由于其特殊性和定制化,往往涉及到现场部署难度大、申请流程长、代码规范要求高等问题。因此,客户项目的热部署需求在这种背景下应运而生。
常见的热部署方案
- Spring Boot DevTools是Spring官方提供的工具,它可以监控类文件的更改并自动重启应用程序。它还支持属性文件的热加载。
- JRebel是一款商业的热部署工具,它可以在应用程序运行时直接将新的Java代码加载到虚拟机中,而不需要重新启动。它支持各种Java应用服务器和框架。
- Java虚拟机本身提供了一些工具来支持热部署。例如,使用Java的-XX:PermSize和-XX:MaxPermSize参数可以动态调整永久代内存大小,这在一些情况下可以避免类加载时的内存不足错误。
- HotSwap是一个开源的工具,它可以与各种IDE(如Eclipse、IntelliJ IDEA)一起使用,允许你在调试会话中替换类的字节码,以实现热部署。
客户定制项目中的热部署使用
1.开发与调试
开发过程中,因为项目运行在我们本地,因此项目只需要简单的配置 spring-dev-tool
即可。
org.springframework.boot
spring-boot-devtools
true
接着,我们在 IDEA 中启动热部署即可。
File -> Settings -> Build,Execution,Deplyment -> Compiler,选中打勾 Build project automatically。
2.项目部署
JavaCompiler
其实JDK的底层本身就提供了动态加载类文件的能力,它就是JavaCompiler。如果使用JavaCompiler动态加载类文件内容,那就需要经过下述流程:
- 将Java代码组装成符合标准的源码格式,然后通过编译将其转化为class字节码。
- 运用ClassLoader工具将这个class字节码加载到JVM中,生成可运行的class对象。
- 借助反射机制,在运行时动态调用这个class中的逻辑代码,实现灵活的程序控制。
String javaCode = "public class HelloWorld {"
+ " public static void main(String[] args) {"
+ " System.out.println("Hello, World!");"
+ " }"
+ "}";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
// 创建一个Java文件对象
JavaFileObject javaFileObject = new JavaSourceFromString("HelloWorld", javaCode);
// 设置编译选项,例如生成的字节码版本
Iterable options = Arrays.asList("-source", "1.8", "-target", "1.8");
// 编译Java代码
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, Collections.singletonList(javaFileObject));
boolean result = task.call() == true;
// 关闭文件管理器
fileManager.close();
if (result) {
System.out.println("Compilation succeeded.");
} else {
System.out.println("Compilation failed.");
}
得到class之后,我们想要调用class的方法,最直接的就是反射调用,相对就比较简单了。
import javax.tools.*;
import java.io.*;
import java.lang.reflect.*;
public class DynamicCompilationExample {
public static void main(String[] args) {
try {
// 编译源码
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
JavaFileObject javaFileObject = new JavaSourceFromString("MyBusinessLogic", javaCode);
Iterable myClass = classLoader.loadClass("MyBusinessLogic");
// 创建类的实例并调用其方法
Object instance = myClass.newInstance();
Method executeMethod = myClass.getDeclaredMethod("execute");
executeMethod.invoke(instance);
} catch (Exception e) {
e.printStackTrace();
}
}
Arthas
阿里的arthas非常受欢迎,因为它提供了一套解决线上问题的解决方案,如在线查看服务器状态,支持热更新,以及跟踪执行情况,打印执行参数和返回参数等功能。它的功能非常强大,而且对应用没有侵入性,不会影响目标应用的业务逻辑。
在 Arthas Shell 中,我们可以使用 redefine 命令进行热部署。例如,如果你想热部署一个类 com.example.MyClass,可以执行以下命令(在此之前,你需要Java 代码编译为字节码) :
redefine --class com.example.MyClass --source /path/to/MyClass.class
总结
热部署技术是能够极大方便开发部署的,但需谨慎使用。在客户项目中,我们不仅需评估热部署的适用范围,还需遵守客户的安全规范。过多依赖热部署可能引发问题,应尽量避免。
作为开发或者是项目负责人,确保在开发和测试环境中合理使用热部署以提高效率,但在生产环境中,需遵循最佳实践,确保稳定性和安全性。客户的规定和标准是首要考虑因素,应积极遵循以确保项目的成功和安全运行。
参考文件: