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

devtools/2024/10/11 13:29:10/

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

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

1、Tomcat 启动流程

<a class=tomcat-19.png" />

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():销毁组件。

<a class=tomcat-20.png" />

2、各组件的默认实现

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

<a class=tomcat-21.png" />

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库。

<a class=tomcat-22.png" />

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

tomcat___74">1、tomcat 源码入口 类

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

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

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

 /*** 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 completedBootstrap 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 reportingif (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 。

<a class=tomcat-23.png" />

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

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

<a class=tomcat-24.png" />

4、步骤如下:

1) Connector组件Endpoint中的Acceptor监听客户端套接字连接并接收Socket。
2) 将连接交给线程池Executor处理,开始执行请求响应任务。
3) Processor组件读取消息报文,解析请求行、请求体、请求头,封装成Request对象。
4) Mapper组件根据请求行的URL值和请求头的Host值匹配由哪个Host容器、Context容器、Wrapper容器处理请求。
5) CoyoteAdaptor组件负责将Connector组件和Engine容器关联起来,把生成的Request对象和响应对象Response传递到Engine容器中,调用 Pipeline。
6) Engine容器的管道开始处理,管道中包含若干个Valve、每个Valve负责部分处理逻辑。执行完Valve后会执行基础的 Valve–StandardEngineValve,负责调用Host容器的Pipeline。
7) Host容器的管道开始处理,流程类似,最后执行 Context容器的Pipeline。
8) Context容器的管道开始处理,流程类似,最后执行 Wrapper容器的Pipeline。
9) Wrapper容器的管道开始处理,流程类似,最后执行 Wrapper容器对应的Servlet对象的 处理方法。

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

1、请求流程源码解析

<a class=tomcat-25.png" />

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

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

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

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

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


http://www.ppmy.cn/devtools/91606.html

相关文章

Python 设计模式之工厂函数模式

文章目录 案例基本案例逐渐复杂的案例 问题回顾什么是工厂模式&#xff1f;为什么会用到工厂函数模式&#xff1f;工厂函数模式和抽象工厂模式有什么关系&#xff1f; 工厂函数模式是一种创建型设计模式&#xff0c;抛出问题&#xff1a; 什么是工厂函数模式&#xff1f;为什么…

Flink Checkpoint expired before completing解决方法

在Flink消费Kafka日志的时候出现了这样的一则报错&#xff0c; JobManager报错如下&#xff1a; 2024-03-07 15:21:12,500 [Checkpoint Timer] WARN org.apache.flink.runtime.checkpoint.CheckpointFailureManager [] - Failed to trigger or complete checkpoint 181 for …

剖析算法内部结构----------贪心算法

什么是贪心算法&#xff1f; 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在问题求解过程中&#xff0c;每一步都采取当前状态下最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望导致最终的全局最优解的算法策略。 贪心算法的核心思想是做选择时&…

Robot Operating System——发布温度传感器数据

大纲 应用场景定义字段解释 案例 sensor_msgs::msg::Temperature 是 ROS (Robot Operating System) 中的一个消息类型&#xff0c;用于表示温度数据。它的定义在 sensor_msgs 包中&#xff0c;通常用于传输温度传感器的数据。 应用场景 环境监测 气象站&#xff1a;在气象站中…

Qt文件读写

Qt中文件读写类简述 包含头文件#include <QFile> 读写模式如下 枚举 文件读写步骤 1 先使用string 类型来接受打开文件的返回值 QFileDialog::getOpenFileName(this,"文件","./"); //打开一个文件 2 构建文件对象 Qfile ff (qstring)接受打…

服务优雅上下线实现方案-运维

1、前言 前一篇文章介绍了nginx通过nginx_upstream_check_module模块实现后端健康检查&#xff0c;基于这篇文章介绍一下服务优雅上下线的实现方案。 对于微服务来说&#xff0c;服务的优雅上下线是必要的。对于上线来说&#xff0c;如果服务没有启动成功&#xff0c;就不应该对…

Qt实现无边框窗口的拖动和缩放

在使用QT创建窗体的时候&#xff0c;为了使窗口美化&#xff0c;通常不使用QT自带的边框。会调用下面函数去除窗体边框。 setWindowFlags(Qt::FramelessWindowHint) 但是有个问题&#xff0c;当去除了QT自带边框后&#xff0c;窗体就变得不能移动了&#xff0c;也不能改变窗口大…

【C++】————哈希表

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;C 创作时间 &#xff1a;2024年8月6日 前言&#xff1a; 在计算机科学的广袤世界中&#xff0c;数据结构犹如基石&#xff0c;支撑着各种高效算法的构建与运行。而哈希表&#xff08;Hash Table&#xff09;&#…