美文网首页Tomcat
Tomcat 后台周期任务

Tomcat 后台周期任务

作者: 晴天哥_王志 | 来源:发表于2018-11-11 11:21 被阅读65次

    开篇

     Tomcat后台有周期性任务,包括一些过期任务等,这篇文章主要是想讲清楚后台周期性任务的启动和执行过程。

    Tomcat容器关系图

    • 1、要理解Tomcat的后台周期任务需要理解Tomcat的组件关系图以及容器的继承关系。

    • 2、Tomcat的组件关系图说明了容器的启动过程,按照(Engine -> Host -> Context -> Wrapper)的顺序启动容器。

    • 3、Tomcat的容器关系图说明ContainerBase的实现类,后台周期任务按照(Wrapper -> Context -> Host ->Engine)的顺序执行,但是除了Engine容器之外其他容器因为backgroundProcessorDelay的变量值为-1所以不会执行。

    • 4、Tomcat的容器Engine在执行后台周期任务的过程中会递归链式调用(Host -> Context -> Wrapper)容器。

    Tomcat 组件关系图

    说明:

    • Tomcat各个容器的包含关系如上图。

    • 各容器组件之间的包含关系如:StandardEngine -> StandardHost ->StandardContext -> StandardWrapper。

    Tomcat容器关系图

    image.png

    说明:

    • ContainerBase作为容器的基类,实现类StandardEngine、StandardHost、StandardContext、StandardWrapper。

    • ContainerBase基类的backgroundProcessorDelay 初始化为 -1;

    • StandardHost、StandardContext、StandardWrapper的backgroundProcessorDelay初始值为-1.

    • StandardEngine的重写backgroundProcessorDelay的值设为10。

    
    public abstract class ContainerBase extends LifecycleMBeanBase implements Container {
    
        protected int backgroundProcessorDelay = -1;
    }
    
    
    
    public class StandardEngine extends ContainerBase implements Engine {
    
        public StandardEngine() {
            super();
    
            pipeline.setBasic(new StandardEngineValve());
            try {
                setJvmRoute(System.getProperty("jvmRoute"));
            } catch(Exception ex) {
            }
            backgroundProcessorDelay = 10;
        }
    }
    

    后台周期任务源码分析

    容器和周期任务启动过程

    • 1、ContainerBase的继承类StandardEngine、StandardHost、StandardContext、StandardWrapper。

    • 2、上述的Engine、Host、Context、Wrapper类在启动过程中都会调用ContainerBase的startInternal()方法。

    • 3、ContainerBase的startInternal()方法中,会启动本容器的Cluster、Realm等对象,然后递归执行Container children[] = findChildren()子容器,Engine调用Host、Host调用Context、Context调用Wrapper容器完成容器链路的启动。

    • 4、按照递归调用的反向顺序,依次执行Wrapper、Context、Host、Engine容器的threadStart()方法,由于Wrapper、Context、Host的backgroundProcessorDelay值为-1所以不会执行直至Engine启动。

    • 5、Engine的backgroundProcessorDelay值为10,执行Engine的threadStart()方法,通过执行ContainerBackgroundProcessor的方法执行Engine容器本身的线程启动,并且通过container.findChildren()递归调用Host、Context、Wrapper等容器的任务。

    public abstract class ContainerBase extends LifecycleMBeanBase implements Container {
    
        protected int backgroundProcessorDelay = -1;
    
        protected synchronized void startInternal() throws LifecycleException {
    
            // Start our subordinate components, if any
            Cluster cluster = getClusterInternal();
            if (cluster instanceof Lifecycle) {
                ((Lifecycle) cluster).start();
            }
    
            Realm realm = getRealmInternal();
            if (realm instanceof Lifecycle) {
                ((Lifecycle) realm).start();
            }
    
            // Start our child containers, if any
            Container children[] = findChildren();
            List<Future<Void>> results = new ArrayList<>();
            for (int i = 0; i < children.length; i++) {
                results.add(startStopExecutor.submit(new StartChild(children[i])));
            }
    
            MultiThrowable multiThrowable = new MultiThrowable();
    
            for (Future<Void> result : results) {
                try {
                    result.get();
                } catch (Throwable e) {
                    log.error(sm.getString("containerBase.threadedStartFailed"), e);
                    multiThrowable.add(e);
                }
    
            }
    
            // Start the Valves in our pipeline (including the basic), if any
            if (pipeline instanceof Lifecycle) {
                ((Lifecycle) pipeline).start();
            }
    
    
            setState(LifecycleState.STARTING);
    
            // Start our thread
            threadStart();
        }
    

    周期任务线程启动过程分析

    • 1、只有Engine通过new Thread(new ContainerBackgroundProcessor(), threadName)启动后台周期性任务,按照backgroundProcessorDelay的周期性执行。

    • 2、ContainerBackgroundProcessor的processChildren方法内部先执行本容器的backgroundProcess()方法,然后在递归调用子容器的processChildren()方法。

    • 3、针对容器的container.backgroundProcess()方法继续看细节分析。

        protected void threadStart() {
    
            if (thread != null)
                return;
            if (backgroundProcessorDelay <= 0)
                return;
    
            threadDone = false;
            String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
            thread = new Thread(new ContainerBackgroundProcessor(), threadName);
            thread.setDaemon(true);
            thread.start();
        }
    
    
    
        protected class ContainerBackgroundProcessor implements Runnable {
    
            @Override
            public void run() {
                Throwable t = null;
    
                try {
                    while (!threadDone) {
                        try {
                            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);
                    }
                }
            }
    
            protected void processChildren(Container container) {
                ClassLoader originalClassLoader = null;
    
                try {
                    if (container instanceof Context) {
                        Loader loader = ((Context) container).getLoader();
                        // Loader will be null for FailedContext instances
                        if (loader == null) {
                            return;
                        }
    
                        // Ensure background processing for Contexts and Wrappers
                        // is performed under the web app's class loader
                        originalClassLoader = ((Context) container).bind(false, null);
                    }
                    container.backgroundProcess();
                    Container[] children = container.findChildren();
                    for (int i = 0; i < children.length; i++) {
                        if (children[i].getBackgroundProcessorDelay() <= 0) {
                            processChildren(children[i]);
                        }
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error("Exception invoking periodic operation: ", t);
                } finally {
                    if (container instanceof Context) {
                        ((Context) container).unbind(false, originalClassLoader);
                   }
                }
            }
        }
    }
    

    容器后台线程处理过程

    • 1、cluster.backgroundProcess()执行Cluster的后台处理。

    • 2、realm.backgroundProcess()执行Realm的后台处理。

    • 3、pipeline的backgroundProcess()执行valve链的后台处理。

    • 4、fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null)的执行容器的周期性任务。

    public abstract class ContainerBase extends LifecycleMBeanBase
            implements Container {
    
        public void backgroundProcess() {
    
            if (!getState().isAvailable())
                return;
    
            Cluster cluster = getClusterInternal();
            if (cluster != null) {
                try {
                    cluster.backgroundProcess();
                } catch (Exception e) {
                    log.warn(sm.getString("containerBase.backgroundProcess.cluster",
                            cluster), e);
                }
            }
            Realm realm = getRealmInternal();
            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();
            }
    
            // 启动周期性任务
            fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
        }
    }
    



    StandardContext容器重写backgroundProcess方法

    • StandardContext继承ContainerBase并覆写backgroundProcess()方法。

    • StandardContext执行loader.backgroundProcess()方法。

    • StandardContext执行manager.backgroundProcess()方法。

    • StandardContext执行resources.backgroundProcess()方法。

    • StandardContext执行((DefaultInstanceManager)).backgroundProcess()方法。

    • StandardContext执行super.backgroundProcess()父类的backgroundProcess()方法。

    public class StandardContext extends ContainerBase
            implements Context, NotificationEmitter {
    
        public void backgroundProcess() {
    
            if (!getState().isAvailable())
                return;
    
            Loader loader = getLoader();
            if (loader != null) {
                try {
                    loader.backgroundProcess();
                } catch (Exception e) {
                    log.warn(sm.getString(
                            "standardContext.backgroundProcess.loader", loader), e);
                }
            }
            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();
        }
    }
    



    StandardWrapper容器重写backgroundProcess方法

    • StandardWrapper继承ContainerBase并覆写backgroundProcess()方法。

    • StandardWrapper执行super.backgroundProcess()父类的backgroundProcess()方法。

    • StandardWrapper执行((PeriodicEventListener) getServlet()).periodicEvent()的方法。

    public class StandardWrapper extends ContainerBase
        implements ServletConfig, Wrapper, NotificationEmitter {
    
        public void backgroundProcess() {
            super.backgroundProcess();
    
            if (!getState().isAvailable())
                return;
    
            if (getServlet() instanceof PeriodicEventListener) {
                ((PeriodicEventListener) getServlet()).periodicEvent();
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:Tomcat 后台周期任务

        本文链接:https://www.haomeiwen.com/subject/llxtfqtx.html