Tomcat 组件生命周期与初始化过程

2021-02-11 1661点热度 0人点赞 0条评论

LifeCycle 接口

上一篇文章中提到,Tomcat 中主要有这些重要的组件:

  • Server:Server 容器代表了一个 tomcat 实例(Catalina 实例),可以包含一个或多个 Service 容器;
  • Service:Service 是提供具体的对外服务的,一个 Service 容器中可以有多个 Connector 组件(监听不同的端口请求并处理)和一个 Servlet 容器(做具体的业务处理逻辑);
  • Engine 和 Host:Engine 组件是 Servlet 容器的核心,它支持定义多个虚拟主机(Host),虚拟主机允许 Tomcat 引擎在一台机器上配置多个域名;
  • Context:每个虚拟主机下支持部署多个 Web 应用,这就是我们所熟悉的上下文对象。 Context 是使用由 Servlet 规范中制定的 Web 应用程序的格式表示,不论是压缩后的 war 格式还是为压缩的目录结构;
  • Wrapper:在一个 Context 中可以部署多个 Servlet,并且每个 Servlet 都会被一个 Wrapper 所包含。

这些组件是层层包含的。 Tomcat 通过定义 LifeCycle 接口来统一管理各组件的生命周期。我们来看一下 LifeCycle 接口中定义了以下方法:

public interface Lifecycle {
    public void init() throws LifecycleException;
    public void start() throws LifecycleException;
    public void stop() throws LifecycleException;
    public void destroy() throws LifecycleException;
}

LifeCycle 这个类的继承关系中,我们也可以印证,如 Container 以及其包含的 Host, Engine, wrapperContext 都实现了 LifeCycle 接口。

同样的,ServiceServer 这两大组件也实现了 LifeCycle 接口,并分别被 StandardServiceStandardServer 类实现。

tomcat-lifecycle-hierarchy

启动入口分析

之前我们已经找到 Bootstrap 类是 Tomcat 程序的启动入口,那么我们是如何找到这个入口的呢?

首先,对于下载的 Tomcat Server,其启动入口是 bin/startup.sh 文件,他其实是启动了 catalina.sh,并传入了 start 参数:

PRGDIR=`dirname "PRG"`
EXECUTABLE=catalina.sh

exec "PRGDIR"/"EXECUTABLE" start "@"

那么我们再进去看 catalina.sh,搜索解析 start 参数的地方:

elif [ "1" = "start" ] ; then
    # ...
    if [ "1" = "-security" ] ; then
        # eval <some_code>
    else
        eval _NOHUP ""_RUNJAVA"" ""CATALINA_LOGGING_CONFIG""LOGGING_MANAGER "JAVA_OPTS" "CATALINA_OPTS" 
          -DENDORSED_PROP=""JAVA_ENDORSED_DIRS"" 
          -classpath ""CLASSPATH""          -Dcatalina.base=""CATALINA_BASE"" 
          -Dcatalina.home=""CATALINA_HOME""          -Djava.io.tmpdir=""CATALINA_TMPDIR"" 
          org.apache.catalina.startup.Bootstrap "@" start          >> "CATALINA_OUT" 2>&1 "&"
    fi
fi

所以 Tomcat 启动的过程就可以简化理解为:

java xxx.jar $SOME_OPTS org.apache.catalina.startup.Bootstrap

至此确认了 Tomcat 启动的入口就是这个 Bootstrap 类。

继续看 Bootstrap.main 方法:

public final class Bootstrap {

    private Object catalinaDaemon = null;
    private static volatile Bootstrap daemon = null;

    public static void main(String args[]) {
        synchronized (daemonLock) {
            if (daemon == null) {
                // Don't set daemon until init() has completed
                Bootstrap bootstrap = new Bootstrap();
                try {
                    bootstrap.init();       // <------------- 初始化逻辑
                } catch (Throwable t) {
                    handleThrowable(t);
                    t.printStackTrace();
                    return;
                }
                daemon = bootstrap;
            } else {
                // When running as a service the call to stop will be on a new
                // thread so make sure the correct class loader is used to
                // prevent a range of class not found exceptions.
                Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
            }
        }
        // ...
    }

    public void init() throws Exception {

        initClassLoaders();

        // 实例化 Catalina 对象 startupInstance
        Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.getConstructor().newInstance();

        // Set the shared extensions class loader
        String methodName = "setParentClassLoader";
        Class<?> paramTypes[] = new Class[1];
        paramTypes[0] = Class.forName("java.lang.ClassLoader");
        Object paramValues[] = new Object[1];
        paramValues[0] = sharedLoader;

        // 反射调用 startupInstance.setParentClassLoader(paramValues) 方法,设置类加载器
        Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);

        // 赋值给 catalinaDaemon
        catalinaDaemon = startupInstance;
    }
}

这段初始化逻辑,实际上是:

  • 实例化一个 Catalina 对象并赋值给了 Bootstrap 中的成员变量 catalinaDaemon
  • 实例化一个 Bootstrap 对象并赋值给自己的成员变量 daemon

接着往下看 Main 方法,这里指出了 Tomcat 启动的关键两个步骤:加载初始化阶段启动阶段

public final class Bootstrap {
    public static void main(String args[]) {
        if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
        }
    }

    private void load(String[] arguments) throws Exception {
        String methodName = "load";
        Object param[];
        Class<?> paramTypes[];

        // 实际上是调用 Catalina.load()
        Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        method.invoke(catalinaDaemon, param);
    }

    public void start() throws Exception {
        if (catalinaDaemon == null) {
            init();
        }

        // 实际上是调用 Catalina.start()
        Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
        method.invoke(catalinaDaemon, (Object [])null);
    }
}

这里是典型的委派模式的应用。

加载初始化阶段

Server 初始化

先看 Catalina.load()方法。

public class Catalina {

    public void load() {
        initDirs();
        initNaming();

        // Create and execute our Digester
        Digester digester = createStartDigester();

        file = configFile();
        inputStream = new FileInputStream(file);
        inputSource = new InputSource(file.toURI().toURL().toString());

        inputSource.setByteStream(inputStream);
        digester.push(this);

        // 解析 server.xml 配置文件
        digester.parse(inputSource);

        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        initStreams();

        // 继续初始化下一层的 Server 对象
        getServer().init();
    }
}

此时初始化来到了 Server.init()阶段,而这个接口定义在 LifeCycle.init()中,这里 LifeCycleServer 对象的生命周期进行了管理。

public abstract class LifecycleBase implements Lifecycle {
    @Override
    public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }

        // 模板方法设计模式,定义抽象步骤顺序,由子类提供实现
        try {
            // 典型的状态模式的影子
            setStateInternal(LifecycleState.INITIALIZING, null, false);

            // 核心初始化操作,这里是调用了 Server.initInternal()
            initInternal();

            // 典型的状态模式的影子
            setStateInternal(LifecycleState.INITIALIZED, null, false);
        } catch (Throwable t) {
            handleSubClassException(t, "lifecycleBase.initFail", toString());
        }
    }
}

public final class StandardServer extends LifecycleMBeanBase implements Server {
    @Override
    protected void initInternal() throws LifecycleException {
        super.initInternal();
        // ...

        // 继续初始化下一级所有的 Service 容器
        // Service.init() 方法再次被 LifeCycle.init() 接管生命周期
        for (Service service : services) {
            service.init();
        }
    }
}

StandardServer 中,通过一个遍历继续初始化下一级所有的 Service 容器。

Service 初始化

老规矩,Service 容器的初始化也要先去 LifeCycle 报道,然后实现其 initInternal()方法:

public class StandardService extends LifecycleMBeanBase implements Service {
    @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();

        // 1. Servlet 容器引擎初始化
        if (engine != null) {
            engine.init();
        }

        // Initialize any Executors
        for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            executor.init();
        }

        // Initialize mapper listener
        mapperListener.init();

        // Initialize our defined Connectors
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                try {
                    // 2. 遍历初始化所有的 Connector
                    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);
                }
            }
        }
    }
}

Engine 初始化

先看 Engine 对象的初始化逻辑。可以看到 StandardEngine 的初始化过程逻辑其实是在其父类 ContainerBase 中完成的。

public class StandardEngine extends ContainerBase implements Engine {
    @Override
    protected void initInternal() throws LifecycleException {
        getRealm();
        super.initInternal();
    }
}

public abstract class ContainerBase extends LifecycleMBeanBase implements Container {
    @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();
    }
}

可以看到 Engine 组件初始化阶段,其实就是创建了一个名叫 startStopExecutor 的线程池,这个线程池会在 Engine.start()过程中使用,并对应多个 Host 实例。

Connector 初始化

Connector 的初始化,主要是调用了 ProtocolHandler.init()方法。

public class Connector extends LifecycleMBeanBase  {

    protected final ProtocolHandler protocolHandler;

    @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();

        // Initialize adapter
        adapter = new CoyoteAdapter(this);
        protocolHandler.setAdapter(adapter);

        // ...

        // 初始化 protocolHandler
        protocolHandler.init();
    }
}

继续看 AbstractProtocol.init()实现,其中调用了 Endpoint.init()方法来初始化 Endpoint 对象。

public abstract class AbstractProtocol<S> implements ProtocolHandler, MBeanRegistration {

    private final AbstractEndpoint<S> endpoint;

    @Override
    public void init() throws Exception {
        // ...
        String endpointName = getName();
        endpoint.setName(endpointName.substring(1, endpointName.length()-1));
        endpoint.setDomain(domain);

        // 初始化 Endpoint 对象
        endpoint.init();
    }
}

Endpoint 对象在初始化过程中调用了 bind()虚方法,最终由 NioEndpoint 类来实现:

public abstract class AbstractEndpoint<S> {

    public abstract void bind() throws Exception;

    public void init() throws Exception {
        if (bindOnInit) {
            bind();
            bindState = BindState.BOUND_ON_INIT;
        }
        // ...
    }
}

public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {

    @Override
    public void bind() throws Exception {

        if (!getUseInheritedChannel()) {

            // 获取 NIO Channel
            serverSock = ServerSocketChannel.open();
            socketProperties.setProperties(serverSock.socket());
            InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));

            // 绑定端口,但尚未 accept 客户端连接
            serverSock.socket().bind(addr,getAcceptCount());
        }
        // ...
    }
}

这里的 NioEndpoint 对象初始化过程中,获取了 NIO Channel 并完成了 serverSock.socket()的端口绑定。

至此,Tomcat 启动过程中,主要组件的初始化逻辑基本完成了。

tomcat-start-init-flow

SilverLining

也可能是只程序猿

文章评论