热部署在 Java 定制项目的使用

什么热部署

热部署,即用户在使用服务或软件时,可以在无感知的情况下对系统或软件进行升级,以修复BUG或实现功能的迭代。在Java项目中,热部署是在运行时执行Java类文件,触发Spring或其他第三方框架以更新源代码的过程。在热部署过程中,Java程序无需重启,服务可以持续提供服务,并且程序代码在热部署后能够立即更新。

为什么需要热部署

对于后端开发人员来说,最重要的任务是自测、联调和上线。在自测阶段,我们需要根据代码质量进行适当的调试;在联调阶段,我们需要与前端同学进行不断的适配和调试;在上线阶段,我们要确保项目万无一失,尽可能减少项目出现的问题。

在上述的3个场景中,后端开发人员需要不断地进行排查、修复bug和重启的过程。在本地IDEA中,重启项目的时间可以暂时忽略。然而,一旦进入联调和测试阶段,由于需要使用公司的pack平台和公司内部研发平台进行打包、部署的CI/CD流程,这部分时间将大大增加。

热部署的目的在于尽可能降低项目的重启和部署次数,减少部署过程中消耗的大量无效时间,节省碎片化时间,为开发同事提供额外的开发时间,以便更加关注于程序的开发与优化。

热部署的难度

目前,市面上有许多Java热部署的方案,但是它们的功能差异较大。一般而言,开发人员在开发项目时并不会过多考虑热部署的相关技术方案。这主要是因为热部署不同于重启,它不仅需要兼容各种第三方框架,还需要开发人员对字节码有深入的了解。同时,考虑热部署时还需要考虑到Spring和HOTSWAP等相关限制。

对于公司内部的服务,我们可以方便地进行项目部署,因此热部署的技术方案可以暂时不考虑。然而,对于客户定制项目,由于其特殊性和定制化,往往涉及到现场部署难度大、申请流程长、代码规范要求高等问题。因此,客户项目的热部署需求在这种背景下应运而生。

常见的热部署方案

  1. Spring Boot DevTools
  • Spring Boot DevTools是Spring官方提供的工具,它可以监控类文件的更改并自动重启应用程序。它还支持属性文件的热加载。
  1. JRebel
  • JRebel是一款商业的热部署工具,它可以在应用程序运行时直接将新的Java代码加载到虚拟机中,而不需要重新启动。它支持各种Java应用服务器和框架。
  1. JVM工具
  • Java虚拟机本身提供了一些工具来支持热部署。例如,使用Java的-XX:PermSize和-XX:MaxPermSize参数可以动态调整永久代内存大小,这在一些情况下可以避免类加载时的内存不足错误。
  1. HotSwap
  • HotSwap是一个开源的工具,它可以与各种IDE(如Eclipse、IntelliJ IDEA)一起使用,允许你在调试会话中替换类的字节码,以实现热部署。

客户定制项目中的热部署使用

1.开发与调试

开发过程中,因为项目运行在我们本地,因此项目只需要简单的配置 spring-dev-tool即可。

xml 复制代码
<dependencies> 
    <dependency> 
        <groupId>org.springframework.boot </groupId> 
        <artifactId> spring-boot-devtools </artifactId> 
        <optional>true</optional> 
    </dependency> 
</dependencies>

接着,我们在 IDEA 中启动热部署即可。

File -> Settings -> Build,Execution,Deplyment -> Compiler,选中打勾 Build project automatically。

2.项目部署

JavaCompiler

其实JDK的底层本身就提供了动态加载类文件的能力,它就是JavaCompiler。如果使用JavaCompiler动态加载类文件内容,那就需要经过下述流程:

  • 将Java代码组装成符合标准的源码格式,然后通过编译将其转化为class字节码。
  • 运用ClassLoader工具将这个class字节码加载到JVM中,生成可运行的class对象。
  • 借助反射机制,在运行时动态调用这个class中的逻辑代码,实现灵活的程序控制。
java 复制代码
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<String> 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的方法,最直接的就是反射调用,相对就比较简单了。

java 复制代码
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<? extends JavaFileObject> fileObjects = Collections.singletonList(javaFileObject);
            compiler.getTask(null, fileManager, null, null, null, fileObjects).call();
            fileManager.close();

            // 使用自定义ClassLoader加载编译后的类
            MyClassLoader classLoader = new MyClassLoader();
            Class<?> 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 代码编译为字节码) :

vbnet 复制代码
redefine --class com.example.MyClass --source /path/to/MyClass.class

总结

热部署技术是能够极大方便开发部署的,但需谨慎使用。在客户项目中,我们不仅需评估热部署的适用范围,还需遵守客户的安全规范。过多依赖热部署可能引发问题,应尽量避免。

作为开发或者是项目负责人,确保在开发和测试环境中合理使用热部署以提高效率,但在生产环境中,需遵循最佳实践,确保稳定性和安全性。客户的规定和标准是首要考虑因素,应积极遵循以确保项目的成功和安全运行。

参考文件:

  1. yeas.fun/archives/ho...
  2. juejin.cn/post/715269...
  3. pdai.tech/md/spring/s...
  4. www.cnblogs.com/wchukai/p/5...
  5. tech.meituan.com/2022/03/17/...
相关推荐
Abladol-aj1 小时前
并发和并行的基础知识
java·linux·windows
清水白石0081 小时前
从一个“支付状态不一致“的bug,看大型分布式系统的“隐藏杀机“
java·数据库·bug
吾日三省吾码6 小时前
JVM 性能调优
java
Estar.Lee6 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
弗拉唐7 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi778 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
2401_857610038 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
少说多做3438 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀8 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员