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。


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();
        }
    }
}
目录
相关文章
|
安全 应用服务中间件 数据安全/隐私保护
|
编解码 应用服务中间件 Android开发
后台(08)——Tomcat
探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分...
1270 0
|
Web App开发 Java 应用服务中间件
Java Web之tomcat开机后台启动
Windows下安装好了tomcat了以后,可以直接进如 bin 目录双击 startup.bat 来启动,但是这样启动有一个文集就是任务栏会一直存在一个小窗口,这个窗口是tomcat的控制台,会随着后台程序的运行输出tomcat的运行情况,便于查找一些错误发生的原因。
1182 0
|
编解码 Java 应用服务中间件
【技术贴】servlet传参|前台传参含中文符号等 tomcat乱码 java后台接收乱码终极解决方
1.前台传参,一定要编码,否则中文传不出来~~tomcat乱码此篇只适合于tomcat中文传参乱码,websphere6.1中文传参乱码请移步http://hi.baidu.com/ae6623/item/27c43f57e913a0cad2e10c46 前台如果用js进行了编码,后台用jsp或者servlet进行解码的时候就有可能乱码,如下,是我遇到的一个问题。
1423 0
|
Java 应用服务中间件
Tomcat的Session管理(二) - Session后台处理
Tomcat的Session管理(二) - Session后台处理 Tomcat会开启一个后台线程每隔一段时间检查Session的有效性,这个线程是在Tomcat启动的时候当StardardEngine启动时随之启动的。
634 0
|
4月前
|
安全 应用服务中间件 网络安全
Tomcat如何配置PFX证书?
【10月更文挑战第2天】Tomcat如何配置PFX证书?
348 7
|
4月前
|
存储 算法 应用服务中间件
Tomcat如何配置JKS证书?
【10月更文挑战第2天】Tomcat如何配置JKS证书?
567 4
|
1月前
|
网络协议 Java 应用服务中间件
centos7环境下tomcat8的安装与配置
本文介绍了在Linux环境下安装和配置Tomcat 8的详细步骤。首先,通过无网络条件下的文件交互软件(如Xftp 6或MobaXterm)下载并解压Tomcat安装包至指定路径,启动Tomcat服务并测试访问。接着,修改Tomcat端口号以避免冲突,并部署Java Web应用项目至Tomcat服务器。最后,调整Linux防火墙规则,确保外部可以正常访问部署的应用。关键步骤包括关闭或配置防火墙、添加必要的端口规则,确保Tomcat服务稳定运行。
|
6月前
|
网络协议 Java 应用服务中间件
tomcat配置域名及HTTPS
tomcat配置域名及HTTPS
|
6月前
|
Java 应用服务中间件 Windows
【应用服务 App Service】App Service 中部署Java项目,查看Tomcat配置及上传自定义版本
【应用服务 App Service】App Service 中部署Java项目,查看Tomcat配置及上传自定义版本