el-tab 内容每次重新加载_死磕Tomcat系列(6)——Tomcat如何做到热加载和热部署的...

点击上方“Java技术前线”,选择“置顶或者星标”

与你一起成长

热部署就是在服务器运行时重新部署项目,热加载即在在运行时重新加载class,从而升级应用。

通常情况下在开发环境中我们使用的是热加载,因为热加载的实现的方式在Web容器中启动一个后台线程,定期检测相关文件的变化,如果有变化就重新加载类,这个过程不会清空Session。而在生产环境我们一般应用的是热部署,热部署也是在Web应用后台线程定期检测,发现有变化就会重新加载整个Web应用,这种方式更加彻底会清空Session。

热加载

热加载其实我们在开发过程中经常使用,例如我们使用Idea开发时,我们在设置页面可以进行设置,当修改文件时,我们可以选择不重启项目,选择重新加载此文件。而在Tomcat中也能设置,Tomcat默认情况下是不开启热加载的。需要在Tomcat路径下的 Context.xml中配置 reloadable参数来开启这个功能。

<Context reloadable="true"/>

复制代码

我们演示一下Tomcat是如何热加载的。在webapp下我们新建了一个项目,里面的Servlet文件如下

public class MyServlet extends HttpServlet {

@Override

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

System.out.println("MyServlet 在处理 get()请求...");

PrintWriter out = response.getWriter();

response.setContentType("text/html;charset=utf-8");

out.println(">My Servlet Version1!
");

}

@Override

protected void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

System.out.println("MyServlet 在处理 post()请求...");

PrintWriter out = response.getWriter();

response.setContentType("text/html;charset=utf-8");

out.println(">My Servlet Version1!
");

}

}

复制代码

目录结构如下

-webapp

-- mywebapp

-- WEB-INF

-- web.xml

-- classes

-- MyServlet.class

复制代码

为了演示Tomcat运行时能修改class文件能够动态加载。我们分为以下三步

  1. 正常启动Tomcat。输入 http://localhost:8080/mywebapp/myservlet,观察页面输出

  2. 在Tomcat启动的情况下修改 MyServlet文件后覆盖原来的class文件

  3. 再次观察页面情况。观察页面输出是否修改

下面直接用动态图演示效果,更直观一些。

9afed7e138d0e882e2460dff1131cae8.gif

我们可以看到在Tomcat运行的情况下,直接替换class文件是能够直接生效的。那么Tomcat是如何做到的呢?其实我们可以自己推导一下。

  1. 所有的class文件都是交由类加载来管理的

  2. 如果换了class文件是不是只需要更换相应的类加载器重新加载就行

那么接下来我们来验证我们的结论,看一下在Tomcat中是如何实现热加载的。Tomcat要监听class文件是否变化应该是新起了一个线程来观测。那么看到在Context的启动方法中,看到调用了 threadStart的方法。

protected void threadStart() {

backgroundProcessorFuture = Container.getService(this).getServer().getUtilityExecutor()

.scheduleWithFixedDelay(new ContainerBackgroundProcessor(),//要执行的Runnable

backgroundProcessorDelay, //第一次执行延迟多久

backgroundProcessorDelay, //之后每次隔多久执行一次

TimeUnit.SECONDS); //时间单位

}

}

复制代码

其中在后台开启周期性的任务,使用了Java提供的 ScheduledThreadPoolExecutor。除了能周期性执行任务以外还有线程池的功能。上面代码中调用了 scheduleWithFixedDelay方法,第一个传入的参数就是要执行的任务。我们接下来看任务类 ContainerBackgroundProcessor是如何实现的。

protected class ContainerBackgroundProcessor implements Runnable {

@Override

public void run() {

// 请注意这里传入的参数是 " 宿主类 " 的实例

processChildren(ContainerBase.this);

}

protected void processChildren(Container container) {

try {

//1. 调用当前容器的 backgroundProcess 方法。

container.backgroundProcess();

//2. 遍历所有的子容器,递归调用 processChildren,

// 这样当前容器的子孙都会被处理

Container[] children = container.findChildren();

for (int i = 0; i < children.length; i++) {

// 这里会判断子容器如果已经启动了后台线程,那么这里就不会启动了

if (children[i].getBackgroundProcessorDelay() <= 0) {

processChildren(children[i]);

}

}

} catch (Throwable t) { ... }

复制代码

上面代码中我们可以知道具体的后台监听代码是在 backgroundProcess方法中实现的。那么我们看 Context容器的 backgroundProcess方法是如何实现的。

public void backgroundProcess() {

//WebappLoader 周期性的检查 WEB-INF/classes 和 WEB-INF/lib 目录下的类文件

Loader loader = getLoader();

if (loader != null) {

loader.backgroundProcess();

}

............省略

}

复制代码

进去 loader.backgroundProcess();中我们可以看到

public void backgroundProcess() {

//此处判断热加载开关是否开启和监控的文件夹中文件是否有修改

if (reloadable && modified()) {

try {

Thread.currentThread().setContextClassLoader

(WebappLoader.class.getClassLoader());

if (context != null) {

//Context重启

context.reload();

}

} finally {

if (context != null && context.getLoader() != null) {

Thread.currentThread().setContextClassLoader

(context.getLoader().getClassLoader());

}

}

}

}

复制代码

我们可以发现Tomcat热加载的步骤

  1. 如果发现有文件发生变化,热加载开关开启

  2. 关闭Context容器

  3. 重启Context容器

在这个过程中,最重要的部分其实就是类加载器了。因为一个Context容器对应一个类加载器。所以在销毁Context容器的时候也连带着将其类加载器一并销毁了。Context在重启的过程中也会创建新的类加载器来加载我们新建的文件。

热部署

如果还是不懂热部署是什么的,下面演示一遍应该就明白了。Tomcat在启动的时候会将其目录下webapp中war包解压后然后封装为一个Context供外部访问。那么热部署就是在程序运行时,如果我们修改了War包中的东西。那么Tomcat就会删除之前的War包解压的文件夹,重新解压新的War包。

d664671ead9293d8033596e7bb6a2edb.gif

我们发现上面动图中在Tomcat运行时,我们修改了War包的信息,它就会将原来的删除然后重新生成一份。

我们从上面的动图中其实就看出了热部署和热加载的区别了。热部署是将文件夹删除然后重新解压包。那么热加载是由Context容器负责的。那么热部署又是由哪个容器负责呢?因为一个文件夹对应一个Context。既然文件夹都删除了,那么肯定不是由Context容器负责了。那么应该就是Context的父容器Host来负责。

我们可以看到 Host容器并没有实现自己的 backgroundProcess方法。那么它是如何监听的呢?既然它没有实现方法,肯定是调用了父类的 backgroundProcess方法。我们可以看到在父类的 backgroundProcess

@Override

public void backgroundProcess() {

. ...........省略

fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);

}

复制代码

可以看到周期事件的监听器。而 Host的事件监听器是 HostConfig类的 lifecycleEvent方法

@Override

public void lifecycleEvent(LifecycleEvent event) {

if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {// 周期事件

check();

} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {// 开始之前事件

beforeStart();

} else if (event.getType().equals(Lifecycle.START_EVENT)) { // 开始事件

start();

} else if (event.getType().equals(Lifecycle.STOP_EVENT)) { // 结束事件

stop();

}

}

复制代码

我们可以看 check方法

protected void check() {

if (host.getAutoDeploy()) {

// 检查Host下所有已经部署的web应用

DeployedApplication[] apps =

deployed.values().toArray(new DeployedApplication[0]);

for (int i = 0; i < apps.length; i++) {

if (!isServiced(apps[i].name))

checkResources(apps[i], false);

}

// 检查Web应用是否有变化

if (host.getUndeployOldVersions()) {

checkUndeploy();

}

// 执行部署

deployApps();

}

}

复制代码

热部署的步骤其实也可以简化为三步骤

  1. 检查Host管理下的所有web应用

  2. 如果原来的Web应用被删除,就将相应Context容器删除

  3. 如果有新War包放进来,就部署相应的War包

参考文章

  • 深入拆解Tomcat

  • my.oschina.net/xianggao/bl…

作者:不学无数的程序员

来源:https://juejin.im/post/5d2709d7e51d4510664d1774

热门内容:

  • 888G面试资源分享
  • Mybatis教程1:MyBatis快速入门
  • MyBatis教程2:使用MyBatis对表执行CRUD操作
  • MyBatis教程3:优化MyBatis配置文件中的配置
  • MyBatis教程4:解决字段名与实体类属性名不相同的冲突
  • 2019年Java经典面试题汇总
  • Maven教程1:Maven入门
  • Maven教程2:Maven项目构建过程练习
  • Maven教程3:使用Maven构建项目
  • Maven教程4:Maven核心概念
  • Maven教程5: 聚合与继承
  • Maven教程6: Maven与Eclipse整合
  • Maven教程7:eclipse中使用Maven创建Web项目.md
  • Maven教程8: 使用Maven构建多模块项目
  • Maven教程9: 使用Nexus搭建Maven私服

cbdcb2e35ea5003dbb76583e704af99b.png

喜欢就点个"在看"呗^_^

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值