Tomcat启动分析(七) - 容器组件

本文及后续几篇文章开始分析容器组件,它们都与请求处理息息相关,首先介绍容器组件共同的父类Container接口及ContainerBase抽象类。

容器组件

容器组件处理客户端请求并返回响应,Tomcat中的容器组件按从高到低的顺序有以下四个层次:

  • Engine:表示整个Catalina servlet引擎,可以包含一个或多个子容器,如Host或者Context等;
  • Host:表示虚拟主机,包含Context;
  • Context:表示ServletContext,包含一个或多个Wrapper;
  • Wrapper:表示单独的servlet定义。

需要注意的是,Catalina不需要包括所有上述容器组件,所以容器组件的实现必须被设计成在没有父容器的情况下依然可以正确运行。

Container接口

Container接口继承了Lifecycle接口,表示Servlet容器相关组件的抽象,类层次结构如下图所示:

Tomcat启动分析(七) - 容器组件_第1张图片
Container类层次结构.png

public interface Container extends Lifecycle {
    // ----------------------------------------------------- Manifest Constants
    public static final String ADD_CHILD_EVENT = "addChild";
    public static final String ADD_VALVE_EVENT = "addValve";
    public static final String REMOVE_CHILD_EVENT = "removeChild";
    public static final String REMOVE_VALVE_EVENT = "removeValve";
    // ------------------------------------------------------------- Properties
    public Log getLogger();
    public String getLogName();
    public ObjectName getObjectName();
    public String getDomain();
    public String getMBeanKeyProperties();
    public Pipeline getPipeline();
    public Cluster getCluster();
    public void setCluster(Cluster cluster);
    public int getBackgroundProcessorDelay();
    public void setBackgroundProcessorDelay(int delay);
    public String getName();
    public void setName(String name);
    public Container getParent();
    public void setParent(Container container);
    public ClassLoader getParentClassLoader();
    public void setParentClassLoader(ClassLoader parent);
    public Realm getRealm();
    public void setRealm(Realm realm);
    // --------------------------------------------------------- Public Methods
    public void backgroundProcess();
    public void addChild(Container child);
    public void addContainerListener(ContainerListener listener);
    public void addPropertyChangeListener(PropertyChangeListener listener);
    public Container findChild(String name);
    public Container[] findChildren();
    public ContainerListener[] findContainerListeners();
    public void removeChild(Container child);
    public void removeContainerListener(ContainerListener listener);
    public void removePropertyChangeListener(PropertyChangeListener listener);
    public void fireContainerEvent(String type, Object data);
    public void logAccess(Request request, Response response, long time, boolean useDefault);
    public AccessLog getAccessLog();
    public int getStartStopThreads();
    public void setStartStopThreads(int startStopThreads);
    public File getCatalinaBase();
    public File getCatalinaHome();
}
  • 一个容器可以包含其他容器,可以通过addChild、findChild、findChildren、removeChild和setParent等接口方法进行操作;
  • 通过addContainerListener、findContainerListeners和removeContainerListener等接口方法可以向容器组件添加容器事件监听器ContainerListener,还可以通过fireContainerEvent方法发布容器事件;
  • 通过addPropertyChangeListener和removePropertyChangeListener可以向容器组件添加属性值改变监听器PropertyChangeListener;
  • 注意Container接口继承了Lifecycle接口,Lifecycle接口添加的是生命周期事件监听器LifecycleListener,因此容器组件包含三种监听器:生命周期事件监听器LifecycleListener、容器事件监听器ContainerListener和属性值改变监听器PropertyChangeListener。

ContainerBase抽象类

ContainerBase抽象类是Container接口的默认实现,它继承了LifecycleBase类,同时也是其他容器组件如StandardEngine、StandardHost和StandardContext等的父类,其部分成员变量如下:

public abstract class ContainerBase extends LifecycleMBeanBase
        implements Container {
    // 省略一些代码
    /**
     * The child Containers belonging to this Container, keyed by name.
     */
    protected final HashMap children = new HashMap<>();
    protected final List listeners = new CopyOnWriteArrayList<>();
    protected String name = null;
    protected Container parent = null;
    protected ClassLoader parentClassLoader = null;
    protected final Pipeline pipeline = new StandardPipeline(this);
    /**
     * The number of threads available to process start and stop events for any
     * children associated with this container.
     */
    private int startStopThreads = 1;
    protected ThreadPoolExecutor startStopExecutor;

    @Override
    public int getStartStopThreads() {
        return startStopThreads;
    }

    /**
     * Handles the special values.
     */
    private int getStartStopThreadsInternal() {
        int result = getStartStopThreads();
        // Positive values are unchanged
        if (result > 0) {
            return result;
        }
        // Zero == Runtime.getRuntime().availableProcessors()
        // -ve  == Runtime.getRuntime().availableProcessors() + value
        // These two are the same
        result = Runtime.getRuntime().availableProcessors() + result;
        if (result < 1) {
            result = 1;
        }
        return result;
    }

    @Override
    public void setStartStopThreads(int startStopThreads) {
        this.startStopThreads = startStopThreads;
        // Use local copies to ensure thread safety
        ThreadPoolExecutor executor = startStopExecutor;
        if (executor != null) {
            int newThreads = getStartStopThreadsInternal();
            executor.setMaximumPoolSize(newThreads);
            executor.setCorePoolSize(newThreads);
        }
    }
}
  • children变量是一个以子容器组件名为键、子容器组件自身为值的Map;
  • listeners变量保存了添加到该容器组件的容器事件监听器,使用CopyOnWriteArrayList的原因请参见《Effective Java》第三版第79条;
  • name变量是该容器组件的名字;
  • parent变量引用该容器组件的父容器组件;
  • pipeline变量引用一个StandardPipeline,为什么要有一个Pipeline呢?从ContainerBase的类注释可以得到答案:子类需要将自己对请求的处理过程封装成一个Valve并通过Pipeline的setBasic方法安装到Pipeline;
  • startStopExecutor表示启动和停止子容器组件用的线程池,startStopThreads表示这个线程池的线程数。
  • Use local copies to ensure thread safety 这句注释没看明白,即使用了局部变量也是同一个引用。

组件初始化

@Override
protected void initInternal() throws LifecycleException {
    BlockingQueue startStopQueue = new LinkedBlockingQueue<>();
    startStopExecutor = new ThreadPoolExecutor(
            getStartStopThreadsInternal(),
            getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
            startStopQueue,
            new StartStopThreadFactory(getName() + "-startStop-"));
    startStopExecutor.allowCoreThreadTimeOut(true);
    super.initInternal();
}
  • 初始化过程为容器组件自己创建了一个线程池,该线程池用于执行子容器的启动和停止等过程;
  • startStopExecutor里的每个线程的名称是“容器组件名称-startStop-”再加上线程计数,这在Tomcat启动的日志中有所体现,如Host组件启动时日志会有如“localhost-startStop-1”字样的输出。

组件启动

@Override
protected synchronized void startInternal() throws LifecycleException {
    // 省略一些代码
    // Start our child containers, if any
    Container children[] = findChildren();
    List> results = new ArrayList<>();
    for (int i = 0; i < children.length; i++) {
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    }

    boolean fail = false;
    for (Future 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();
}
  • 启动过程利用初始化时创建的线程池启动了各个子容器,如果startStopThreads大于1那么多个子容器可以并发地启动。容器组件启动时会等待全部的子容器组件启动完毕,若有一个子容器启动失败就会抛出异常;
  • 如果有与容器组件关联的Pipeline则启动Pipeline;
  • 调用基类LifecycleBase的setState方法发布LifecycleState.STARTING事件给添加到容器组件自身的生命周期事件监听器。

Pipeline组件

Pipeline组件用于封装容器组件的请求处理过程,一般来说每个容器组件都有与其关联的一个Pipeline。
Pipeline接口的代码如下:

public interface Pipeline {
    public Valve getBasic();
    public void setBasic(Valve valve);
    public void addValve(Valve valve);
    public Valve[] getValves();
    public void removeValve(Valve valve);
    public Valve getFirst();
    public boolean isAsyncSupported();
    public Container getContainer();
    public void setContainer(Container container);
    public void findNonAsyncValves(Set result);
}
  • getBasic、setBasic与基本阀(Basic Valve)的概念有关,Pipeline是由一系列阀(Valve)组成的链表,基本阀始终指向最后一个阀;
  • 基本阀封装了容器组件对请求的处理过程,总是在一个Pipeline的末尾执行;
  • 其余的方法与链表操作有关,如添加、移除阀等方法。

StandardPipeline类继承LifecycleBase类并实现了Pipeline接口,与阀有关的方法实现很简单,在此不再赘述。initInternal方法没有做任何事情,startInternal则依次启动了各个实现了Lifecycle接口的阀。

@Override
protected void initInternal() {
    // NOOP
}

@Override
protected synchronized void startInternal() throws LifecycleException {
    // Start the Valves in our pipeline (including the basic), if any
    Valve current = first;
    if (current == null) {
        current = basic;
    }
    while (current != null) {
        if (current instanceof Lifecycle)
            ((Lifecycle) current).start();
        current = current.getNext();
    }

    setState(LifecycleState.STARTING);
}
  • 在看StandardPipeline类源码的过程中需要注意basic变量永远指向最后一个阀,first指向第一个阀。当只有一个阀时,first为null但basic不会是null,这时getFirst会返回basic而不是null。

Valve组件

Valve组件是容器组件中处理请求的组件,主要用在Pipeline中,是一种责任链设计模式。Tomcat内置了很多阀的实现,如前文提到的StandardEngineValve、StandardHostValve和StandardContextValve,具体可参考阀的Valve配置文档。
Valve接口的码如下:

public interface Valve {
    public Valve getNext();
    public void setNext(Valve valve);
    public void backgroundProcess();
    public void invoke(Request request, Response response)
        throws IOException, ServletException;
    public boolean isAsyncSupported();
}
  • getNext和setNext分别获取和设置后继阀;
  • invoke方法处理请求,处理规约可以参考Valve API文档。

你可能感兴趣的:(Tomcat启动分析(七) - 容器组件)