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


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


    热加载和热部署是什么?

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

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

    热加载与热部布署检测

    热加载

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

    "true">

    f78f5133c5e3fe00f2d41c6062bdc194.png

    配置完后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

    1. protected void threadStart() {
    2.     if (thread != null) {
    3.         return;
    4.     }
    5.     if (backgroundProcessorDelay <= 0) {
    6.         return;
    7.     }
    8.     threadDone = false;
    9.     //tomcat的热加载和热部署都是通过这个线程进行的;
    10.     String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
    11.     thread = new Thread(new ContainerBackgroundProcessor(), threadName);
    12.     thread.setDaemon(true);
    13.     
    14.     thread.start();
    15. }

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

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

    1. @Override
    2. public void run() {
    3.     Throwable t = null;
    4.     String unexpectedDeathMessage = sm.getString(
    5.             "containerBase.backgroundProcess.unexpectedThreadDeath",
    6.             Thread.currentThread().getName());
    7.     try {
    8.         while (!threadDone) {
    9.             try {
    10.                 //休眠10s
    11.                 Thread.sleep(backgroundProcessorDelay * 1000L);
    12.             } catch (InterruptedException e) {
    13.                 // Ignore
    14.             }
    15.             if (!threadDone) {
    16.                 //检测下级变动
    17.                 processChildren(ContainerBase.this);
    18.             }
    19.         }
    20.     } catch (RuntimeException|Error e) {
    21.         t = e;
    22.         throw e;
    23.     } finally {
    24.         if (!threadDone) {
    25.             log.error(unexpectedDeathMessage, t);
    26.         }
    27.     }
    28. }

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

    1. @Override
    2. public void backgroundProcess() {
    3.     if (!getState().isAvailable()) {
    4.         return;
    5.     }
    6.     //webapploader每隔10s检查一次
    7.     //检查的目录 WEB-INF/classes、web-INF/LIB 是否有文件变动
    8.     Loader loader = getLoader();
    9.     if (loader != null) {
    10.         try {
    11.             loader.backgroundProcess();
    12.         } catch (Exception e) {
    13.             log.warn(sm.getString(
    14.                     "standardContext.backgroundProcess.loader", loader), e);
    15.         }
    16.     }
    17.     //检查是否有有session过期
    18.     Manager manager = getManager();
    19.     if (manager != null) {
    20.         try {
    21.             manager.backgroundProcess();
    22.         } catch (Exception e) {
    23.             log.warn(sm.getString(
    24.                     "standardContext.backgroundProcess.manager", manager),
    25.                     e);
    26.         }
    27.     }
    28.     //检查静态资源是否有更新
    29.     WebResourceRoot resources = getResources();
    30.     if (resources != null) {
    31.         try {
    32.             resources.backgroundProcess();
    33.         } catch (Exception e) {
    34.             log.warn(sm.getString(
    35.                     "standardContext.backgroundProcess.resources",
    36.                     resources), e);
    37.         }
    38.     }
    39.     InstanceManager instanceManager = getInstanceManager();
    40.     if (instanceManager instanceof DefaultInstanceManager) {
    41.         try {
    42.             ((DefaultInstanceManager)instanceManager).backgroundProcess();
    43.         } catch (Exception e) {
    44.             log.warn(sm.getString(
    45.                     "standardContext.backgroundProcess.instanceManager",
    46.                     resources), e);
    47.         }
    48.     }
    49.     super.backgroundProcess();
    50. }

    检测变动类

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

    1. @Override
    2. public void backgroundProcess() {
    3.     //判断是否存在更新 存在进行更新
    4.     if (reloadable && modified()) {
    5.         try {
    6.             Thread.currentThread().setContextClassLoader
    7.                 (WebappLoader.class.getClassLoader());
    8.             if (context != null) {
    9.                 context.reload();
    10.             }
    11.         } finally {
    12.             if (context != null && context.getLoader() != null) {
    13.                 Thread.currentThread().setContextClassLoader
    14.                     (context.getLoader().getClassLoader());
    15.             }
    16.         }
    17.     }
    18. }

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

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

    org.apache.catalina.startup.HostConfig#lifecycleEvent

    1. @Override
    2. public void lifecycleEvent(LifecycleEvent event) {
    3.     // Identify the host we are associated with
    4.     try {
    5.         host = (Host) event.getLifecycle();
    6.         if (host instanceof StandardHost) {
    7.             setCopyXML(((StandardHost) host).isCopyXML());
    8.             setDeployXML(((StandardHost) host).isDeployXML());
    9.             setUnpackWARs(((StandardHost) host).isUnpackWARs());
    10.             setContextClass(((StandardHost) host).getContextClass());
    11.         }
    12.     } catch (ClassCastException e) {
    13.         log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
    14.         return;
    15.     }
    16.     // Process the event that has occurred
    17.     if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
    18.         //执行检查
    19.         check();
    20.     } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
    21.         beforeStart();
    22.     } else if (event.getType().equals(Lifecycle.START_EVENT)) {
    23.         start();
    24.     } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
    25.         stop();
    26.     }
    27. }

    org.apache.catalina.core.StandardContext#reload

    1. @Override
    2. public synchronized void reload() {
    3.     // Validate our current component state
    4.     if (!getState().isAvailable()) {
    5.         throw new IllegalStateException
    6.             (sm.getString("standardContext.notStarted", getName()));
    7.     }
    8.     if(log.isInfoEnabled()) {
    9.         log.info(sm.getString("standardContext.reloadingStarted",
    10.                 getName()));
    11.     }
    12.     // Stop accepting requests temporarily. 停止标识
    13.     setPaused(true);
    14.     try {
    15.         //停上方法
    16.         stop();
    17.     } catch (LifecycleException e) {
    18.         log.error(
    19.             sm.getString("standardContext.stoppingContext", getName()), e);
    20.     }
    21.     try {
    22.         //启动
    23.         start();
    24.     } catch (LifecycleException e) {
    25.         log.error(
    26.             sm.getString("standardContext.startingContext", getName()), e);
    27.     }
    28.     //取消停止标识
    29.     setPaused(false);
    30.     if(log.isInfoEnabled()) {
    31.         log.info(sm.getString("standardContext.reloadingCompleted",
    32.                 getName()));
    33.     }
    34. }

    最后

        以上只是热加载和热部署的流程,其实到最后还有类的卸载和加载这里又是另一外一套打破双亲委派机制的流程,后续抽时间再深入。那么简单来说就是热加载是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

  • 相关阅读:
    【计算机网络】初步了解TCP/IP四层模型
    数据库新开账号,并授予了相应表的查询权限。访问时,其他PC端远程被拒绝
    线性【SVM】数学原理和算法实现
    [vue3] Axios 使用
    Python怎么计算时间差(含代码实例)
    Go web 开发数据库管理平台,利用远程过程调用(RPC)实现对MySQL数据库的管理和使用
    【Java毕设】基于idea Java的在线考试系统(附源码+课件)
    asp.net教师调课系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
    springcloud15:config配置中心+Bus消息总线
    Web3:Fediverse低效是一种平衡 - berk
  • 原文地址:https://blog.csdn.net/qq_16498553/article/details/126576935