天天看點

Tomcat 背景周期任務

開篇

 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。

  • 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();
        }
    }
}           

繼續閱讀