zoukankan      html  css  js  c++  java
  • Tomcat中的CatainerBase类

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

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

    从图中可以看到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以及其他方法就留个读者自行探索了。

  • 相关阅读:
    <11>改变图像的尺寸,方便上传服务器
    <10>获取当前时间
    <09>获得字符串的size
    <08>时间戳的转换
    <07>手机号码验证
    <06>邮箱的验证
    <05>判断字符串是否为空
    WKWebView的一些知识
    objc_setAssociatedObject 使用
    linker command failed with exit code 1 (use -v to see invocation) 编译报错原因
  • 原文地址:https://www.cnblogs.com/coldridgeValley/p/5925016.html
Copyright © 2011-2022 走看看