浅析热加载和热部署-疑问点

229 阅读5分钟

一 什么是热加载和热部署

1.1 热部署(Hot Deploy)

  • 在服务器运行时重新部署项目
  • 热部署针对的是容器或者是整个应用,部署了新的资源或者修改了一些代码,需要在不停机的情况下的重新加载整个应用。

1.2 热加载(Hot Swap)

热加载针对的是单个字节码文件,指的是重新编译后,不需要停机,应用程序就可以加载使用新的class文件。

具体过程如下:

  • 在在运行时重新加载class,从而升级应用。
  • 热加载的实现原理主要依赖[Java类加载机制 - 详解, 不清晰的一定去看一下],在实现方式可以概括为在容器启动的时候起一条后台线程,定时的检测类文件的时间戳变化,如果类的时间戳变掉了,则将类重新载入。
  • 对比反射机制,反射是在运行时获取类信息,通过动态的调用来改变程序行为; 热加载则是在运行时通过重新加载改变类信息,直接改变程序行为。

二 热加载的原理是什么

2.1 检测哪些文件需要重新被加载

如果应用系统简单的话,可以开启一个单独的线程,间隔时间内重新加载所有的class文件。

如果稍微大一点的话,需要考虑性能,尽量识别出哪些需要被热加载,比如根据class文件最新更新时间判断等等

因为他不会清理内存吗,所以有内存溢出的风险,所以成熟的热加载软件或者系统都会有对应的方案来处理。

三 首先需要了解东西

关键的步骤:

  1. 存储旧对象实例:在加载新版本的类之前,确保你有一种方式来存储旧版本的对象实例。这可以是一个对象引用,一个数据结构,或者其他适当的机制。
  2. 加载新版本的类:使用自定义类加载器加载新版本的类。这通常需要不同的类加载器,以避免与旧版本的类冲突。
  3. 创建新对象实例:使用新版本的类创建一个新的对象实例,这将代表新版本的类。
  4. 替换旧对象:在加载新版本的类和创建新对象实例后,替换存储的旧对象实例。这可以通过简单地更新对象引用来实现。
  5. 清理旧对象:确保旧版本的对象实例被妥善清理和释放。这可以通过垃圾回收或其他资源管理机制来实现。
  6. 通知其他部分:如果旧版本的对象被其他地方调用,确保通知这些其他部分,以便它们能够适应新版本的对象。
  7. 错误处理和回滚:实现错误处理机制,以应对加载新版本时可能出现的问题。同时,考虑实现回滚机制,以便在出现问题时可以恢复到旧版本。
  8. 注意:在新旧对象替换的时候存在短暂的不可用时间。

四 案例---简单→不包括类的管理

public class MyClassLoader extends ClassLoader {
    //要加载的java类的classpath路径
    private String classPath;

    public MyClassLoader( String classPath) {
        super(ClassLoader.getSystemClassLoader());
        this.classPath = classPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = this.loadData(name);
        //返回加载后的Class对象
        return this.defineClass(name, data, 0, data.length);
    }

    private byte[] loadData(String name) {
        try {
            name = name.replace(".", "/");
            FileInputStream is = new FileInputStream(new File(classPath + "/" +name + ".class"));
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int b = 0;
            while ((b = is.read()) != -1) {
                bos.write(b);
            }
            is.close();
            return bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getClassPath() {
        return classPath;
    }

    public void setClassPath(String classPath) {
        this.classPath = classPath;
    }
}



public class HotSwapMain {

    public static void main(String[] args) throws Exception{

        while (true) {

            MyClassLoader classLoader = new MyClassLoader("E:/code/ecom-review-core/target/classes");

            Class<?> clazz = classLoader.loadClass("com.core.jvm.hotload.HotSwap");

            Object newInstance = clazz.newInstance();

            clazz.getMethod("output").invoke(newInstance);

            Thread.sleep(2000);

        }

    }

}
  • 这里还是缺少新旧对象的替换等复杂的操作的,只是实现了一个简单的Demo,功能是加载一个新的类。
  • 第二每次都在while循环中新建了一个类以及类加载器,一般是需要实现旧类和类加载器的管理的。

五 框架

我们真正使用的时候其实最多的还是用框架去进行实现的,因为框架已经实现了很多功能了,自己去实现太复杂。

  1. ava Instrumentation API:Java提供了 java.lang.instrument 包,它允许你在类加载时进行字节码修改。这个API被一些热加载框架使用。
  2. Apache Commons BCEL:这是一个字节码工程库,允许你编辑字节码。它通常与热加载一起使用。
  3. Spring Boot DevTools:Spring Boot提供了开发工具,其中包括了热加载功能,可以在开发时自动重新加载类。
  4. OSGi (Open Service Gateway Initiative) :这是一个Java模块系统和服务平台,允许你动态部署和卸载模块。
  5. JRebel:这是一个商业的Java热加载工具,它提供了广泛的热加载支持。

六 总结

不管理热部署还是热加载,其实最终目的是无需要重启服务,实现服务自动化加载。

区别来看

  • 热部署更加全面,针对的是整个项目,而热加载仅针对变更的文件代码做局部更新,对于新文件或目录无效。需要重部署。
  • 当然对于java语言来说,热加载属于比较成熟的技术,而对于tomcat来说热部署也是比较方法,直接把文件丢进webapp自动就部署上了。在不同的场景会有不同的使用。像idea、vscode很多ide都会实现热部署的功能,所以在开发层面极大提升我们研发的效率。

对tomcat实现热部署感兴趣的可以看这篇文章,tomcat是实现了新旧对象的管理的。

主要参考

莫言静好、热加载和热部署

SpringBoot中的花样配置—实现热部署配置及原理