Tomcat中的CatainerBase类

本文深入剖析了 Tomcat 中 ContainerBase 类的 initInternal 和 startInternal 方法,详细解读了线程池构造、组件启动流程及周期性任务执行机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本篇文章主要讲解了ContainerBase类中的一些重要方法

在之前的文章中我们说过Catalina中包含4种容器,分别是Wrapper,Context,Host,Engine。我们今天来介绍下相关的CatainerBase类,先看类图。

818454-20161101224909158-1530994109.jpg

从图中可以看到Catalina中4中容器都继承了一个共同的接口ContainerContainer的抽象实现类是ContainerBase,而4种容器的标准实现分别是StandardEngine,StandardHost,StandardContext,StandardWrapper。关于4个标准实现类我们会在后面关于Catalina处理请求的时候讲,今天我们主要查看ContainerBase类。ContainerBase类中方法比较多,就不直接贴源码了,我们挑部分来查看。大家都知道所有的容器组件都有自己的生命周期过程,那么在这个过程中都会调用ContainerBase类的init(),start(),stop(),destroy()方法,而实际上ContainerBase也继承了LifecycleBase类,所以是没有init()这种方法,而是只需要实现initInternal()方法。鉴于我们之前在启动过程5
的最后一部分非常粗略的提到过ContainerBase类的startInternal()方法,所以我们今天在这里将详细讲解initInternal()startInternal()这2个方法。

initInternal

protected ThreadPoolExecutor startStopExecutor;

 @Override
protected void initInternal() throws LifecycleException {
    BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<Runnable>();
    startStopExecutor = new ThreadPoolExecutor(
            getStartStopThreadsInternal(),
            getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
            startStopQueue,
            new StartStopThreadFactory(getName() + "-startStop-"));
    startStopExecutor.allowCoreThreadTimeOut(true);
    super.initInternal();
}

线程池构造

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) 

其实initInternal()方法比较简单,就是初始化了startStopExecutor这个成员变量,而这个变量指向的是一个线程池,追踪下getStartStopThreadsInternal()可以看出默认返回1,所以这个线程池corePoolSize,maximumPoolsize都是1,缓存时间是10秒。再看super.initInternal()方法则是在LifecycleMBeanBase类中注册JMX,跳过开始看startInternal()方法。

startInternal

  /**
 * Start this component and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected synchronized void startInternal() throws LifecycleException {

    // Start our subordinate components, if any
    //111
    if ((loader != null) && (loader instanceof Lifecycle))
        ((Lifecycle) loader).start();
    logger = null;
    getLogger();
    if ((manager != null) && (manager instanceof Lifecycle))
        ((Lifecycle) manager).start();
    if ((cluster != null) && (cluster instanceof Lifecycle))
        ((Lifecycle) cluster).start();
    Realm realm = getRealmInternal();
    if ((realm != null) && (realm instanceof Lifecycle))
        ((Lifecycle) realm).start();
    if ((resources != null) && (resources instanceof Lifecycle))
        ((Lifecycle) resources).start();
    
    //2222222222222
    // Start our child containers, if any
    Container children[] = findChildren();
    List<Future<Void>> results = new ArrayList<Future<Void>>();
    for (int i = 0; i < children.length; i++) {
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    }

    boolean fail = false;
    for (Future<Void> result : results) {
        try {
            result.get();
        } catch (Exception e) {
            log.error(sm.getString("containerBase.threadedStartFailed"), e);
            fail = true;
        }

    }
    if (fail) {
        throw new LifecycleException(
                sm.getString("containerBase.threadedStartFailed"));
    }

    //333333333
    // Start the Valves in our pipeline (including the basic), if any
    if (pipeline instanceof Lifecycle)
        ((Lifecycle) pipeline).start();


    setState(LifecycleState.STARTING);

    //444444
    // Start our thread
    threadStart();

}

代码1的地方是启动各个组件,每个容器的组件不需要分别启动,而是在模版方法中统一启动。

代码2,先看findChildren()方法

    /**
 * Return the set of children Containers associated with this Container.
 * If this Container has no children, a zero-length array is returned.
 */
@Override
public Container[] findChildren() {

    synchronized (children) {
        Container results[] = new Container[children.size()];
        return children.values().toArray(results);
    }

}

返回和本容器相关的所有的子容器的集合,比如Engine对应的子容器当然是Host,Host对应的是Context等。这里使用了Future模式,先创建一个任务的集合,然后把所有任务添加到这个集合里(线程池的submit()方法返回任务本身),最后遍历任务集合调用每个task的get()方法来获取任务执行结果,剩下的只要看下StartChild类即可。

 private static class StartChild implements Callable<Void> {

    private Container child;

    public StartChild(Container child) {
        this.child = child;
    }

    @Override
    public Void call() throws LifecycleException {
        child.start();
        return null;
    }
}

静态内部类,实现Callable,在call()方法中调用了子容器的start()方法。看到这里就比较明了了,当一个容器在启动(调用start())方法的时候,他的子容器的启动是采用向线程池提交任务的方式来完成的。

在任务提交完毕后,调用task的get()方法来保证所有子容器start()方法调用完毕线程才继续网下执行(get()方法是阻塞的)。

代码3的地方是调用自身容器的pipeline组件的start()方法。

代码4的地方调用了一个私有方法threadStart(),我们来看下源码。

 /**
 * Start the background thread that will periodically check for
 * session timeouts.
 */
protected void threadStart() {
    //thread 
    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();

}

/**
 * The background thread.
 */
private Thread thread = null;

看起来很简单,其实这个私有方法是start()方法中最重要的方法,也是今天这篇文章讲解的重点。

第一步thread变量的判断,这个没啥,只是非空判断,没啥继续往下看。

如果backgroundProcessorDelay 不大于0,那么方法就停止。那么这个变量的值是多少呢。

  /**
 * The processor delay for this component.
 */
protected int backgroundProcessorDelay = -1;

可以看到默认初始化是-1,但是不同的容器的值可能不一样,我们去标准的4个容器中查看下。

StandardEngine

    /**
 * Create a new StandardEngine component with the default basic Valve.
 */
public StandardEngine() {

    super();
    pipeline.setBasic(new StandardEngineValve());
    /* Set the jmvRoute using the system property jvmRoute */
    try {
        setJvmRoute(System.getProperty("jvmRoute"));
    } catch(Exception ex) {
        log.warn(sm.getString("standardEngine.jvmRouteFail"));
    }
    // By default, the engine will hold the reloading thread
    backgroundProcessorDelay = 10;

}

全查过一遍发现,只有在StandardEngine中能看到这个变量的赋值,也就是说其他三个容器中这个值默认就是-1,同时也说明只有StandardEnginestart()方法调用的时候才会走这个方法,其他容器这个方法是走不到下面代码的。了解了这里我们就来看这个方法到底做了什么吧。

新建了一个线程,传递了一个ContainerBackgroundProcessor的实例,然后设置线程为守护线程,并且启动线程。ContainerBackgroundProcessor是个Runnable接口的实现类,查看其run方法。

/**
 * The background thread completion semaphore.
 */
private volatile boolean threadDone = false;        

 @Override
    public void run() {
        Throwable t = null;
        String unexpectedDeathMessage = sm.getString(
                "containerBase.backgroundProcess.unexpectedThreadDeath",
                Thread.currentThread().getName());
        try {
            while (!threadDone) {
                try {
                    //睡眠一段事件
                    Thread.sleep(backgroundProcessorDelay * 1000L);
                } catch (InterruptedException e) {
                    // Ignore
                }
                if (!threadDone) {
                    //获取容器本身
                    Container parent = (Container) getMappingObject();
                    //获取当前线程的classloader
                    ClassLoader cl = 
                        Thread.currentThread().getContextClassLoader();
                    if (parent.getLoader() != null) {
                        cl = parent.getLoader().getClassLoader();
                    }
                    //11111
                    processChildren(parent, cl);
                }
            }
        } catch (RuntimeException e) {
            t = e;
            throw e;
        } catch (Error e) {
            t = e;
            throw e;
        } finally {
            if (!threadDone) {
                log.error(unexpectedDeathMessage, t);
            }
        }
    }

可以看到threadDone变量初始化是false,也就是while循环进去,先睡眠一段时间,然后继续往下执行,先获取容器本身,再获取当前线程的classloader,传递参数给processChildren这个方法。

 protected void processChildren(Container container, ClassLoader cl) {
        try {
            if (container.getLoader() != null) {
                Thread.currentThread().setContextClassLoader
                    (container.getLoader().getClassLoader());
            }
            container.backgroundProcess();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(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);
            }
        }
    }

这里传递进来的Container的实现类其实是StandardEngine,调用其backgroundProcess方法

 /**
 * Execute a periodic task, such as reloading, etc. This method will be
 * invoked inside the classloading context of this container. Unexpected
 * throwables will be caught and logged.
 */
@Override
public void backgroundProcess() {
    
    if (!getState().isAvailable())
        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);                
        }
    }
    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);
}

乍一看感觉很复杂,看不懂,不妨先看看英文注释,方法上的文档说的很清楚:执行一个周期性的任务,比如reloading等。也许还是有点迷糊,但是主要不懂的地方还是在于不知道这个方法里每个组件(比如cluster,loader,manager等等代表的意义),这里就不详细的介绍了,也许后面有空会仔细介绍。先大致讲解下

cluster

Catalina的集群组件,通过cluster组件可以实现:集群应用部署,即多个Tomcat实例,不需要每个都分别部署应用,只需要在某个实例上部署,整个集群中的各个实例都会自动同步应用进行部署。那么他的backgroundProcess()方法主要的功能是监听指定文件夹下有没有新增的war包或者文件是新增的还是修改的已决定来重新部署和通知其他tomcat集群成员。

loader

Catalina在启动过程中创建的classloader的实例,backgroundProcess()方法主要的功能是查看Context容器是否需要重新加载,热部署就是利用这个机制来完成的。

manager

Catalina中的Session管理器,backgroundProcess()方法主要的功能是将过期会话(session)置为无效

realm

Catalina中的安全组件,backgroundProcess()方法主要的功能是,我也不清楚貌似是跟servlet的安全校验有关。

pipeline

容器的pipeline组件,这里是遍历整个pipeline链表,分别调用backgroundProcess()方法,不太重要,每个具体类功能也不同。

下面解释下为啥方法这样写。因为在早期tomcat中不同的组件都要做一些周期性的检查,他们都分别在自己的类中启动一个线程来做这些检查,后来就统一写到了一起,一个是方便管理,另外个也是设计模式的体现。

我们继续看processChildren()方法的后半部分。

Container[] children = container.findChildren();
        for (int i = 0; i < children.length; i++) {
            if (children[i].getBackgroundProcessorDelay() <= 0) {
                processChildren(children[i], cl);
            }
        }

很简单,获取所有的子容器,然后遍历每个子容器来调用他们的processChildren()方法。

好了,到这里ContainerBase类的startInternal()方法就分析完毕了,可以看到,在角角落落里藏了很多个值得注意的东西,比如说下面就要提到的Session管理器等,至于stop,destroy以及其他方法就留个读者自行探索了。

转载于:https://www.cnblogs.com/coldridgeValley/p/5925016.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值