• How Tomcat works — 四、tomcat启动(3)


    上一节说到StandardService负责启动其子组件:container和connector,不过注意,是有先后顺序的,先启动container,再启动connector,这一节先来看看container。

    目录

    • Pipeline和Vavle
    • StandardEngine类和StandardHost类
    • StandardContext类
    • 总结

    Pipeline和Vavle

    在第二节(How Tomcat works — 二、tomcat启动(1))中没有介绍关于Pipeline和Vavle,因为前面侧重的是整个架构,但是在初始化的时候就不得不说了。

    Pipeline,就是一根管道,用来连接两个容器,在一个容器流向下一个容器的时候使用。在tomcat中也是这个意思,很形象,Engine、Host等都是容器,在执行完上一个容器(比如Engine)的相关操作的时候要开始执行下一个容器(比如Host)的操作了,这个时候需要经过一根管道pipeline,那么我们可以在管道中执行一个其他必要的操作,这个时候可以在管道上面添加Vavle(阀),一根管道pipeline上可以有多个阀门(也很形象)。每根管道都有一个默认的阀门。

    在tomcat实现中有一个实现了Pipeline接口的类StandardPipeline——是每两个容器之间的管道,每个容器都有一个默认的Valve实现StandardEnginevavle、StandardHostValve、StandardContextVavle、StandardWrapperVavle。

    其实valve的作用和filter的作用类似。

    StandardEngine类

    StandardEngine作为整个容器的最顶层负责启动其子组件——StandardHost,对,他就这一个作用。

    StandardEngine.initInternal

    这个方法被超类LifecycleBase.init方法调用,主要作用就是调用超类LifecycleMBeanBase.initInternal方法注册MBean,并初始化一个startStopExecutor(ThreadPoolExecutor),后面用来使用线程启动子容器。

    StandardEngine.startInternal

    这个方法的主要作用就是调用父类的方法Container.startInternal——主要的操作就在这个方法里面:

    protected synchronized void startInternal() throws LifecycleException {
    
        // Start our subordinate components, if any
        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();
    
        // 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"));
        }
    
        // 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();
    
    }
    View Code
    • manager、cluster、realm、resource如果有的话全部启动(所以其实tomcat本身就是支持集群的)
    • 调用findChildren方法找到左右子容器——也就是所有的StandardHost(默认只有一个)
    • 对于每个子容器(StandardHost)使用线程启动,StartChild类实现了Callable接口,在call方法里面执行传入容器的start方法,将子容器启动作为一个task,然后使用ThreadPoolExecutor.submit提交task
    • 然后等待这些Future执行完(因为里面是各个子容器的初始化和启动工作,后面connector必须在容器准备好之后才能启动)
    • 启动pipeline(这里就是StandardPipeline),pipeline会启动所包含的所有vavle(这里就是只有StandardEngineValve)
    • setState会通知所有监听了StandardEngine事件的listener
    • threadStart,启动一个后台线程执行包含的所有阀门的backgroundProcess方法,并触发Lifecycle.PERIODIC_EVENT事件

    StandardHost类

    StandardHost的作用和StandardEngine类似,都是初始化启动子容器,不过这里是StandardContext。启动的方式也一样,所以就不再详述。

    StandardContext类

    这个类才是重中之重,解析web.xml、部署webapp都在这儿实现。在StandardHost中调用StandardContext.start方法,执行的是超类LifecycleBase.start方法,主要进行了如下操作

    • 调用init方法初始化StandardContext
    • 调用startInternal方法启动StandardContext

    init

    在该方法中主要进行了MBean的注册,再就是触发了Lifecycle.AFTER_INITEVENT事件,ContextConfig是StandardContext的listener,在发生该事件的时候执行ContextConfig.init方法,在该方法中主要是构造一个能解析web.xml的digester。

    startInternal

    由于LifecycleBase没有实现该方法,所以就是直接调用StandardContext.startInternal,在StandardContext.startInternal方法中主要进行了如下操作:

    • setResource:添加新的resource
    • 如果webappLoader为null则初始化
    • 初始化charset mapper
    • 设置webapp工作目录,比如%TOMCAT_HOME%/work//Catalina/localhost/_等
    • 触发Lifecycle.CONFIGURE_START_EVENT事件,ContextConfig监听了该事件,会进行解析web.xml
    • 启动所有的children(StandardWrapper,代表所有配置的servlet)
    • 初始化Standardmanager
    • 配置initParam
    • 配置启动filter、listener

    总结

    container部分终于启动完成了,不过还是有些部分略过了,比如StandardWrapper(这个还可以往深挖,不过也包含在StandardContext的启动过程中)初始化和webapp的发布。到现在发现,这些启动过程就是围绕生命周期的这几个方法展开:

    • start和init:LifecycleBase提供了默认实现
    • startInternal和initInternal:这两个方法每个类都是重载实现自己需要启动的东西
  • 相关阅读:
    WEP编码格式
    OSK VFS read数据流分析
    科学剖析濒死体验 "复生"者讲述"死"前1秒钟
    Android的开发相对于tizen的开发难度
    minix文件系统分析
    贴给小程序(1) 查找第一个0值
    Linux下的QQ
    OSK USB 驱动
    LRU算法之hash+list实现(转)
    插入排序
  • 原文地址:https://www.cnblogs.com/sunshine-2015/p/5746920.html
Copyright © 2020-2023  润新知