Tomcat 的启动化过程分析(一)

介绍

该笔记是在学习拉勾教育 Java 高薪训练营后,结合课程和老师的视频,自己跟踪源码后做的笔记。

文章目录

Tomcat 的架构

Tomcat 在启动过程前,会先进行初始化,通过解析 server.xml 来创建一个多层的容器。这个容器由多个组件组成,以 server.xml 为例,这个 xml 文件包含了要启动的各组件。

<Server>
    <Service>
        <Connector />
        <Connector />
        <Engine>
            <Host>
                <Context />
            </Host>
        </Engine>
    </Service>
</Server>
  • 最顶层组件为 Server, Tomcat 在初始化时会首先创建一个 Server。通过创建 Digester 来解析 xml 文件,使用反射创建 StandardServer(默认的 server);
  • 顶层组件为 Service,一个 Server 可以有多个 Service,在代码里 StandardServer#initInternal 可看到会遍历每个 Service 的 init() 方法,在 init() 方法中每个 Service 实现自己的 initInternal() 方法。达到初始化一个 Server 时,会初始化一个或多个 Service。每个 Service 包含多个连接器和一个容器。Service 的主要作用是将连接器和容器包在一起,每个 Service 对应不同的端口号,即对应不同的应用。一个应用监听一个端口号,否则会有端口冲突,导致传过来的数据包不知道给哪个端口号;
  • Connector 为连接器组件。连接器负责网络通信,解析应用层协议,并使用适配器模式将 Request / Response 转换为 ServletRequest/ ServletResponse。有多个连接器,是因为不同连接器解析不同协议和支持不同的 I/O 模型,协议有 HTTP 1.1、HTTP 2 ,I/O 模型有 NIO、IO 等;
  • Engine 为容器组件,表示引擎,用来管理多个虚拟站点,一个 Server 只有一个 Engine。以 StandardServcie#initInternal 为例,使用 engine.init() 对其初始化,这里 engine 为 StandardEngine。容器有四个组件组成,用于接收连接器传来的 ServletRequest/ ServletResponse 进行处理,处理完后在发给连接器,让它发送给对应的客户端;
  • Host 为容器组件,表示虚拟主机(域名)。一个 Service 有多个 Host,即多个域名。比如有 localhost、https://manage.shopping.com/ 、https://user.shopping.com/ 等;
  • Context 为容器组件,表示一个 Web 应用程序,处理请求;
  • Wrapper 为容器组件,表示一个 Servlet。一个 Context 中有多个 Servlet,处理请求。

图片来自深入拆解Tomcat & Jetty,一个 Tomcat(即 Server)实例包含多个 Service,每个 Service 对应不同的应用,监听不同的端口。一个 Service 包含多个连接器和一个容器。连接器和容器间通过 ServletRequest 和 ServletRespone 进行通信。
Tomcat 的 Server 包含多个 Service

Tomcat 的一键启动

从前面的结构介绍可看出,Tomcat 的容器是存在父子关系的,一键启动是通过启动父组件,由父组件来启动一系列子组件完成的。
  各组件会继承 LifeCycle 接口的方法,同样状态所有组件调用相同方法,比如初始化,Server.init()、Service.init()、Engine.init() 等都调用 init 方法。同理,启动时,各组件也是调用同样的 start 方法。

Tomcat 的启动过程

反射创建实例 Catalina,调用该实例 Catalina 的初始化 load() 和启动 start() 方法。这两方法会调用各组件的方法,进行一键初始化和一键启动。

  • bootstrap.init(),反射创建实例 Catalina;
  • daemon.load(),daemon 为 bootstrap。初始化,会调用 Catalina.load() 方法,接着会调用各组件的初始化方法 initInternal,达到一键初始化;
  • daemon.start(),调用 Catalina.start() 方法,接着调用各组件的 start 方法,达到一键启动。
    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);
            }
        }

        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }

            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                // 初始化各组件
                daemon.load(args);
                // 启动各组件
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else if (command.equals("configtest")) {
                daemon.load(args);
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
                System.exit(0);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException &&
                    t.getCause() != null) {
                t = t.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值