首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >tomcat热加载、热部署-源码解析

tomcat热加载、热部署-源码解析

作者头像
逍遥壮士
发布2022-12-01 15:40:40
6460
发布2022-12-01 15:40:40
举报
文章被收录于专栏:技术趋势技术趋势

上文:tomcat线程模型-源码解析


热加载和热部署是什么?

请查看原来的写过的文章:热部署和热加载有什么区别?

tomcat热加载和执热部署都是通过后台进程检测项目中的.class和目录是否发生变化。

热加载与热部布署检测

热加载

开启热加载 在 context.xml 中配置 reloadable="true"

<Context reloadable="true">

配置完后tomcat运行中会检测WEB-INF/classes和WEB-INF/lib 是否发生变化,如果发生变化进行加载。

那么热加载的流程是: 设置当前context(上下文)不能接受请求的标志为:true, 停止当前的context(上下文),启动当前context(上下文),重新设置当前context不能接收请求的标志为:false。

代码位置:org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor#run

每隔10s就会自动检测是否有代码变动。

源码实现

热部署和热加载为该线程

位置:org.apache.catalina.core.ContainerBase#threadStart

protected void threadStart() {

    if (thread != null) {
        return;
    }
    if (backgroundProcessorDelay <= 0) {
        return;
    }

    threadDone = false;
    //tomcat的热加载和热部署都是通过这个线程进行的;
    String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
    thread = new Thread(new ContainerBackgroundProcessor(), threadName);
    thread.setDaemon(true);
    
    thread.start();

}

检测文件是否发生变动,每隔10s钟;

位置:org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor

@Override
public void run() {
    Throwable t = null;
    String unexpectedDeathMessage = sm.getString(
            "containerBase.backgroundProcess.unexpectedThreadDeath",
            Thread.currentThread().getName());
    try {
        while (!threadDone) {
            try {
                //休眠10s
                Thread.sleep(backgroundProcessorDelay * 1000L);
            } catch (InterruptedException e) {
                // Ignore
            }
            if (!threadDone) {
                //检测下级变动
                processChildren(ContainerBase.this);
            }
        }
    } catch (RuntimeException|Error e) {
        t = e;
        throw e;
    } finally {
        if (!threadDone) {
            log.error(unexpectedDeathMessage, t);
        }
    }
}

位置:org.apache.catalina.core.StandardContext#backgroundProcess

@Override
public void backgroundProcess() {

    if (!getState().isAvailable()) {
        return;
    }
    //webapploader每隔10s检查一次
    //检查的目录 WEB-INF/classes、web-INF/LIB 是否有文件变动
    Loader loader = getLoader();
    if (loader != null) {
        try {
            loader.backgroundProcess();
        } catch (Exception e) {
            log.warn(sm.getString(
                    "standardContext.backgroundProcess.loader", loader), e);
        }
    }
    //检查是否有有session过期
    Manager manager = getManager();
    if (manager != null) {
        try {
            manager.backgroundProcess();
        } catch (Exception e) {
            log.warn(sm.getString(
                    "standardContext.backgroundProcess.manager", manager),
                    e);
        }
    }
    //检查静态资源是否有更新
    WebResourceRoot resources = getResources();
    if (resources != null) {
        try {
            resources.backgroundProcess();
        } catch (Exception e) {
            log.warn(sm.getString(
                    "standardContext.backgroundProcess.resources",
                    resources), e);
        }
    }
    InstanceManager instanceManager = getInstanceManager();
    if (instanceManager instanceof DefaultInstanceManager) {
        try {
            ((DefaultInstanceManager)instanceManager).backgroundProcess();
        } catch (Exception e) {
            log.warn(sm.getString(
                    "standardContext.backgroundProcess.instanceManager",
                    resources), e);
        }
    }
    super.backgroundProcess();
}

检测变动类

源码位置:org.apache.catalina.loader.WebappLoader#backgroundProcess

@Override
public void backgroundProcess() {
    //判断是否存在更新 存在进行更新
    if (reloadable && modified()) {
        try {
            Thread.currentThread().setContextClassLoader
                (WebappLoader.class.getClassLoader());
            if (context != null) {
                context.reload();
            }
        } finally {
            if (context != null && context.getLoader() != null) {
                Thread.currentThread().setContextClassLoader
                    (context.getLoader().getClassLoader());
            }
        }
    }
}

###以下是热部署检测。

热部署的事件是在检测热加载后进行的。位置是:org.apache.catalina.core.ContainerBase#backgroundProcess

org.apache.catalina.startup.HostConfig#lifecycleEvent

@Override
public void lifecycleEvent(LifecycleEvent event) {

    // Identify the host we are associated with
    try {
        host = (Host) event.getLifecycle();
        if (host instanceof StandardHost) {
            setCopyXML(((StandardHost) host).isCopyXML());
            setDeployXML(((StandardHost) host).isDeployXML());
            setUnpackWARs(((StandardHost) host).isUnpackWARs());
            setContextClass(((StandardHost) host).getContextClass());
        }
    } catch (ClassCastException e) {
        log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
        return;
    }

    // Process the event that has occurred
    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();
    }
}

org.apache.catalina.core.StandardContext#reload

@Override
public synchronized void reload() {

    // Validate our current component state
    if (!getState().isAvailable()) {
        throw new IllegalStateException
            (sm.getString("standardContext.notStarted", getName()));
    }

    if(log.isInfoEnabled()) {
        log.info(sm.getString("standardContext.reloadingStarted",
                getName()));
    }

    // Stop accepting requests temporarily. 停止标识
    setPaused(true);

    try {
        //停上方法
        stop();
    } catch (LifecycleException e) {
        log.error(
            sm.getString("standardContext.stoppingContext", getName()), e);
    }

    try {
        //启动
        start();
    } catch (LifecycleException e) {
        log.error(
            sm.getString("standardContext.startingContext", getName()), e);
    }
    //取消停止标识
    setPaused(false);

    if(log.isInfoEnabled()) {
        log.info(sm.getString("standardContext.reloadingCompleted",
                getName()));
    }

}

最后

以上只是热加载和热部署的流程,其实到最后还有类的卸载和加载这里又是另一外一套打破双亲委派机制的流程,后续抽时间再深入。那么简单来说就是热加载是tomcat检测WEB-INF下面的classes和lib中文件及目录的变动而重新加载,如果重新加载后需要重新部署会暂停当前进程进行重新部署(包含新项目),当然这也是需要配置的。以上仅为简单的逻辑了解,需要深入同学请继续。还有注意一项是热加载其实是有一个监听器,通过while死循环每隔10s进行检查一次。

参考文章:

https://blog.csdn.net/chen_xiaoqi/article/details/120748629#:~:text=tomcat%E7%9A%84%E7%83%AD%E5%8A%A0%E8%BD%BD%E5%92%8C%E7%83%AD%E9%83%A8%E7%BD%B2%E6%98%AF%E9%80%9A%E8%BF%87ScheduledThreadPoolExecutor%20%E5%AE%9A%E6%97%B6%E7%BA%BF%E7%A8%8B%E6%B1%A0%E6%9D%A5%E5%AE%9E%E7%8E%B0%E7%9A%84%E3%80%82%20bgFuture%20%3D%20exec.scheduleWithFixedDelay%28new%20ContainerBackgroundProcessor%28%29%2C%2F%2F%E8%A6%81%E6%89%A7%E8%A1%8C%E7%9A%84Runnable,backgroundProcessorDelay%2C%20%2F%2F%E7%AC%AC%E4%B8%80%E6%AC%A1%E6%89%A7%E8%A1%8C%E5%BB%B6%E8%BF%9F%E5%A4%9A%E4%B9%85%20backgroundProcessorDelay%2C%20%2F%2F%E4%B9%8B%E5%90%8E%E6%AF%8F%E6%AC%A1%E6%89%A7%E8%A1%8C%E9%97%B4%E9%9A%94%E5%A4%9A%E4%B9%85%20TimeUnit.SECONDS%29%3B%20%2F%2F%E6%97%B6%E9%97%B4%E5%8D%95%E4%BD%8D%201

https://blog.51cto.com/u_11440114/3225039

https://blog.csdn.net/qq_33590654/article/details/116753362

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-08-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 技术趋势 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档