• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

Tomcat 7 启动分析(4):各组件 init、start 方法调用

Tomcat winrains 来源:预流 1年前 (2019-10-29) 43次浏览
在正常启动 Tomcat 7 的情况下,上篇文章分析到了执行 org.apache.catalina.core.StandardServer 的 init 和 start 方法这儿,那么就来看看这两个方法里面到底干了些什么。
但是在 StandardServer 类里面并没有发现这两个方法:

由此推知这两方法必定是在该类的父类中已实现了,在 StandardServer 类的父类 LifecycleMBeanBase 类的父类 LifecycleBase 类里面终于找到了这两个方法的实现,下面先来看下 init 方法:

@Override
public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
    setStateInternal(LifecycleState.INITIALIZING, null, false);
    try {
        initInternal();
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
                sm.getString("lifecycleBase.initFail",toString()), t);
    }
    setStateInternal(LifecycleState.INITIALIZED, null, false);
}
protected abstract void initInternal() throws LifecycleException;

先将干扰程序阅读视线的 setStateInternal 方法调用忽略掉(下一篇文章会详细讲解该方法),发现这里面就做了一件事情,调用了一下接下来定义的抽象方法 initInternal()(第 21 行)。实际上看下 LifecycleBase 的实现类就会发现,所有的组件类几乎都继承了 LifecycleBase 类,所以这些组件类一般只会有 initInternal 方法的定义。(这里所说的组件类就是前面我们分析 Digester 解析时发现的 StandardServer、StandardService、StandardEngine、StandardHost、StandardContext 等类)
这里所说的组件可以将其理解为我们最开始分析 server.xml 时 xml 文件里的各个节点,父子关系也即 xml 文件里的父子节点。浏览下 LifecycleBase 的子类就会发现节点的实现类都是这个类的子类(记住这点,后面会提到)。

同样分析 start 方法:

/**
 * {@inheritDoc}
 */
@Override
public final synchronized void start() throws LifecycleException {
    if (LifecycleState.STARTING_PREP.equals(state) ||
            LifecycleState.STARTING.equals(state) ||
            LifecycleState.STARTED.equals(state)) {
        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted",
                    toString()), e);
        } else if (log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted",
                    toString()));
        }
        return;
    }
    if (state.equals(LifecycleState.NEW)) {
        init();
    } else if (state.equals(LifecycleState.FAILED)){
        stop();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
            !state.equals(LifecycleState.STOPPED)) {
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }
    setStateInternal(LifecycleState.STARTING_PREP, null, false);
    try {
        startInternal();
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
                sm.getString("lifecycleBase.startFail",toString()), t);
    }
    if (state.equals(LifecycleState.FAILED) ||
            state.equals(LifecycleState.MUST_STOP)) {
        stop();
    } else {
        // Shouldn't be necessary but acts as a check that sub-classes are
        // doing what they are supposed to.
        if (!state.equals(LifecycleState.STARTING)) {
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        }
        setStateInternal(LifecycleState.STARTED, null, false);
    }
}
/**
 * Sub-classes must ensure that the state is changed to
 * {@link LifecycleState#STARTING} during the execution of this method.
 * Changing state will trigger the {@link Lifecycle#START_EVENT} event.
 *
 * If a component fails to start it may either throw a
 * {@link LifecycleException} which will cause it's parent to fail to start
 * or it can place itself in the error state in which case {@link #stop()}
 * will be called on the failed component but the parent component will
 * continue to start normally.
 *
 * @throws LifecycleException
 */
protected abstract void startInternal() throws LifecycleException;

第 7 到 21 行是 start 功能的前置校验,这里如果发现 start 方法已经调用过了,将会记录日志并直接返回。第 23 到 30 行如果发现 start 放的需要做的前置方法没有调用完,或者调用出错,将会先调用这些前置方法。第 32 行暂时先不管,不影响程序阅读,第 35 行是该方法的实质,将会调用本类中定义的抽象方法 startInternal()(第 71 行)。下面的代码同上述一样,都是一些 start 方法调用过程中可能出现的错误的错误处理。
从以上 init 和 start 方法的定义可以看到这两个方法最终将会调用子类中定义的 initInternal 和 startInternal 。
接回本文开头,一开始在找 StandardServer 类中 init 和 start 方法的定义,看完了上面的分析发现最终会调用 StandardServer 类的 initInternal 和 startInternal 两个方法。那这两个方法里面干了些什么?
initInternal 方法:

/**
 * Invoke a pre-startup initialization. This is used to allow connectors
 * to bind to restricted ports under Unix operating environments.
 */
@Override
protected void initInternal() throws LifecycleException {
    super.initInternal();
    // Register global String cache
    // Note although the cache is global, if there are multiple Servers
    // present in the JVM (may happen when embedding) then the same cache
    // will be registered under multiple names
    onameStringCache = register(new StringCache(), "type=StringCache");
    // Register the MBeanFactory
    MBeanFactory factory = new MBeanFactory();
    factory.setContainer(this);
    onameMBeanFactory = register(factory, "type=MBeanFactory");
    // Register the naming resources
    globalNamingResources.init();
    // Populate the extension validator with JARs from common and shared
    // class loaders
    if (getCatalina() != null) {
        ClassLoader cl = getCatalina().getParentClassLoader();
        // Walk the class loader hierarchy. Stop at the system class loader.
        // This will add the shared (if present) and common class loaders
        while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
            if (cl instanceof URLClassLoader) {
                URL[] urls = ((URLClassLoader) cl).getURLs();
                for (URL url : urls) {
                    if (url.getProtocol().equals("file")) {
                        try {
                            File f = new File (url.toURI());
                            if (f.isFile() &&
                                    f.getName().endsWith(".jar")) {
                                ExtensionValidator.addSystemResource(f);
                            }
                        } catch (URISyntaxException e) {
                            // Ignore
                        } catch (IOException e) {
                            // Ignore
                        }
                    }
                }
            }
            cl = cl.getParent();
        }
    }
    // Initialize our defined Services
    for (int i = 0; i < services.length; i++) {
        services[i].init();
    }
}

init 方法里面做了好几件事情,牵涉的话题比较多,这里重点关注最后第 53 到 55 行的代码,这里将循环调用 Server 类里内置的 Service 数组的 init 方法。
startInternal 方法:

/**
 * Start nested components ({@link Service}s) 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 void startInternal() throws LifecycleException {
    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);
    globalNamingResources.start();
    // Start our defined Services
    synchronized (services) {
        for (int i = 0; i < services.length; i++) {
            services[i].start();
        }
    }
}

重点关注第 17 到 21 行,同上一段所分析的代码类似,将循环调用 Sever 类里内置的 Service 数组的 start 方法。
那么这里提到的 Service 的对象到底是什么?
上篇文章分析 Digester 时提到“经过对 xml 文件的解析将会产生 org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext 等等一系列对象,这些对象从前到后前一个包含后一个对象的引用(一对一或一对多的关系)。
所以正常情况下这里的 Service 将会是 org.apache.catalina.core.StandardService 的对象(该代码见 org.apache.catalina.startup.Catalina 类的 339 到 341 行)。
所以按上面的分析,接下来将会调用 StandardService 类的 init 和 start 方法,实际上这个类也是 LifecycleBase 类的子类,所以最终的也会调用本类中的 initInternal 和 startInternal 方法。
initInternal 方法:

/**
 * Invoke a pre-startup initialization. This is used to allow connectors
 * to bind to restricted ports under Unix operating environments.
 */
@Override
protected void initInternal() throws LifecycleException {
    super.initInternal();
    if (container != null) {
        container.init();
    }
    // Initialize any Executors
    for (Executor executor : findExecutors()) {
        if (executor instanceof LifecycleMBeanBase) {
            ((LifecycleMBeanBase) executor).setDomain(getDomain());
        }
        executor.init();
    }
    // Initialize our defined Connectors
    synchronized (connectors) {
        for (Connector connector : connectors) {
            try {
                connector.init();
            } catch (Exception e) {
                String message = sm.getString(
                        "standardService.connector.initFailed", connector);
                log.error(message, e);
                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                    throw new LifecycleException(message);
            }
        }
    }
}

这里将会调用 Service 下的各类子组件中的 init 方法。
startInternal 方法:

/**
 * Start nested components ({@link Executor}s, {@link Connector}s and
 * {@link Container}s) 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 void startInternal() throws LifecycleException {
    if(log.isInfoEnabled())
        log.info(sm.getString("standardService.start.name", this.name));
    setState(LifecycleState.STARTING);
    // Start our defined Container first
    if (container != null) {
        synchronized (container) {
            container.start();
        }
    }
    synchronized (executors) {
        for (Executor executor: executors) {
            executor.start();
        }
    }
    // Start our defined Connectors second
    synchronized (connectors) {
        for (Connector connector: connectors) {
            try {
                // If it has already failed, don't try and start it
                if (connector.getState() != LifecycleState.FAILED) {
                    connector.start();
                }
            } catch (Exception e) {
                log.error(sm.getString(
                        "standardService.connector.startFailed",
                        connector), e);
            }
        }
    }
}

同理,将会调用 service 下各类子组件中的 start 方法。
StandardService 的子容器是 StandardEngine ,看下 StandardEngine 的 startInternal 方法:

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

这里不同于上面两级容器的实现,而是直接调用了父类的 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> 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();
}

第 19 到 34 行即启动当前容器下的子容器的代码,这里采用了分线程分别启动的方式。核心的调用子容器的 start 方法的代码在 StartChild 类的 call 方法中:

private static class StartChild implements Callable {
    private Container child;
    public StartChild(Container child) {
        this.child = child;
    }
    @Override
    public Void call() throws LifecycleException {
        child.start();
        return null;
    }
}

这里使用了JDK 5 的执行线程的方式,不理解的话请参考相关文档说明。
StandardHost 中的 startInternal 与 StandardEngine 类似,这里不再赘述,建议有兴趣的朋友逐一分析 Review 一遍,碰到组件里面嵌套的变量不知道具体实现类的就从上篇文章里面提到的 createStartDigester 那边开始找起,这里不能直接找到的就在里面提到的 new *RuleSet 的 addRuleInstances 方法里面找。通过这种调用将会最终执行完所有在 server.xml 里配置的节点的实现类中 initInternal 和 startInternal 方法。

最后上面提到的 org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext 等等组件的这两个方法都会调用到。就这样,Tomcat 7 在内存中为这一连串组件产生对象,建立对象调用关系,调用它们各自的初始化和启动方法,启动的总体过程就介绍完了,这些对象 start 之后将会响应客户端的请求,为用户服务了。当然,这里还没有涉及到对于具体的发布到 tomcat 里面的没有应用的载入过程,web 应用中配置的 servlet、filter、listener 等的载入、启动服务过程,浏览器发起的一个请求如何经过 Tomcat 内各组件的流转调用到具体应用里去的,这一系列问题都还没谈到。因为 Tomcat 本身庞大繁杂,需要找一个视角切入进去,为了叙述的简单,先从整体上对 Tomcat 内包含的各组件产生机制有一个大体轮廓的了解,这样为后面的介绍提供一个统一的背景。
下一篇文章将分析本文开头遗留的一个问题 —— setStateInternal 方法的作用,以及 Tomcat 中的 Lifecycle 实现原理。

作者:预流

来源:https://juejin.im/post/5a6d6f6751882573520da54d


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)