Tomcat热加载和热部署

2. Tomcat热加载和热部署

在项目开发过程中,经常要改动Java/JSP 文件,但是又不想重新启动Tomcat,有两种方式:热加载和热部署。热部署表示重新部署应⽤,它的执行主体是Host。 热加载表示重新加载class,它的执行主体是Context。

  • 热加载:在server.xml -> context 标签中 设置 reloadable="true"
java 复制代码
<Context docBase="D:\mvc" path="/mvc"  reloadable="true" />
  • 热部署:在server.xml -> Host标签中 设置 autoDeploy="true"
java 复制代码
<Host name="localhost"  appBase="webapps"
      unpackWARs="true" autoDeploy="true">

它们的区别是:

  • 热加载的实现方式是 Web 容器启动一个后台线程,定期检测类文件的变化,如果有变化,就重新加载类,在这个过程中不会清空 Session ,一般用在开发环境。
  • 热部署原理类似,也是由后台线程定时检测 Web 应用的变化,但它会重新加载整个 Web 应用。这种方式会清空 Session,比热加载更加干净、彻底,一般用在生产环境。

思考:Tomcat 是如何用后台线程来实现热加载和热部署的?

2.1 Tomcat开启后台线程执行周期性任务

Tomcat 通过开启后台线程ContainerBase.ContainerBackgroundProcessor,使得各个层次的容器组件都有机会完成一些周期性任务。我们在实际工作中,往往也需要执行一些周期性的任务,比如监控程序周期性拉取系统的健康状态,就可以借鉴这种设计。

Tomcat9 是通过ScheduledThreadPoolExecutor来开启后台线程的,它除了具有线程池的功能,还能够执行周期性的任务。

此后台线程会调用当前容器的 backgroundProcess 方法,以及递归调用子孙的 backgroundProcess 方法,backgroundProcess 方法会触发容器的周期性任务。

有了 ContainerBase 中的后台线程和 backgroundProcess 方法,各种子容器和通用组件不需要各自弄一个后台线程来处理周期性任务,这样的设计显得优雅和整洁。

2.2 Tomcat热加载实现原理

有了 ContainerBase 的周期性任务处理"框架",作为具体容器子类,只需要实现自己的周期性任务就行。而 Tomcat 的热加载,就是在 Context 容器中实现的。Context 容器的 backgroundProcess 方法是这样实现的:

java 复制代码
//  StandardContext#backgroundProcess

//WebappLoader 周期性的检查 WEB-INF/classes 和 WEB-INF/lib 目录下的类文件
// 热加载
Loader loader = getLoader();
if (loader != null) {
    loader.backgroundProcess();        
}

WebappLoader 实现热加载的逻辑:它主要是调用了 Context 容器的 reload 方法,先stop Context容器,再start Context容器。具体的实现:

1)停止和销毁 Context 容器及其所有子容器,子容器其实就是 Wrapper,也就是说 Wrapper 里面 Servlet 实例也被销毁了。

2)停止和销毁 Context 容器关联的 Listener 和 Filter。

3)停止和销毁 Context 下的 Pipeline 和各种 Valve。

4)停止和销毁 Context 的类加载器,以及类加载器加载的类文件资源。

5)启动 Context 容器,在这个过程中会重新创建前面四步被销毁的资源。

在这个过程中,类加载器发挥着关键作用。一个 Context 容器对应一个类加载器,类加载器在销毁的过程中会把它加载的所有类也全部销毁。Context 容器在启动过程中,会创建一个新的类加载器来加载新的类文件。

2.3 Tomcat热部署实现原理

热部署跟热加载的本质区别是,热部署会重新部署 Web 应用,原来的 Context 对象会整个被销毁掉,因此这个 Context 所关联的一切资源都会被销毁,包括 Session。

Host 容器并没有在 backgroundProcess 方法中实现周期性检测的任务,而是通过监听器 HostConfig 来实现的

java 复制代码
// HostConfig#lifecycleEvent
// 周期性任务
if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
    check();
}
protected void check() {
    if (host.getAutoDeploy()) {
        // Check for resources modification to trigger redeployment
        DeployedApplication[] apps = deployed.values().toArray(new DeployedApplication[0]);
        for (DeployedApplication app : apps) {
            if (tryAddServiced(app.name)) {
                try {
                    // 检查 Web 应用目录是否有变化
                    checkResources(app, false);
                } finally {
                    removeServiced(app.name);
                }
            }
        }
        // Check for old versions of applications that can now be undeployed
        if (host.getUndeployOldVersions()) {
            checkUndeploy();
        }

        // Hotdeploy applications
        //热部署
        deployApps();
    }

HostConfig 会检查 webapps 目录下的所有 Web 应用:

  • 如果原来 Web 应用目录被删掉了,就把相应 Context 容器整个销毁掉。
  • 是否有新的 Web 应用目录放进来了,或者有新的 WAR 包放进来了,就部署相应的 Web 应用。

因此 HostConfig 做的事情都是比较"宏观"的,它不会去检查具体类文件或者资源文件是否有变化,而是检查 Web 应用目录级别的变化。

相关推荐
折哥的程序人生 · 物流技术专研几秒前
第8篇:模板方法模式的优缺点与面试高频考点
java·设计模式·面试·模板方法模式·行为型模式·优缺点·扩充系列
人道领域37 分钟前
【0-1的agent进阶篇】Prompt 与上下文工程
java·开发语言·prompt·mcp
KINGSEA_1681 小时前
AgentScope Java 2.0 架构解析
java
折哥的程序人生 · 物流技术专研1 小时前
Java 23 种设计模式:从踩坑到精通 | 备忘录模式 —— 快照与撤销,给对象装一个“后悔药”
java·设计模式·备忘录模式·行为型模式·java设计模式·从踩坑到精通·撤销重做
ikun_文2 小时前
Java里面异常Exception体系知识点
java
不负岁月无痕2 小时前
STL -- C++ 红黑树封装 Map 和 Set
java·c语言·开发语言·c++·面试
小园子的小菜2 小时前
Java核心基础:反射与泛型底层原理及实战用法详解
java·开发语言
淘源码A2 小时前
多院区集团化云PACS系统源码,原生兼容国产软硬件环境
java·云计算·源码·saas·pacs·医学影像系统
何中应2 小时前
主流 OpenJDK 发行版下载安装
java·oracle·jdk·openjdk
daad7772 小时前
记录vscode连接matlab启动环境搭建
java·vscode·matlab