Tomcat

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

2018-09-13  本文已影响1人  buzzerrookie

本文首先分析Container接口及ContainerBase抽象类,然后分析Engine、Host、Context和Pipeline等组件的初始化和启动过程。

Container

Container是一个重要的接口,表示Servlet容器相关组件的抽象,类层次结构如下图所示:


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();
}

ContainerBase抽象类是Container接口的默认实现,也是其他标准组件如StandardEngine、StandardWrapper、StandardHost和StandardContext的父类,其部分代码如下:

public abstract class ContainerBase extends LifecycleMBeanBase
        implements Container {
    // 省略一些代码
    protected final HashMap<String, Container> children = new HashMap<>();
    protected final List<ContainerListener> 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
    protected void initInternal() throws LifecycleException {
        BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
        startStopExecutor = new ThreadPoolExecutor(
                getStartStopThreadsInternal(),
                getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
                startStopQueue,
                new StartStopThreadFactory(getName() + "-startStop-"));
        startStopExecutor.allowCoreThreadTimeOut(true);
        super.initInternal();
    }

    @Override
    protected synchronized void startInternal() throws LifecycleException {

        // Start our subordinate components, if any
        logger = null;
        getLogger();
        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])));
        }

        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();
    }

    // 省略一些代码
}

Engine

Engine元素表示整个请求处理的机构,它从一个或多个Connector中接收并处理请求,并将响应返回给Connector。Service元素内有且仅能有一个Engine元素。
Engine接口继承了Container接口,StandardEngine类继承了ContainerBase基类并实现了Engine接口,其部分代码如下所示:

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;
}

public void setService(Service service) {
    this.service = service;
}

@Override
public void addChild(Container child) {
    if (!(child instanceof Host))
        throw new IllegalArgumentException
            (sm.getString("standardEngine.notHost"));
    super.addChild(child);
}

@Override
public void setParent(Container container) {
    throw new IllegalArgumentException
        (sm.getString("standardEngine.notParent"));
}

@Override
protected void initInternal() throws LifecycleException {
    // Ensure that a Realm is present before any attempt is made to start
    // one. This will create the default NullRealm if necessary.
    getRealm();
    super.initInternal();
}

@Override
protected synchronized void startInternal() throws LifecycleException {
    // Log our server identification information
    if(log.isInfoEnabled())
        log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
    // Standard container startup
    super.startInternal();
}

Host

Host元素表示一个虚拟主机,在Host元素内可以有多个Context元素与之关联以表示不同的Web应用,Engine元素内可以配置多个Host,但其中一个的名称必须与Engine的defaultHost属性值相匹配。
Host接口继承了Container接口,StandardHost类继承了ContainerBase基类并实现了Host接口,其部分代码如下所示。

public class StandardHost extends ContainerBase implements Host {
    // ----------------------------------------------------------- Constructors
    /**
     * Create a new StandardHost component with the default basic Valve.
     */
    public StandardHost() {

        super();
        pipeline.setBasic(new StandardHostValve());

    }
    // ----------------------------------------------------- Instance Variables
    /**
     * The set of aliases for this Host.
     */
    private String[] aliases = new String[0];

    private final Object aliasesLock = new Object();

    /**
     * The application root for this Host.
     */
    private String appBase = "webapps";
    private volatile File appBaseFile = null;

    /**
     * The XML root for this Host.
     */
    private String xmlBase = null;

    /**
     * host's default config path
     */
    private volatile File hostConfigBase = null;

    /**
     * The auto deploy flag for this Host.
     */
    private boolean autoDeploy = true;

    /**
     * The Java class name of the default context configuration class
     * for deployed web applications.
     */
    private String configClass =
        "org.apache.catalina.startup.ContextConfig";

    /**
     * The Java class name of the default Context implementation class for
     * deployed web applications.
     */
    private String contextClass = "org.apache.catalina.core.StandardContext";

    /**
     * The deploy on startup flag for this Host.
     */
    private boolean deployOnStartup = true;

    /**
     * deploy Context XML config files property.
     */
    private boolean deployXML = !Globals.IS_SECURITY_ENABLED;

    /**
     * Should XML files be copied to
     * $CATALINA_BASE/conf/&lt;engine&gt;/&lt;host&gt; by default when
     * a web application is deployed?
     */
    private boolean copyXML = false;

    /**
     * The Java class name of the default error reporter implementation class
     * for deployed web applications.
     */
    private String errorReportValveClass =
        "org.apache.catalina.valves.ErrorReportValve";

    /**
     * Unpack WARs property.
     */
    private boolean unpackWARs = true;

    /**
     * Work Directory base for applications.
     */
    private String workDir = null;

    /**
     * Should we create directories upon startup for appBase and xmlBase
     */
    private boolean createDirs = true;

    /**
     * Track the class loaders for the child web applications so memory leaks
     * can be detected.
     */
    private final Map<ClassLoader, String> childClassLoaders =
            new WeakHashMap<>();

    /**
     * Any file or directory in {@link #appBase} that this pattern matches will
     * be ignored by the automatic deployment process (both
     * {@link #deployOnStartup} and {@link #autoDeploy}).
     */
    private Pattern deployIgnore = null;
    private boolean undeployOldVersions = false;
    private boolean failCtxIfServletStartFails = false;

    @Override
    public void addChild(Container child) {

        child.addLifecycleListener(new MemoryLeakTrackingListener());

        if (!(child instanceof Context))
            throw new IllegalArgumentException
                (sm.getString("standardHost.notContext"));
        super.addChild(child);

    }
    // 省略一些代码
}

startInternal方法如下所示,如果容器关联的pipeline中没有错误报告阀(errorReportValveClass),那么就给pipeline添加一个,否则什么也不做。

@Override
protected synchronized void startInternal() throws LifecycleException {
    // Set error report valve
    String errorValve = getErrorReportValveClass();
    if ((errorValve != null) && (!errorValve.equals(""))) {
        try {
            boolean found = false;
            Valve[] valves = getPipeline().getValves();
            for (Valve valve : valves) {
                if (errorValve.equals(valve.getClass().getName())) {
                    found = true;
                    break;
                }
            }
            if(!found) {
                Valve valve =
                    (Valve) Class.forName(errorValve).getConstructor().newInstance();
                getPipeline().addValve(valve);
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error(sm.getString(
                    "standardHost.invalidErrorReportValveClass",
                    errorValve), t);
        }
    }
    super.startInternal();
}

Context

Context元素表示一个Web应用,运行于一个特定的虚拟主机内。每个Web应用可以基于WAR文件,也可以基于相应的未打包目录。Catalina根据HTTP请求URI基于最长匹配选择处理该请求的Web应用,一旦选择,该Context就会根据定义好的servlet映射选择一个适当的servlet去处理。
Context接口继承了Container接口,StandardContext类继承了ContainerBase基类并实现了Context接口,其部分代码如下所示。

public class StandardContext extends ContainerBase
        implements Context, NotificationEmitter {
    /**
     * The display name of this web application.
     */
    private String displayName = null;

    /**
     * Override the default context xml location.
     */
    private String defaultContextXml;

    /**
     * Override the default web xml location.
     */
    private String defaultWebXml;


    /**
     * The distributable flag for this web application.
     */
    private boolean distributable = false;

    /**
     * The document root for this web application.
     */
    private String docBase = null;

    /**
     * The reloadable flag for this web application.
     */
    private boolean reloadable = false;

    /**
     * Encoded path.
     */
    private String encodedPath = null;

    /**
     * Unencoded path for this web application.
     */
    private String path = null;

    // 省略一些代码
    public StandardContext() {
        super();
        pipeline.setBasic(new StandardContextValve());
        broadcaster = new NotificationBroadcasterSupport();
        // Set defaults
        if (!Globals.STRICT_SERVLET_COMPLIANCE) {
            // Strict servlet compliance requires all extension mapped servlets
            // to be checked against welcome files
            resourceOnlyServlets.add("jsp");
        }
    }
    // 省略一些代码
}

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<String> result);
}

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);
}

Valve

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

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();
}
上一篇下一篇

猜你喜欢

热点阅读