Tomcat源码分析---后台线程管理

Tomcat后台管理以前也接触过,比如对普通session,持久session,集群session的超时管理,就是后台线程去做的。
在看时序图之前首先看一下后台线程,也就是ContainerBase的内部类ContainerBackgroundProcessor里面的一个重要方法
processChildren(),它是由ContainerBackgroundProcessor#run()每隔一定的时间调用的。
processChildren()方法内容如下:
         protected void processChildren( Container container,  ClassLoader cl) {
            try {
                if (container.getLoader() !=  null) {
                     Thread.currentThread().setContextClassLoader
                        (container.getLoader().getClassLoader());
                }
                container.backgroundProcess();
            }  catch ( Throwable t) {
                log.error(" Exception invoking periodic operation: ", t);
            }  finally {
                 Thread.currentThread().setContextClassLoader(cl);
            }
             Container[] children = container.findChildren();
             for ( int i = 0; i < children.length; i++) {
                 if (children[i].getBackgroundProcessorDelay() <= 0) {
                    processChildren(children[i], cl);
                }
            }
        }

实际上它就是调用当前容器的backgroundProcess(),然后再递归执行每个子容器,再调用子容器的backgroundProcess()。

下面来看看ContainerBase#backgroundProcess():
     public void backgroundProcess() {        
         if (!started)
             return;
         if (cluster !=  null) {
             try {
                cluster.backgroundProcess();
            }  catch ( Exception e) {
                log.warn(sm.getString(" containerBase.backgroundProcess.cluster", cluster), e);                
            }
        }
         if (loader !=  null) {
             try {
                loader.backgroundProcess();
            }  catch (Exception e) {
                log.warn(sm.getString(" containerBase.backgroundProcess.loader", loader), e);                
            }
        }
         if (manager !=  null) {
             try {
                manager.backgroundProcess();
            }  catch ( Exception e) {
                log.warn(sm.getString(" containerBase.backgroundProcess.manager", manager), e);                
            }
        }
         if (realm !=  null) {
             try {
                realm.backgroundProcess();
            }  catch (Exception e) {
                log.warn(sm.getString(" containerBase.backgroundProcess.realm", realm), e);                
            }
        }
         Valve current = pipeline.getFirst();
         while (current !=  null) {
             try {
                current.backgroundProcess();
            }  catch ( Exception e) {
                log.warn(sm.getString(" containerBase.backgroundProcess.valve", current), e);                
            }
            current = current.getNext();
        }
        lifecycle.fireLifecycleEvent( Lifecycle. PERIODIC_EVENTnull);
    }

这段代码的作用就是调用容器相关联的Cluster,Loader,Manager,Realm的后台方法,再调用和它管理的所有valve,最后触发一个监听事件。这两段代码就是后台管理的核心了。Tomcat对后台管理就是像一个倒着的树一样,一步一步执行下来的。

下面进入正题,看看后台管理调用的时序图:


   首先是从CatalinaShutdownHook开始,先调用自己的processChildren(),然后调用第一个容器StandardEngine,引擎会执行和它相关联的Cluster,Loader,Manager,Realm的后台方法,这里只关联了Realm,所以执行Realm的后台方法,这不很简单,一个空方法,所以时序图省略了。然后引擎开始调用它的所有子容器,就是StandardHost,和主机关联的组件没有,所以不执行,valve也是空方法,不要以为这里就完事了,这里会触发一个监听事件,所以会调用到HostConfig,这里可能就会触发重新部署了。
   HostConfing首先调用一个checkResources(),这主要是对部署描述文件进行检查,检查他们的时间戳,看看是否改变了,如果改变的话,就将当前的上下文停止掉,并且从自己的部署列表中删除这个上下文。注意,因为这里会有一个重部署的检查工作,所以不会简单的停止启动就完事了,后面还有检查工作。停止完后,再调用自身的deployApps(),这个方法就是启动时候调用的部署方法,里面会调用三种部署应用,这里不是把每个应用都再部署一遍,如果发现某个应用不在自己的列表里,就会执行一次部署工作,这个部署工作就和启动时的部署一模一样了,所以这里就省略了,具体请看《Tomcat源码分析---启动过程》一文。
  主机的后台方法执行完了,会回到ContainerBackgroundProcessor,接着执行主机下面的子容器,现在执行的就是StandardContext,它会调用WebappLoader#backgroundProcess(),这个后台方面里会检查当前应用的class和jar是否改动过,这个过程是委托给WebappClassLoader#modified(),这个modified()方法会检查class和jar的时间戳,获取时间戳又会委托FileDirContext#getAttributes(),如果发现有class或者jar改动过了,就简单的重启上下文就可以了,因为部署描述符还是一样的,只是类和jar更新了,所以重启上下文就可以了。
  接着上下文会执行ManagerBase#backgroundProcess(),它会调用StandardSession#isValid(),如果session检查自己已经失效就会调用自定义的session属性的Listener,同时将自己从ManagerBase中删除,我们以前所介绍的session管理,其中的后台管理就和这里的步骤完全一样。
  上下文执行完了,回到ContainerBackgroundProcessor,此后会继续执行StandardWrapper#backgroundProcess(),StandardWrapper会先调用父类ContainerBase#backgroundProcess(),由于StandardWrapper没有和其他的组件管理,也没有事件监听器,valve的后台方法都是空的,所以返回,此时StandardWrapper会调用这么一段:
         if (getServlet() !=  null && (getServlet()  instanceof  PeriodicEventListener)) {
            (( PeriodicEventListener) getServlet()).periodicEvent();
        }
谁继承了这个监听器呢?只有JspServlet继承了,这个periodicEvent()会调用checkCompile()方法,默认情况下JSP预编译是没有打开的,如果打开了JSP预编译,在启动后我们又部署了一个新的JSP到应用上,JspServlet就会检查到,然后在后台编译这个JSP。

包装执行完了,后台所有的东西就介绍了,从这里可以看出,Tomcat后台检查实际上比是较繁重的。

猜你喜欢

转载自blog.csdn.net/hixiaoxiaoniao/article/details/80820845