在运行的过程中升级 Web 应用,如果你不想重启系统,实现的方式有两种:热加载和热部署。

热加载和热部署的实现

它们的实现跟类加载机制有关,具体来说就是:

热加载的实现方式是 Web 容器启动一个后台线程,定期检测类文件的变化,如果有变化,就重新加载类,在这个过程中不会清空 HTTP Session ,一般用在开发环境。

热部署原理类似,也是由后台线程定时检测 Web 应用的变化,但它会重新加载整个 Web 应用###。这种方式会清空 Session,比热加载更加干净、彻底,一般用在生产环境。

周期性任务处理框架

后台线程做周期性的任务。线程池中的 ScheduledThreadPoolExecutor,它除了具有线程池的功能,还能够执行周期性的任务。

周期性任务处理框架的核心就是 ContainerBackgroundProcessor 任务类 对应一个 顶层 container,使用一个后台线程,就可以遍历具体的子 container,递归调用容器接口的 backgroundProcess 方法。

ContainerBackgroundProcessor 实现了 Runnable 接口,是周期性执行的任务类,其“宿主类”,是 ContainerBase 的类实例。

有了 ContainerBase 基类中的 backgroundProcess 方法,只需要在顶层容器, Engine 容器中启动一个后台线程,那么这个线程不但会执行 Engine 容器的周期性任务,它还会执行所有子容器的周期性任务。###子容器和组件不用新启动线程,如果有周期性任务要执行,就重新实现 backgroundProcess 方法;这样的设计显得优雅和整洁。

热加载

有了周期性任务处理“框架”,可以在 Context 容器中实现热加载。Context 容器的 backgroundProcess 方法是这样实现的:
Context 容器通过 WebappLoader 来检查类文件是否有更新。
通过 Session 管理器来检查是否有 Session 过期
通过资源管理器来检查静态资源是否有更新
调用了父类 ContainerBase 的 backgroundProcess 方法。

WebappLoader 是如何实现热加载的?
它主要是调用了 Context 容器的 reload 方法,而 Context 的 reload 方法比较复杂。
在 reload 过程中,类加载器发挥着关键作用。一个 Context 容器对应一个类加载器,类加载器在销毁的过程中会把它加载的所有类也全部销毁。Context 容器在启动过程中,会创建一个新的类加载器来加载新的类文件。

reload 总结起来,主要完成了下面这些任务:
停止和销毁 Context 容器及其所有子容器 Wrapper,也就是说 Wrapper 里面 Servlet 实例也被销毁了。
停止和销毁 Context 容器关联的 Listener 和 Filter。
停止和销毁 Context 下的 Pipeline 和各种 Valve。
停止和销毁 Context 的类加载器,以及类加载器加载的类文件资源。
启动 Context 容器,在这个过程中会重新创建前面四步被销毁的资源。

在 Context 的 reload 方法里,并没有调用 Session 管理器的 destroy 方法,关联的 Session 没有被销毁。

热部署

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

热部署又是由哪个容器来实现的呢?应该不是由 Context,因为热部署过程中 Context 容器被销毁了,其实是 Context 的父容器 Host 容器完成的。

跟 Context 不一样,Host 容器并没有在 backgroundProcess 方法中进行周期性检测,而是###通过周期事件监听器 HostConfig 来实现的。“周期事件”达到时,HostConfig 会做什么事呢?检查 webapps 目录下的所有 Web 应用的目录级别的变化。

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

为什么不用 backgroundProcess 因为热加载是频繁的,热部署是不频繁的吧,开一个单独的线程???