一 什么是热加载和热部署
1.1 热部署(Hot Deploy)
- 在服务器运行时重新部署项目
- 热部署针对的是容器或者是整个应用,部署了新的资源或者修改了一些代码,需要在不停机的情况下的重新加载整个应用。
1.2 热加载(Hot Swap)
热加载针对的是单个字节码文件,指的是重新编译后,不需要停机,应用程序就可以加载使用新的class文件。
具体过程如下:
- 在在运行时重新加载class,从而升级应用。
- 热加载的实现原理主要依赖[Java类加载机制 - 详解, 不清晰的一定去看一下],在实现方式可以概括为在容器启动的时候起一条后台线程,定时的检测类文件的时间戳变化,如果类的时间戳变掉了,则将类重新载入。
- 对比反射机制,反射是在运行时获取类信息,通过动态的调用来改变程序行为; 热加载则是在运行时通过重新加载改变类信息,直接改变程序行为。
二 热加载的原理是什么
2.1 检测哪些文件需要重新被加载
如果应用系统简单的话,可以开启一个单独的线程,间隔时间内重新加载所有的class文件。
如果稍微大一点的话,需要考虑性能,尽量识别出哪些需要被热加载,比如根据class文件最新更新时间判断等等
因为他不会清理内存吗,所以有内存溢出的风险,所以成熟的热加载软件或者系统都会有对应的方案来处理。
三 首先需要了解东西
关键的步骤:
- 存储旧对象实例:在加载新版本的类之前,确保你有一种方式来存储旧版本的对象实例。这可以是一个对象引用,一个数据结构,或者其他适当的机制。
- 加载新版本的类:使用自定义类加载器加载新版本的类。这通常需要不同的类加载器,以避免与旧版本的类冲突。
- 创建新对象实例:使用新版本的类创建一个新的对象实例,这将代表新版本的类。
- 替换旧对象:在加载新版本的类和创建新对象实例后,替换存储的旧对象实例。这可以通过简单地更新对象引用来实现。
- 清理旧对象:确保旧版本的对象实例被妥善清理和释放。这可以通过垃圾回收或其他资源管理机制来实现。
- 通知其他部分:如果旧版本的对象被其他地方调用,确保通知这些其他部分,以便它们能够适应新版本的对象。
- 错误处理和回滚:实现错误处理机制,以应对加载新版本时可能出现的问题。同时,考虑实现回滚机制,以便在出现问题时可以恢复到旧版本。
- 注意:在新旧对象替换的时候存在短暂的不可用时间。
四 案例---简单→不包括类的管理
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循环中新建了一个类以及类加载器,一般是需要实现旧类和类加载器的管理的。
五 框架
我们真正使用的时候其实最多的还是用框架去进行实现的,因为框架已经实现了很多功能了,自己去实现太复杂。
- ava Instrumentation API:Java提供了
java.lang.instrument包,它允许你在类加载时进行字节码修改。这个API被一些热加载框架使用。 - Apache Commons BCEL:这是一个字节码工程库,允许你编辑字节码。它通常与热加载一起使用。
- Spring Boot DevTools:Spring Boot提供了开发工具,其中包括了热加载功能,可以在开发时自动重新加载类。
- OSGi (Open Service Gateway Initiative) :这是一个Java模块系统和服务平台,允许你动态部署和卸载模块。
- JRebel:这是一个商业的Java热加载工具,它提供了广泛的热加载支持。
六 总结
不管理热部署还是热加载,其实最终目的是无需要重启服务,实现服务自动化加载。
区别来看
- 热部署更加全面,针对的是整个项目,而热加载仅针对变更的文件代码做局部更新,对于新文件或目录无效。需要重部署。
- 当然对于java语言来说,热加载属于比较成熟的技术,而对于tomcat来说热部署也是比较方法,直接把文件丢进webapp自动就部署上了。在不同的场景会有不同的使用。像idea、vscode很多ide都会实现热部署的功能,所以在开发层面极大提升我们研发的效率。
对tomcat实现热部署感兴趣的可以看这篇文章,tomcat是实现了新旧对象的管理的。