(四) Tomcat 源码系列之 Tomcat 启动流程

一切的 Java 程序入口都是 main 方法, Tomcat 的启动类是哪个呢?

直接启动 Tomcat, 使用 jps -l 命令 (Linux 也可以使用 ps -ef | grep java), 查看本机上所有正在运行的 Java 程序

在这里插入图片描述
可以看到, Tomcat 的主启动类是 org.apache.catalina.startup.Bootstrap

初始化各个组件

先来到 Bootstrap 类, 查看 Bootstrap 的 main 方法

synchronized (daemonLock) {
    if (daemon == null) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 初始化类加载器, 并创建 Catalina 对象
            bootstrap.init();
        }
        ....
        daemon = bootstrap;
    }
    else {
        Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
    }
}

try {
    String command = "start";
    ....
    // 默认进入此行代码
    else if (command.equals("start")) {
        daemon.setAwait(true);
        // 利用反射调用 Catalina 的 load 方法, 加载各个组件
        daemon.load(args);
        // 启动各个组件
        daemon.start();
        if (null == daemon.getServer()) {
            System.exit(1);
        }
    }......
}

Catalina

查看 load 方法

// 创建一个解析 xml 文件的对象
Digester digester = createStartDigester();
try {
    try {
        // 拿到 server.xml 配置文件
        file = configFile();
        .....
    }
    try {
        ...
        // 完成对 server.xml 配置文件的解析
        // 并创建了默认的组件对象 : StandardServer, StandardService
        // StandardEngine, StandardHost, Connector, Executor, Mapper
        // 并为 Server 添加了六个监听器, 为 Engine 添加监听器 : EngineConfig
        // 为 Host 添加监听器 : HostConfig
        digester.parse(inputSource);
    }
    try {
        // 初始化 Server 组件
        getServer().init();
    }
}

接下来初始化各个组件, 其中 StandardServer, StandardService, StandardEngine, Connector, 都继承自 LifecycleBase 抽象类, 这里使用了 模板设计模式, 将公共代码抽取出来放到他们的公共父类中

public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
    try {
        // 标记组件生命周期的状态
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        // 初始化各个组件 Server -> Service -> Engine -> MapperListener -> Connector 
        // 在启动 Container 组件的时候又会初始化 Host, Context, Wrapper, Pipeline, Valve 组件
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    }
    ....
}

监听器

Server 默认存在六个生命周期监听器 (这些监听器对应 server.xml 配置文件中配置的监听器), 监听 Server 在生命周期中发生的事件, 并进行相应处理

  • NameContextListener

    用于初始化和填充与每个 Context 和 Server 关联的的 JNDI 服务

  • VersionLoggerListener

    用于以日志形式输出服务器 、操作系统、JVM的版本信息

  • AprLifecycleListener

    用于加载 (服务器启动) 和 销毁 (服务器停止) APR。 如果找不到 APR 库, 则会输出日志, 并不影响Tomcat启动

  • JreMemoryLeakPreventionListener

    用于避免 JRE 内存泄漏问题

  • GlobalResourcesLifecycleListener

    用户加载 (服务器启动) 和 销毁 (服务器停止) 全局命名服务

  • ThreadLocalLeakPreventionListener

    用于在 Context 停止时重建 Executor 池中的线程, 以避免 ThreadLocal 相关的内存泄漏

Lifecycle

由于所有的组件均存在初始化、启动、停止等生命周期方法,拥有生命周期管理的特性, 所以 Tomcat 在设计的时候, 基于生命周期管理抽象成了一个接口 Lifecycle ,而组件 Server、Service、Container、Executor、Connector 组件 , 都实现了 Lifecycle 接口,从而具有了以下生命周期中的核心方法:

  • init() :初始化组件
  • start() :启动组件
  • stop() :停止组件
  • destroy() :销毁组件

在 Lifecycle 的 Javadoc 文档中, 有这样的一段注释

在这里插入图片描述

描述了各个组件执行完对应的方法后, 应处于哪种生命周期状态

各组件的默认实现

上面提到的 Server、Service、Engine、Host、Context 都是接口, 的默认实现类都是加上 Standard 前缀的, 比如 StandardServer, StandardService …

在这里插入图片描述

ProtocolHandler : Coyote 协议接口,通过封装 Endpoint 和 Processor , 实现针对具体协议的处理功能。Tomcat按照协议和 IO 提供了6个实现类。

AJP协议:
  • AjpNioProtocol :采用 NIO 的 IO 模型。
  • AjpNio2Protocol:采用 NIO2 的IO模型。
  • AjpAprProtocol :采用 APR 的 IO 模型,需要依赖于 APR 库。
HTTP协议:
  • Http11NioProtocol :采用 NIO 的 IO 模型,默认使用的协议(如果服务器没有安装APR)。
  • Http11Nio2Protocol:采用 NIO2 的IO模型。
  • Http11AprProtocol :采用 APR 的IO模型,需要依赖于 APR 库。

在这里插入图片描述

当前对于 Endpoint 组件来说,在 Tomcat 中没有对应的 Endpoint 接口, 但是有一个抽象类 AbstractEndpoint ,其下有三个实现类: NioEndpoint, Nio2Endpoint, AprEndpoint . 这三个实现类,分别对应于前面连接器支持的三种 IO 模型:NIO,NIO2,APR , Tomcat 8.5.57 版本中,默认采用的是 NioEndpoint。

在这里插入图片描述

当初始化完 Endpoint 后, 开始启动各个组件

启动各个组件

当执行完 Catalina 的 load 方法后, Bootstrap 开始反射调用 Catalina 的 start 方法, Catalina 直接启动 Server

public final synchronized void start() throws LifecycleException {
    ....
     try {
         // 标记组件生命周期的状态
         setStateInternal(LifecycleState.STARTING_PREP, null, false)
  	     // 启动组件 Server -> Service -> Engine -> 子容器 (Host -> Pipeline -> HostValve) ->
         // 启动所有的 (Context -> Wrapper -> Pipeliine (Wrapper) -> WrapperValve ->
         // Pipeline(Context) -> Context(Valve) ) ->
         // Pipeline (Engine) -> EngineValve -> MapperListener -> Connector
         startInternal();
         if (state.equals(LifecycleState.FAILED)) {
             stop();
         }
         ....
     }
}

可以看到, 同样使用 模板设计模式, 将 start 方法的逻辑, 抽取放到公共父类 LifecycleBase

容器组件

在启动 Engine 组件的时候, 调用了父类 ContainerBasestartInternal 方法

protected synchronized void startInternal() throws LifecycleException {

        // 启动子组件
        logger = null;
        getLogger();

        // 检查是否存在集群, 如果有就启动他们
        Cluster cluster = getClusterInternal();
        if (cluster instanceof Lifecycle) {
            ((Lifecycle) cluster).start();
        }
        Realm realm = getRealmInternal();
        if (realm instanceof Lifecycle) {
            // 启动该容器对应的域对象
            ((Lifecycle) realm).start();
        }

        // 拿到子容器 Engine -> Host
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<>();

        // 线程池执行 Callable 任务, 异步调用子容器的 start 方法, 启动子容器
        for (Container child : children) {
            results.add(startStopExecutor.submit(new StartChild(child)));
        }

        MultiThrowable multiThrowable = null;

        // 获取 Callable 任务的返回值, 返回值为 void
        for (Future<Void> result : results) {
            try {
                // 等待异步任务处理完毕才会继续执行
                result.get();
            }
            catch (Throwable e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                if (multiThrowable == null) {
                    multiThrowable = new MultiThrowable();
                }
                multiThrowable.add(e);
            }

        }
        if (multiThrowable != null) {
            throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
                    multiThrowable.getThrowable());
        }

        // 启动该容器中 Pipeline 的 Valve (包括父类), 如果存在的话
        // 顺序应该为 Engine -> Host -> Wrapper
        if (pipeline instanceof Lifecycle) {
            ((Lifecycle) pipeline).start();
        }

        // 当 Engine 设置为 STARTING 状态后
        // 会被 HostConfig 监听到, 调用 HostConfig#start 方法
        // HostConfig#start 方法其实就是部署 Web 应用, 并绑定子容器 Context
        setState(LifecycleState.STARTING);

        // 启动一个线程, 该线程专门用于监听 Session 的过期时间
        threadStart();
    }

查看 HostConfig 的 start 方法

public void start() {

    .....

    // 部署 Web 应用程序
    if (host.getDeployOnStartup()) {
        deployApps();
    }

}
---------------------
// 查看 deployApps 方法
protected void deployApps() {

    File appBase = host.getAppBaseFile();
    File configBase = host.getConfigBaseFile();
    String[] filteredAppPaths = filterAppPaths(appBase.list());
    // 部署 server.xml 配置的 Web 应用
    deployDescriptors(configBase, configBase.list());
    // 部署 War 包应用
    deployWARs(appBase, filteredAppPaths);
    // 部署 webapps 文件夹中的 Web 应用
    // 将异步任务 DeployDirectory 交给线程池处理
    deployDirectories(appBase, filteredAppPaths);

}
-------------------
// 直接查看 DeployDirectory 的 run 方法
// 发现直接调用了 HostConfig 的 deployDirectory 方法
protected void deployDirectory(ContextName cn, File dir) {
    ....
    Class<?> clazz = Class.forName(host.getConfigClass());
    // 创建监听器 ContextConfig, 用于监听 Context 的事件
    LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
    // 将此监听器与此 Context 绑定
    context.addLifecycleListener(listener);

    context.setName(cn.getName());
    context.setPath(cn.getPath());
    context.setWebappVersion(cn.getVersion());
    context.setDocBase(cn.getBaseName());
    // 解析 Context 之后, 绑定到此 Host 的子节点中, 然后启动 Context 子组件
    host.addChild(context);
    ...
}

为 Host 绑定子组件后, 开始启动子容器组件 Context, 查看 Context 的标准实现 StandardContext 的 start 方法, 发现 Context 还未初始化, 先执行其 init 方法, 完成对 Context 的初始化, 然后启动 Context, 查看 startInternal 方法

protected synchronized void startInternal() throws LifecycleException {

    .....

    setConfigured(false);
    boolean ok = true;

    // 启动命名资源
    if (namingResources != null) {
        namingResources.start();
    }

    // 创建 work 目录
    postWorkDirectory();

    // 根据需要添加缺少的组件
    if (getResources() == null) {   // (1) Required by Loader
        ...

        try {
            // 设置 WebResourceRoot , 用于加载 context.xml 中的资源
            // 加载的类型支持三种 : 1. pre : 加载 context.xml 中的 <PreResource>
            // 2. main : 加载 Web 应用下 /WEB-INF/lib(class)
            // 3. jar : 加载 context.xml 中的 <JarResource>
            setResources(new StandardRoot(this));
        }
        ....
    }
    if (ok) {
        // 启动资源处理器 WebResourceRoot
        resourcesStart();
    }

    // 设置 WebApp 类加载器
    if (getLoader() == null) {
        WebappLoader webappLoader = new WebappLoader();
        webappLoader.setDelegate(getDelegate());
        setLoader(webappLoader);
    }

    ....

    try {
        if (ok) {
            // 启动 WebApp 类加载器
            Loader loader = getLoader();
            if (loader instanceof Lifecycle) {
                ((Lifecycle) loader).start();
            }

            .....

            // 启动此 Context 的的域
            Realm realm = getRealmInternal();
            if (null != realm) {
                if (realm instanceof Lifecycle) {
                    ((Lifecycle) realm).start();
                }

                ....
            }

            // 发出事件, 通知监听器监听, Context 会被 ContextConfig 监听到
            // 然后解析 web.xml 配置文件和 /WEB-INF/class 的文件, 将里面的
            // Servlet, Filter, Listener ...  包装成 Wrapper, 添加到 Context 子组件中
            // 然后启动子组件 Wrapper, 然后启动 Wrapper 的 Pipeline, WrapperValve
            // 启动 Wrapper 的时候, 也是调用父类的 startInternal 方法
            // 这里的 Servlet 并没有加载到 JVM 中, 只是解析并封装了
            fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

            // 启动此 Context 的子容器 Wrapper
            for (Container child : findChildren()) {
                if (!child.getState().isAvailable()) {
                    child.start();
                }
            }

            // 等待所有子容器启动完毕后, 启动 Context 的 Pipeline 和 ContextValve
            if (pipeline instanceof Lifecycle) { 
                ((Lifecycle) pipeline).start();
            }

            // 集群管理器
            Manager contextManager = null;
            Manager manager = getManager();
            if (manager == null) {
                ...
            }

            // 如果没指定, 使用默认的管理器
            if (contextManager != null) {
                ...
            }

            ...

        // 将资源放入 ServerContext 中
        if (ok) {
            .....
        }

        // Set up the context init params
        mergeParameters();

        // 调用 ServletContainerInitializers
        for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
             initializers.entrySet()) {
            try {
                entry.getKey().onStartup(entry.getValue(),
                                         getServletContext());
            }
           ....
        }

        // 启动监听器
        if (ok) {
            if (!listenerStart()) {
                log.error(sm.getString("standardContext.listenerFail"));
                ok = false;
            }
        }

        ....

        try {
            // 启动管理器
            Manager manager = getManager();
            if (manager instanceof Lifecycle) {
                ((Lifecycle) manager).start();
            }
        }
       ...
        // 启动过滤器
        if (ok) {
            if (!filterStart()) {
                log.error(sm.getString("standardContext.filterFail"));
                ok = false;
            }
        }

        // 加载并实例化配置了 <load-up-start> 的 Servlet
        if (ok) {
            if (!loadOnStartup(findChildren())) {
                log.error(sm.getString("standardContext.servletFail"));
                ok = false;
            }
        }

        ....
}

发现, 每个容器组件都继承了 ContainerBase 抽象类, 每个容器组件都有一个属性 Pipeline, 该 Pipeline 类似于管道, 可以添加 Valve , 而 Valve 就像阀门, 可以打开管道, 这就是 责任链模式 的经典应用

启动容器组件的顺序,

  • 先启动 Engine, 然后启动该容器对应的域对象 LockOutRealm, UserDatabaseRealm
  • 启动子容器 Host, 接着依次启动 : Pipeline -> AccessLogValve -> ErrorReportValve -> HostValve, 然后将 Host 的时间设置为 STARTING, 让 HostConfig 监听, 交个 HostConfig 处理
  • 然后 HostConfig 部署 Web 应用, 为 Host 添加了多个子容器 Context, 线程池异步启动 Host 子容器, 然后启动 : StandardRoot -> DirResourceSet -> WebappLoader, 然后解析 Web 应用中的 web.xml 配置文件和 /WEB-INF/class 文件 的 Servlet, Listener, Filter, 包装成 Wrapper 对象, 并添加到 Context 的子组件中, 并启动它们
  • 接着启动 Context 的子容器 Wrapper, 依次启动 Wrapper -> Pipeline -> WrapperValve
  • 启动完所有的 Wrapper 后, 启动 Context 的 Pipeline, 和 ContextValve, 接着启动 StandardManager 和 StandardSessionIDGenerator
  • 启动完所有的 Context 后, 才来启动 Engine 的 Pipeline, EngineValve
  • 最后再来启动 MapperListener

自此, 所有的 Container 组件才算启动完成, , 然后依次启动 线程池 (如果已配置的话) -> MapperListener -> Connector -> protocolHandler, 最后启动 Endpoint , 直接查看 startInternal 方法

public void startInternal() throws Exception {
    if (!running) {
        running = true;
        paused = false;

        // 实例化各个缓存
        processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                                socketProperties.getProcessorCache());
        eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                             socketProperties.getEventCache());
        nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                              socketProperties.getBufferPool());

        // 如果 server.xml 没有配置线程池, 则创建默认线程池
        // 线程池的核心线程数为 10, 最大线程数为 200, 最大空闲时间为 60s
        if (getExecutor() == null) {
            createExecutor();
        }

        // 初始化连接限制器, 用于限制最大连接数
        initializeConnectionLatch();

        // 创建 Poller 线程, 专门用于处理 Socket 的数据
        pollers = new Poller[getPollerThreadCount()];
        for (int i = 0; i < pollers.length; i++) {
            pollers[i] = new Poller();
            Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-" + i);
            pollerThread.setPriority(threadPriority);
            pollerThread.setDaemon(true);
            pollerThread.start();
        }

        // 新开一个 Acceptor 线程, 用于监听 Socket 的连接
        startAcceptorThreads();
    }
}

至此, Tomcat 启动完成, 由 Acceptor 线程监听 Socket 连接请求, Poller 线程处理 Socket 的读写请求, 并进一步处理

Tomcat 启动的大体流程

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Gp_2512212842/article/details/107432220