前言
所谓热部署,就是在应用正在运行时升级软件,却不需要重新启动应用。对于 Java应用程序来说,热部署就是在运行时更新 Java 类文件,同时触发 Spring 以及其他常用第三方框架的一系列重新加载的过程。而在开发的过程中每天重启服务5-12次,单次3-8分钟,每天部署3-5次,部署频繁耗时长,严重影响上线的效率。而插件提供的本地和远程热部署功能,可让将代码变更“秒级”生效。
优势
在使用热部署插件之后,开发者修改代码远程热部署能够秒级(2~10s)生效,开发者直接发起服务调用,可以节省大量的碎片化时间(热部署插件还具备流量回放、远程调用、远程反编译等功能,可配合进行使用)。
热部署的难点
因为热部署不等同于热重启,像 Tomcat 或者 Spring Boot DevTools 此类热重启模式需要重新加载项目,性能较差。增量热部署难度较大,需要兼容常用的中间件版本,需要深入启动销毁加载流程。另外需遵循四大原则。
热部署的难点:
1、可参照资料匮乏
2、字节码操作难度大
3、HOTSWAP限制
4、Spring源码复杂
5、兼容框架多
6、部署方式多
7、环境打通难
8、用户问题调试难
设计方案
架构设计
插件由 4 大部分组成,包括脚本端、插件端、Agent 端,以及 服务端。脚本端负责自动化构建 Sonic 启动参数、服务启动等集成工作;IDEA 插件端集成环境为开发者提供更便捷的热部署服务;Agent 端随项目启动负责热部署的功能实现;服务端则负责收集热部署信息、失败上报等统计工作。如下图所示:
功能流转
组件是通过 NIO 监听本地文件变更,触发文件变更事件,例如 Class 新增、Class修改、Spring Bean 重载等事件流程。下图展示了一次热部署单个文件的生命周期:
文件监听
首先会在本地和远程预定义两个目录,/var/tmp/sonic/extraClass�path 和 /var/tmp/sonic/classes。extraClasspath 为 Sonic 自 定 义 的 拓 展Classpath URL,classes 为 Sonic 监听的目录,当有文件变更时,通过 IDEA 插件来部署到远程 / 本地,触发 Agent 的监听目录,来继续下面的热加载逻辑:
注意:
因为考虑到业务方WAR 包的 API 项目、Spring Boot、Tomcat 项目、Jetty 项目等,都是以 JAR 包来启动的,这样是无法直接修改用户的 Class 文件的。即使是用户项目可以修改,直接操作用户的 Class,也会带来一系列的安全问题。所以,Sonic 采用拓展 ClassPath URL 路径来实现文件的修改和新增。
并且存在这么一种场景,多个业务侧的项目引入相同的 JAR 包,在 JAR 里面配置 MyBatis 的XML 和注解。在此类情况下,Sonic 没有办法直接来修改 JAR 包中源文件,通过拓展路径的方式可以不需要关注 JAR 包,来修改 JAR 包中某一文件和 XML。同理,采用此类方法可以进行整个 JAR 包的热替换。
JVM Class重载
JVM 的字节码批量重载逻辑,通过新的字节码二进制流和旧的 Class 对象生成ClassDefinition 定 义,instrumentation.redefineClasses(definitions), 来 触 发JVM 重载,重载过后将触发初始化时 Spring 插件注册的 Transfrom。
新增 class Sonic 如何保证可以加载到 Classloader 上下文中?由于项目在远程执行,所以运行环境复杂,有可能是 JAR 包方式启动(Spring Boot),也有可能是普通项目,也有可能是 War Web 项目,针对此类情况 Sonic 做了一层 ClassloaderURL 拓展。
Spring Bean 重载
Spring XML重载
当用户修改 / 新增 Spring XML 时,需要对 XML 中所有 Bean 进行重载
重新 Reload 之后,将 Spring 销毁后重启。需要注意的是:XML 修改方式改动较大,可能涉及到全局的 AOP 的配置以及前置和后置处理器相关的内容,影响范围为全局,所以目前只放开普通的 XML Bean 标签的新增 / 修改,其他能力酌情逐步放开。
MyBatis 热部署
Spring MyBatis 热部署的主要处理流程是在启动期间获取所有 Configuration 路径,并维护它和 Spring Context 的对应关系,在热部署 Class、XML 时去匹配Configuration,从而重新加载 Configuration 以达到热部署的目的。
总结
当前热部署主要是注重Spring Bean、Spring MVC、MyBatis 的重载流程。对于其他开发框架,需要更加深入了解底层实现,以达到兼容性。
补充说明
此文只做博主了解热部署相关文档做的简单说明,该插件实现难度非常大。