Tomcat(1)基础以及原理解析
Tomcat基础
这里的Tomcat基础来自另一个教程:https://www.bilibili.com/video/BV1Y7411K7zz?p=103
JavaWeb概念
a)什么是 JavaWeb
JavaWeb 是指,所有通过 Java 语言编写可以通过浏览器访问的程序的总称,叫 JavaWeb。 JavaWeb 是基于请求和响应来开发的。
b)什么是请求
请求是指客户端给服务器发送数据,叫请求 Request。
c)什么是响应
响应是指服务器给客户端回传数据,叫响应 Response。
d)请求和响应的关系
请求和响应是成对出现的,有请求就有响应。
Web 资源的分类
web 资源按实现的技术和呈现的效果的不同,又分为静态资源和动态资源两种。
静态资源: html、css、js、txt、mp4 视频 ,jpg 图片
动态资源: jsp 页面、Servlet 程序
常用的 Web 服务器
Tomcat:由 Apache 组织提供的一种 Web 服务器,提供对 jsp 和 Servlet 的支持。它是一种轻量级的 javaWeb 容器(服务 器),也是当前应用最广的 JavaWeb 服务器(免费)。
Jboss:是一个遵从 JavaEE 规范的、开放源代码的、纯 Java 的 EJB 服务器,它支持所有的 JavaEE 规范(免费)。
GlassFish: 由 Oracle 公司开发的一款 JavaWeb 服务器,是一款强健的商业服务器,达到产品级质量(应用很少)。
Resin:是 CAUCHO 公司的产品,是一个非常流行的服务器,对 servlet 和 JSP 提供了良好的支持, 性能也比较优良,resin 自身采用 JAVA 语言开发(收费,应用比较多)。
WebLogic:是 Oracle 公司的产品,是目前应用最广泛的 Web 服务器,支持 JavaEE 规范, 而且不断的完善以适应新的开发要求,适合大型项目(收费,用的不多,适合大公司)。
Tomcat 服务器和 Servlet 版本的对应关系
当前企业常用的版本 7.*、8.*
Servlet 程序从 2.5 版本是现在世面使用最多的版本(xml 配置)
到了 Servlet3.0 之后。就是注解版本的 Servlet 使用。
以 2.5 版本为主线讲解 Servlet 程序。
Tomcat 的使用
a)安装
找到你需要用的 Tomcat 版本对应的 zip 压缩包,解压到需要安装的目录即可。
b)目录介绍
bin
专门用来存放 Tomcat 服务器的可执行程序conf
专门用来存放 Tocmat 服务器的配置文件lib
专门用来存放 Tomcat 服务器的jar
包logs
专门用来存放 Tomcat 服务器运行时输出的日记信息temp
专门用来存放 Tomcdat 运行时产生的临时数据webapps
专门用来存放部署的Web
工程。work
是 Tomcat 工作时的目录,用来存放 Tomcat 运行时 jsp 翻译为 Servlet 的源码,和 Session 钝化(序列化)的目录。
c)如何启动 Tomcat 服务器
找到 Tomcat 目录下的 bin 目录下的 startup.bat 文件,双击,就可以启动 Tomcat 服务器。
如何测试 Tomcat 服务器启动成功???
打开浏览器,在浏览器地址栏中输入以下地址测试:
- 1、http://localhost:8080
- 2、http://127.0.0.1:8080
- 3、http://真实 ip:8080
当出现如下界面,说明 Tomcat 服务器启动成功!!!
常见的启动失败的情况有,双击 startup.bat 文件,就会出现一个小黑窗口一闪而来。 这个时候,失败的原因基本上都是因为没有配置好 JAVA_HOME 环境变量。
常见的 JAVA_HOME 配置错误有以下几种情况:
一:JAVA_HOME 必须全大写。
二:JAVA_HOME 中间必须是下划线,不是减号
三:JAVA_HOME 配置的路径只需要配置到 jdk 的安装目录即可。不需要带上 bin 目录。
另一种启动 tomcat 服务器的方式
1、打开命令行
2、cd 到 你的 Tomcat 的 bin 目录下
3、敲入启动命令: catalina run
d)Tomcat 的停止
1、点击 tomcat 服务器窗口的 x 关闭按钮
2、把 Tomcat 服务器窗口置为当前窗口,然后按快捷键 Ctrl+C
3、找到 Tomcat 的 bin 目录下的 shutdown.bat 双击,就可以停止 Tomcat 服务器
e)如何修改 Tomcat 的端口号
Mysql 默认的端口号是:3306
Tomcat 默认的端口号是:8080
找到 Tomcat 目录下的 conf 目录,找到 server.xml 配置文件。
平时上百度:http://www.baidu.com:80
HTTP 协议默认的端口号是:80
80端口号默认不显示
部暑 web 工程到 Tomcat 中
1️⃣第一种部署方法:只需要把 web 工程的目录拷贝到 Tomcat 的 webapps 目录下即可。
1、在 webapps 目录下创建一个 book 工程:
2、把上午做的书城第一阶段的内容拷贝到里面:
3、如何访问 Tomcat 下的 web 工程。
只需要在浏览器中输入访问地址格式如下: http://ip:port/工程名/目录下/文件名
2️⃣第二种部署方法:
找到 Tomcat 下的 conf 目录\Catalina\localhost\ 下,创建如下的配置文件:
abc.xml 配置文件内容如下:
<!--Context 表示一个工程上下文 path 表示工程的访问路径:/abc docBase 表示你的工程目录在哪里 --> <Contextpath="/abc"docBase="E:\book"/>
访问这个工程的路径如下:http://ip:port/abc/ 就表示访问 E:\book 目录
ROOT 的工程的访问,以及 默认index.html 页面的访问
当我们在浏览器地址栏中输入访问地址如下: http://ip:port/ ---> 没有工程名的时候,默认访问的是 ROOT 工程。
当我们在浏览器地址栏中输入的访问地址如下: http://ip:port/工程名/ -----> 没有资源名,默认访问 index.html 页面
IDEA 整合 Tomcat 服务器
操作的菜单如下:RUN | Edit Application
配置你的 Tomcat 安装目录:
就可以通过创建一个 Model 查看是不是配置成功!!!
IDEA 中动态 web 工程的操作
IDEA 中创建动态 web 工程
1、创建一个新模块:
2、输入你的模块名,点击【Finish】完成创建。
Web 工程的目录介绍
如何给动态 web 工程添加额外 jar 包
1、可以打开项目结构菜单操作界面,添加一个自己的类库:
2、添加你你类库需要的 jar 包文件。
3、选择你添加的类库,给哪个模块使用:
4、选择 Artifacts 选项,将类库,添加到打包部署中:
如何在 IDEA 中部署工程到 Tomcat 上运行
1、建议修改 web 工程对应的 Tomcat 运行实例名称:
2、确认你的 Tomcat 实例中有你要部署运行的 web 工程模块:
3、你还可以修改你的 Tomcat 实例启动后默认的访问地址:
修改工程访问路径
修改运行的端口号
配置资源热部署
Tomcat核心原理解析
学习链接:https://www.bilibili.com/video/BV1dJ411N7Um?p=5
Tomcat基础
web概念
1). 软件架构
1. C/S: 客户端/服务器端 ‐‐‐‐‐‐‐‐‐‐‐‐> QQ , 360 ....
2. B/S: 浏览器/服务器端 ‐‐‐‐‐‐‐‐‐‐‐‐> 京东, 网易 , 淘宝 , 传智播客 官网
2). 资源分类
1. 静态资源: 所有用户访问后,得到的结果都是一样的,称为静态资源。静态资源可以直接被浏览器解析。
* 如: html,css,JavaScript,jpg
2. 动态资源: 每个用户访问相同资源后,得到的结果可能不一样 , 称为动态资源。动态资源被访问后,需要先转换为静态资源,再返回给浏览器,通过浏览器进行解析。
* 如:servlet/jsp,php,asp....
3). 网络通信三要素
1. IP:电子设备(计算机)在网络中的唯一标识。
2. 端口:应用程序在计算机中的唯一标识。 0~65536
3. 传输协议:规定了数据传输的规则
1. 基础协议:
1. tcp : 安全协议,三次握手。 速度稍慢
2. udp:不安全协议。 速度快
常见web服务器
web服务器是什么?
1). 服务器:安装了服务器软件的计算机
2). 服务器软件:接收用户的请求,处理请求,做出响应
3). web服务器软件:接收用户的请求,处理请求,做出响应。
在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目
常见的web服务器
1). webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
2). webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
3). JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
4). Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范 servlet/jsp。开源的,免费的。
Tomcat历史
1) Tomcat 初由Sun公司的软件架构师 James Duncan Davidson 开发,名称为 “JavaWebServer”。
2) 1999年 ,在 Davidson 的帮助下,该项目于1999年于apache 软件基金会旗下的 JServ 项目合并,并发布第一个版本(3.x), 即是现在的Tomcat,该版本实现了 Servlet2.2 和 JSP 1.1 规范 。
3) 2001年,Tomcat 发布了4.0版本, 作为里程碑式的版本,Tomcat 完全重新设计了 其架构,并实现了 Servlet 2.3 和 JSP1.2规范。 目前 Tomcat 已经更新到 9.0.x版本 , 但是目前企业中的Tomcat服务器, 主流版本还是 7.x 和 8.x , 所以本课程是基于 8.5 版本进行讲解。
Tomcat安装
安装包下载地址:https://tomcat.apache.org/download-80.cgi
将下载的 .zip 压缩包 , 解压到系统的目录(建议是没有中文不带空格的目录)下即可。
Tomcat目录结构
目录 | 目录下文件 | 说明 |
---|---|---|
bin | / | 存放Tomcat的启动、停止等批处理脚本文件 |
startup.bat , startup.sh |
用于在windows和linux下的启动脚本 | |
shutdown.bat , shutdown.sh |
用于在windows和linux下的停止脚本 | |
conf | / | 用于存放Tomcat的相关配置文件 |
Catalina | 用于存储针对每个虚拟机的Context配置 | |
context.xml | 用于定义所有web应用均需加载的Context配 置, 如果web应用指定了自己的context.xml ,该文件将被覆盖 |
|
catalina.properties | Tomcat 的环境变量配置 | |
catalina.policy | Tomcat 运行的安全策略配置 | |
logging.properties | Tomcat 的日志配置文件, 可以通过该文件修 改Tomcat 的日志级别及日志路径等 | |
server.xml | Tomcat 服务器的核心配置文件 | |
tomcat-users.xml | 定义Tomcat默认的用户及角色映射信息配置 | |
web.xml | Tomcat 中所有应用默认的部署描述文件, 主 要定义了基础Servlet和MIME映射。 | |
lib | / | Tomcat 服务器的依赖包 |
logs | / | Tomcat 默认的日志存放目录 |
webapps | / | Tomcat 默认的Web应用部署目录 |
work | / | Web 应用JSP代码生成和编译的临时目录 |
Tomcat的启动和停止
启动
双击 bin/startup.bat 文件
1.必须保证java环境变量已经配置好,配到jdk路径
2.如果启动遇到中文乱码的情况,可修改conf/logging.properties文件的50行左右的
java.util.logging.ConsoleHandler.encoding,从UTF-8修改为GBK
停止
双击 bin/shutdown.bat 文件
访问
http://localhost:8080
Tomcat源码
下载源码地址: https://tomcat.apache.org/download-80.cgi
- 创建一个Empty Project
- 解压zip压缩包到Project的目录
- 进入解压目录,并创建一个目录,命名为home , 并将conf、webapps目录移入 home 目录中
- 在当前目录下创建一个 pom.xml 文件,引入tomcat的依赖包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>apache-tomcat-8.5.50-src</artifactId>
<name>Tomcat8.5</name>
<version>8.5</version>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<finalName>Tomcat8.5</finalName>
<sourceDirectory>java</sourceDirectory>
<resources>
<resource>
<directory>java</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxrpc</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.5.1</version>
</dependency>
</dependencies>
</project>
- maven导入这个工程
- 配置idea的启动类, 配置 MainClass , 并配置 VM 参数
‐Dcatalina.home=D:/idea‐workspace/itcast_project_tomcat/apache‐tomcat8.5.42‐src/home ‐Dcatalina.base=D:/idea‐workspace/itcast_project_tomcat/apache‐tomcat8.5.42‐src/home ‐Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager ‐Djava.util.logging.config.file=D:/ideaworkspace/itcast_project_tomcat/apache‐tomcat‐8.5.42src/home/conf/logging.properties
- 启动主方法, 运行Tomcat , 访问Tomcat 。
出现上述异常的原因,是我们直接启动org.apache.catalina.startup.Bootstrap的时候没 有加载JasperInitializer,从而无法编译JSP。解决办法是在tomcat的源码ContextConfig 中的configureStart函数中手动将JSP解析器初始化:
context.addServletContainerInitializer(new JasperInitializer(), null);
重启tomcat就可以正常访问了。
Tomcat架构
Http工作原理
HTTP协议是浏览器与服务器之间的数据传送协议。作为应用层协议,HTTP是基于TCP/IP 协议来传递数据的(HTML文件、图片、查询结果等),HTTP协议不涉及数据包 (Packet)传输,主要规定了客户端和服务器之间的通信格式。
从图上你可以看到,这个过程是:
1) 用户通过浏览器进行了一个操作,比如输入网址并回车,或者是点击链接,接着浏览 器获取了这个事件。
2) 浏览器向服务端发出TCP连接请求。
3) 服务程序接受浏览器的连接请求,并经过TCP三次握手建立连接。 4) 浏览器将请求数据打包成一个HTTP协议格式的数据包。
5) 浏览器将该数据包推入网络,数据包经过网络传输,终达到端服务程序。
6) 服务端程序拿到这个数据包后,同样以HTTP协议格式解包,获取到客户端的意图。
7) 得知客户端意图后进行处理,比如提供静态文件或者调用服务端程序获得动态结果。
8) 服务器将响应结果(可能是HTML或者图片等)按照HTTP协议格式打包。 9) 服务器将响应数据包推入网络,数据包经过网络传输终达到到浏览器。
10) 浏览器拿到数据包后,以HTTP协议的格式解包,然后解析数据,假设这里的数据是 HTML。
11) 浏览器将HTML文件展示在页面上。
那我们想要探究的Tomcat作为一个HTTP服务器,在这个过程中都做了些什么事情呢?主 要是接受连接、解析请求数据、处理请求和发送响应这几个步骤。
Tomcat整体架构
Http服务器请求处理
浏览器发给服务端的是一个HTTP格式的请求,HTTP服务器收到这个请求后,需要调用服务端程序来处理,所谓的服务端程序就是你写的Java类,一般来说不同的请求需要由不同的Java类来处理。
1) 图1 , 表示HTTP服务器直接调用具体业务类,它们是紧耦合的。
2) 图2,HTTP服务器不直接调用业务类,而是把请求交给容器来处理,容器通过 Servlet接口调用业务类。因此Servlet接口和Servlet容器的出现,达到了HTTP服务器与业务类解耦的目的。而Servlet接口和Servlet容器这一整套规范叫作Servlet规范。 Tomcat按照Servlet规范的要求实现了Servlet容器,同时它们也具有HTTP服务器的功 能。作为Java程序员,如果我们要实现新的业务功能,只需要实现一个Servlet,并把它注册到Tomcat(Servlet容器)中,剩下的事情就由Tomcat帮我们处理了。
Servlet容器工作流程
为了解耦,HTTP服务器不直接调用Servlet,而是把请求交给Servlet容器来处理,那 Servlet容器又是怎么工作的呢?
当客户请求某个资源时,HTTP服务器会用一个ServletRequest
对象把客户的请求信息封装起来,然后调用Servlet容器的service方法,Servlet容器拿到请求后,根据请求的URL 和Servlet的映射关系,找到相应的Servlet,如果Servlet还没有被加载,就用反射机制创 建这个Servlet,并调用Servlet的init方法来完成初始化,接着调用Servlet的service方法 来处理请求,把ServletResponse
对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端。
Tomcat整体架构
我们知道如果要设计一个系统,首先是要了解需求,我们已经了解了Tomcat要实现两个 核心功能:
1) 处理Socket连接,负责网络字节流与Request和Response对象的转化。
2) 加载和管理Servlet,以及具体处理Request请求。
因此Tomcat设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。连接器负责对外交流,容器负责内部处理。
连接器Coyote
架构介绍
Coyote 是Tomcat的连接器框架的名称 , 是Tomcat服务器提供的供客户端访问的外部接口。客户端通过Coyote与服务器建立连接、发送请求并接受响应 。
Coyote 封装了底层的网络通信(Socket 请求及响应处理),为Catalina 容器提供了统一 的接口,使Catalina 容器与具体的请求协议及IO操作方式完全解耦。Coyote 将Socket 输 入转换封装为 Request 对象,交由Catalina 容器进行处理,处理请求完成后, Catalina 通 过Coyote 提供的Response 对象将结果写入输出流 。
Coyote 作为独立的模块,只负责具体协议和IO的相关操作, 与Servlet 规范实现没有直接关系,因此即便是 Request 和 Response 对象也并未实现Servlet规范对应的接口, 而 是在Catalina 中将他们进一步封装为ServletRequest 和 ServletResponse 。
IO模型与协议
在Coyote中 , Tomcat支持的多种I/O模型和应用层协议,具体包含哪些IO模型和应用层 协议,请看下表:
Tomcat 支持的IO模型(自8.5/9.0 版本起,Tomcat 移除了 对 BIO 的支持):
IO 模 型 | 描述 |
---|---|
NIO | 非阻塞I/O,采用Java NIO类库实现。 |
NIO2 | 异步I/O,采用JDK 7新的NIO2类库实现。 |
APR | 采用Apache可移植运行库实现,是C/C++编写的本地库。如果选择该方 案,需要单独安装APR库。 |
Tomcat 支持的应用层协议 :
应用层协议 | 描述 |
---|---|
HTTP/1.1 | 这是大部分Web应用采用的访问协议。 |
AJP | 用于和Web服务器集成(如Apache),以实现对静态资源的优化以及集群部署,当前支持AJP/1.3。 |
HTTP/2 | HTTP 2.0大幅度的提升了Web性能。下一代HTTP协议 , 自8.5以及9.0 版本之后支持。 |
协议分层 :
在 8.0 之前 , Tomcat 默认采用的I/O方式为 BIO , 之后改为 NIO。 无论 NIO、NIO2 还是 APR, 在性能方面均优于以往的BIO。
Tomcat为了实现支持多种I/O模型和应用层协议,一个容器可能对接多个连接器,就好比 一个房间有多个门。但是单独的连接器或者容器都不能对外提供服务,需要把它们组装起来才能工作,组装后这个整体叫作Service组件。这里请你注意,Service本身没有做什 么重要的事情,只是在连接器和容器外面多包了一层,把它们组装在一起。Tomcat内可能有多个Service,这样的设计也是出于灵活性的考虑。通过在Tomcat中配置多个 Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。
连接器组件
连接器中的各个组件的作用如下:
1️⃣EndPoint
1) EndPoint : Coyote 通信端点,即通信监听的接口,是具体Socket接收和发送处理 器,是对传输层的抽象,因此EndPoint用来实现TCP/IP协议的。
2) Tomcat 并没有EndPoint 接口,而是提供了一个抽象类AbstractEndpoint , 里面定 义了两个内部类:Acceptor和SocketProcessor。Acceptor用于监听Socket连接请求。 SocketProcessor用于处理接收到的Socket请求,它实现Runnable接口,在Run方法里 调用协议处理组件Processor进行处理。为了提高处理能力,SocketProcessor被提交到 线程池来执行。而这个线程池叫作执行器(Executor),我在后面的专栏会详细介绍 Tomcat如何扩展原生的Java线程池。
2️⃣Processor
Processor : Coyote 协议处理接口 ,如果说EndPoint是用来实现TCP/IP协议的,那么 Processor用来实现HTTP协议,Processor接收来自EndPoint的Socket,读取字节流解析成Tomcat Request和Response对象,并通过Adapter将其提交到容器处理, Processor是对应用层协议的抽象。
3️⃣ProtocolHandler
ProtocolHandler: Coyote 协议接口, 通过Endpoint 和 Processor , 实现针对具体协议的处理能力。Tomcat 按照协议和I/O 提供了6个实现类 : AjpNioProtocol , AjpAprProtocol, AjpNio2Protocol , Http11NioProtocol
,Http11Nio2Protocol , Http11AprProtocol。我们在配置tomcat/conf/server.xml 时 , 至少要指定具体的 ProtocolHandler , 当然也可以指定协议名称 , 如 : HTTP/1.1 ,如果安装了APR,那么将使用Http11AprProtocol , 否则使用 Http11NioProtocol 。
4️⃣Adapter
由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat定义了自己的Request类 来“存放”这些请求信息。ProtocolHandler接口负责解析请求并生成Tomcat Request类。 但是这个Request对象不是标准的ServletRequest,也就意味着,不能用Tomcat Request作为参数来调用容器。Tomcat设计者的解决方案是引入CoyoteAdapter,这是适配器模式的经典运用,连接器调用CoyoteAdapter的Sevice方法,传入的是Tomcat Request对象,CoyoteAdapter负责将Tomcat Request转成ServletRequest,再调用容 器的Service方法。
容器Catalina
Tomcat是一个由一系列可配置的组件构成的Web容器,而Catalina是Tomcat的servlet容 器。
Catalina 是Servlet 容器实现,包含了之前讲到的所有的容器组件,以及后续章节涉及到 的安全、会话、集群、管理等Servlet 容器架构的各个方面。它通过松耦合的方式集成 Coyote,以完成按照请求协议进行数据读写。同时,它还包括我们的启动入口、Shell程 序等。
Catalina 地位
Tomcat 的模块分层结构图, 如下:
Tomcat 本质上就是一款 Servlet 容器, 因此Catalina 才是 Tomcat 的核心 , 其他模块都是为Catalina 提供支撑的。
比如 : 通过Coyote 模块提供链接通信,Jasper 模块提供 JSP引擎,Naming 提供JNDI 服务,Juli 提供日志服务。
Catalina 结构
Catalina 的主要组件结构如下:
如上图所示,Catalina负责管理Server,而Server表示着整个服务器。Server下面有多个 服务Service,每个服务都包含着多个连接器组件Connector(Coyote 实现)和一个容器组件Container。在Tomcat 启动的时候, 会初始化一个Catalina的实例。
Catalina各个组件的职责:
组件 | 职责 |
---|---|
Catalina | 负责解析Tomcat的配置文件 , 以此来创建服务器Server组件,并根据 命令来对其进行管理 |
Server | 服务器表示整个Catalina Servlet容器以及其它组件,负责组装并启动 Servlet引擎,Tomcat连接器。 Server通过实现Lifecycle接口,提供了 一种优雅的启动和关闭整个系统的方式 |
Service | 服务是Server内部的组件,一个Server包含多个Service。 它将若干个 Connector组件绑定到一个Container(Engine)上 |
Connector | 连接器,处理与客户端的通信,它负责接收客户请求,然后转给相关 的容器处理,后向客户返回响应结果 |
Container | 容器,负责处理用户的servlet请求,并返回对象给web用户的模块 |
Container 结构
Tomcat设计了4种容器,分别是Engine、Host、Context和Wrapper。这4种容器不是平 行关系,而是父子关系。
Tomcat通过一种分层的架构,使得Servlet容器具有很好的灵活性。
各个组件的含义 :
容器 | 描述 |
---|---|
Engine | 表示整个Catalina的Servlet引擎,用来管理多个虚拟站点,一个Service 多只能有一个Engine,但是一个引擎可包含多个Host |
Host | 代表一个虚拟主机,或者说一个站点,可以给Tomcat配置多个虚拟主机地址,而一个虚拟主机下可包含多个Context |
Context | 表示一个Web应用程序, 一个Web应用可包含多个Wrapper |
Wrapper | 表示一个Servlet,Wrapper 作为容器中的底层,不能包含子容器 |
我们也可以再通过Tomcat的server.xml配置文件来加深对Tomcat容器的理解。Tomcat 采用了组件化的设计,它的构成组件都是可配置的,其中外层的是Server,其他组件 按照一定的格式要求配置在这个顶层容器中。
那么,Tomcat是怎么管理这些容器的呢?你会发现这些容器具有父子关系,形成一个树 形结构,你可能马上就想到了设计模式中的组合模式。没错,Tomcat就是用组合模式来 管理这些容器的。
具体实现方法是,所有容器组件都实现了Container接口,因此组合模 式可以使得用户对单容器对象和组合容器对象的使用具有一致性。这里单容器对象指的 是底层的Wrapper,组合容器对象指的是上面的Context、Host或者Engine。
Container 接口中提供了以下方法(截图中只是一部分方法) :
在上面的接口看到了getParent、SetParent、addChild和removeChild等方法。Container接口扩展了LifeCycle接口,LifeCycle接口用来统一管理各组件的生命周期,后面用专门的篇幅去详细介绍。
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的配置文件,初始化容器组件 ,监听对应的端口号, 准备接受客户端请求 。
源码解析
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。
ProtocolHandler : Coyote协议接口,通过封装Endpoint和Processor , 实现针对具体 协议的处理功能。Tomcat按照协议和IO提供了6个实现类。
AJP协议:
1) AjpNioProtocol :采用NIO的IO模型。
2) AjpNio2Protocol:采用NIO2的IO模型。
3) AjpAprProtocol :采用APR的IO模型,需要依赖于APR库。
HTTP协议:
1) Http11NioProtocol :采用NIO的IO模型,默认使用的协议(如果服务器没有安装 APR)。
2) Http11Nio2Protocol:采用NIO2的IO模型。
3) Http11AprProtocol :采用APR的IO模型,需要依赖于APR库。
3️⃣源码入口
目录: org.apache.catalina.startup
MainClass:BootStrap ‐‐‐‐> main(String[] args)
总结
从启动流程图中以及源码中,我们可以看出Tomcat的启动过程非常标准化, 统一按照生 命周期管理接口Lifecycle的定义进行启动。首先调用init() 方法进行组件的逐级初始化操 作,然后再调用start()方法进行启动。
每一级的组件除了完成自身的处理外,还要负责调用子组件响应的生命周期管理方法, 组件与组件之间是松耦合的,因为我们可以很容易的通过配置文件进行修改和替换。
Tomcat 请求处理流程
请求流程
设计了这么多层次的容器,Tomcat是怎么确定每一个请求应该由哪个Wrapper容器里的 Servlet来处理的呢?
答案是,Tomcat是用Mapper组件来完成这个任务的。
Mapper组件的功能就是将用户请求的URL定位到一个Servlet,它的工作原理是: Mapper组件里保存了Web应用的配置信息,其实就是容器组件与访问路径的映射关系, 比如Host容器里配置的域名、Context容器里的Web应用路径,以及Wrapper容器里 Servlet映射的路径,你可以想象这些配置信息就是一个多层次的Map。
当一个请求到来时,Mapper组件通过解析请求URL里的域名和路径,再到自己保存的 Map里去查找,就能定位到一个Servlet。请你注意,一个请求URL后只会定位到一个 Wrapper容器,也就是一个Servlet。
下面的示意图中 ,就描述了当用户请求链接 http://www.itcast.cn/bbs/findAll 之 后, 是如何找到终处理业务逻辑的servlet 。
那上面这幅图只是描述了根据请求的URL如何查找到需要执行的Servlet , 那么下面我们再来解析一下 , 从Tomcat的设计架构层面来分析Tomcat的请求处理。
步骤如下:
-
Connector组件Endpoint中的Acceptor监听客户端套接字连接并接收Socket。
-
将连接交给线程池Executor处理,开始执行请求响应任务。
-
Processor组件读取消息报文,解析请求行、请求体、请求头,封装成Request对象。
-
Mapper组件根据请求行的URL值和请求头的Host值匹配由哪个Host容器、Context容器、Wrapper容器处理请求。
-
CoyoteAdaptor组件负责将Connector组件和Engine容器关联起来,把生成的 Request对象和响应对象Response传递到Engine容器中,调用 Pipeline。
-
Engine容器的管道开始处理,管道中包含若干个Valve、每个Valve负责部分处理逻辑。执行完Valve后会执行基础的 Valve--StandardEngineValve,负责调用Host容器的 Pipeline。
-
Host容器的管道开始处理,流程类似,后执行 Context容器的Pipeline。
-
Context容器的管道开始处理,流程类似,后执行 Wrapper容器的Pipeline。
-
Wrapper容器的管道开始处理,流程类似,后执行 Wrapper容器对应的Servlet对象的处理方法。
请求流程源码解析
在前面所讲解的Tomcat的整体架构中,我们发现Tomcat中的各个组件各司其职,组件之间松耦合,确保了整体架构的可伸缩性和可拓展性,那么在组件内部,如何增强组件的灵活性和拓展性呢? 在Tomcat中,每个Container组件采用责任链模式来完成具体的 请求处理。
在Tomcat中定义了Pipeline 和 Valve 两个接口,Pipeline 用于构建责任链, 后者代表责 任链上的每个处理器。Pipeline 中维护了一个基础的Valve,它始终位于Pipeline的末端 (后执行),封装了具体的请求处理和输出响应的过程。当然,我们也可以调用 addValve()方法, 为Pipeline 添加其他的Valve, 后添加的Valve 位于基础的Valve之 前,并按照添加顺序执行。Pipiline通过获得首个Valve来启动整合链条的执行 。
Jasper
//todo