# 利刃出鞘_Tomcat 核心原理解析(三)

利刃出鞘_Tomcat 核心原理解析(三)

一、 Tomcat专题 - Tomcat架构 - 启动流程

1、Tomcat 启动流程

2、Tomcat 启动 步骤 :

1) 启动tomcat , 需要调用 bin/startup.bat (在linux 目录下 , 需要调用 bin/startup.sh) ,在startup.bat 脚本中, 调用了catalina.bat。

2) 在catalina.bat 脚本文件中,调用了BootStrap 中的main方法。

3)在BootStrap 的main 方法中调用了 init 方法 , 来创建Catalina 及 初始化类加载器。

4)在BootStrap 的main 方法中调用了 load 方法 , 在其中又调用了Catalina的load方法。

5)在Catalina 的load 方法中 , 需要进行一些初始化的工作, 并需要构造Digester 对象, 用于解析 XML。

6) 然后在调用后续组件的初始化操作 。。。

加载 Tomcat 的配置文件,初始化容器组件 ,监听对应的端口号, 准备接受客户端请求。

二、 Tomcat专题 - Tomcat架构 - 启动流程 - 涉及组件介绍

1、源码解析 Lifecycle

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

1) init():初始化组件。

2) start():启动组件。

3) stop():停止组件。

4) destroy():销毁组件。

2、各组件的默认实现

Server、Service、Engine、Host、Context 都是接口, 下图中罗列了这些接口的默认实现类。当前对于 Endpoint 组件来说,在 Tomcat 中没有对应的 Endpoint 接口, 但是有一个抽象类 AbstractEndpoint ,其下有三个实现类: NioEndpoint、Nio2Endpoint、AprEndpoint ,这三个实现类,分别对应于前面讲解链接器 Coyote 时, 提到的链接器支持的三种IO模型:NIO,NIO2,APR , Tomcat8.5版本中,默认采用的是 NioEndpoint。

3、ProtocolHandler : Coyote 协议接口,通过封装 Endpoint和Processor , 实现针对具体

协议的处理功能。Tomcat 按照协议和 IO 提供了6个实现类。

  • AJP协议:

1) AjpNioProtocol :采用NIO的IO模型。

2) AjpNio2Protocol:采用NIO2的IO模型。

3) AjpAprProtocol :采用APR的IO模型,需要依赖于APR库。

H- TTP协议:

1) Http11NioProtocol :采用NIO的IO模型,默认使用的协议(如果服务器没有安装

APR)。

2) Http11Nio2Protocol:采用NIO2的IO模型。

3) Http11AprProtocol :采用APR的IO模型,需要依赖于APR库。

三、 Tomcat专题 - Tomcat架构 - 启动流程 - 源码跟踪

1、tomcat 源码入口 类

目录: org.apache.catalina.startup.java

2、 tomcat 源码入口 main() 方法

MainClass:BootStrap ‐‐‐‐> main(String[] args)

java 复制代码
 /**
     * Main method and entry point when starting Tomcat via the provided
     * scripts.
     *
     * @param args Command line arguments to be processed
     */
    public static void main(String args[]) {

        System.out.println("main");

        if (daemon == null) {
            System.out.println("main if");
            // 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 {
            System.out.println("main 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);
        }

    }

四、 Tomcat专题 - Tomcat架构 - 启动流程 - 跟踪源码 - Debug

1、从启动流程图中以及源码中,我们可以看出Tomcat的启动过程非常标准化, 统一按照生命周期管理接口Lifecycle的定义进行启动。首先调用init() 方法进行组件的逐级初始化操作,然后再调用start()方法进行启动。

2、每一级的组件除了完成自身的处理外,还要负责调用子组件响应的生命周期管理方法, 组件与组件之间是松耦合的,因为我们可以很容易的通过配置文件进行修改和替换。

五、 Tomcat专题 - Tomcat架构 - 请求处理流程

1、 Tomcat 请求流程

1)设计了这么多层次的容器,Tomcat 是怎么确定每一个请求应该由哪个 Wrapper 容器里的 Servlet 来处理的呢?

答案是,Tomcat 是用 Mapper 组件来完成这个任务的。

2)Mapper 组件的功能就是将用户请求的 URL 定位到一个 Servlet,它的工作原理是:

Mapper 组件里保存了 Web 应用的配置信息,其实就是容器组件与访问路径的映射关系,

比如 Host 容器里配置的域名、Context 容器里的 Web 应用路径,以及 Wrapper 容器里

Servlet 映射的路径,你可以想象这些配置信息就是一个多层次的 Map。

3)当一个请求到来时,Mapper 组件通过解析请求 URL 里的域名和路径,再到自己保存的

Map 里去查找,就能定位到一个 Servlet。请你注意,一个请求URL最后只会定位到一个

Wrapper 容器,也就是一个 Servlet。

2、下面的示意图中 , 就描述了 当用户请求链接 http://www.itcast.cn/bbs/findAll

后, 是如何找到最终处理业务逻辑的 servlet 。

3、那上面这幅图只是描述了根据请求的URL如何查找到需要执行的Servlet , 那么下面我们

再来解析一下 , 从Tomcat的设计架构层面来分析Tomcat的请求处理。

4、步骤如下:

1) Connector组件Endpoint中的Acceptor监听客户端套接字连接并接收Socket。

  1. 将连接交给线程池Executor处理,开始执行请求响应任务。

  2. Processor组件读取消息报文,解析请求行、请求体、请求头,封装成Request对象。

  3. Mapper组件根据请求行的URL值和请求头的Host值匹配由哪个Host容器、Context容器、Wrapper容器处理请求。

  4. CoyoteAdaptor组件负责将Connector组件和Engine容器关联起来,把生成的Request对象和响应对象Response传递到Engine容器中,调用 Pipeline。

  5. Engine容器的管道开始处理,管道中包含若干个Valve、每个Valve负责部分处理逻辑。执行完Valve后会执行基础的 Valve--StandardEngineValve,负责调用Host容器的Pipeline。

  6. Host容器的管道开始处理,流程类似,最后执行 Context容器的Pipeline。

  7. Context容器的管道开始处理,流程类似,最后执行 Wrapper容器的Pipeline。

  8. Wrapper容器的管道开始处理,流程类似,最后执行 Wrapper容器对应的Servlet对象的 处理方法。

六、 Tomcat专题 - Tomcat架构 - 请求处理流程 - 源码跟踪

1、请求流程源码解析

2、在前面所讲解的Tomcat的整体架构中,我们发现Tomcat中的各个组件各司其职,组件之间松耦合,确保了整体架构的可伸缩性和可拓展性,那么在组件内部,如何增强组件的灵活性和拓展性呢?

在 Tomcat 中,每个 Container 组件采用责任链模式来完成具体的请求处理。

3、在 Tomcat 中定义了 Pipeline 和 Valve 两个接口,Pipeline 用于构建责任链, 后者代表责任链上的每个处理器。Pipeline 中维护了一个基础的Valve,它始终位于 Pipeline 的末端(最后执行),封装了具体的请求处理和输出响应的过程。

当然,我们也可以调用 addValve()方法, 为 Pipeline 添加其他的 Valve, 后添加的 Valve 位于基础的 Valve 之前,并按照添加顺序执行。Pipiline 通过获得首个Valve来启动整合链条的执行 。

上一节关联链接请点击
# 利刃出鞘_Tomcat 核心原理解析(二)

相关推荐
WannaRunning40 分钟前
浅谈Tomcat数据源连接池
java·oracle·tomcat
兢兢业业的小白鼠16 小时前
Java高级JVM知识点记录,内存结构,垃圾回收,类文件结构,类加载器
java·开发语言·jvm·tomcat
chushiyunen17 小时前
tomcat使用笔记、启动失败但是未打印日志
java·笔记·tomcat
杉之20 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
心灵宝贝1 天前
Tomcat 部署 Jenkins.war 详细教程(含常见问题解决)
java·tomcat·jenkins
生命是有光的1 天前
【中间件安全计划】锚定Tomcat安全基线
安全·中间件·tomcat
qq_431510161 天前
tomcat组件概览
java·tomcat
ccc_9wy2 天前
玄机-第六章 流量特征分析-常见攻击事件 tomcat的测试报告
网络安全·tomcat·玄机靶场·攻击流程·溯源分析·定时任务反弹shell·gobuster
当归10242 天前
Tomcat中的webapps的访问方式和java -jar内置Tomcat的访问方式的区别
java·tomcat·jar
magic 2452 天前
监听器(Listener)详解
java·servlet·tomcat