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 服务器启动成功???

打开浏览器,在浏览器地址栏中输入以下地址测试:

当出现如下界面,说明 Tomcat 服务器启动成功!!!

常见的启动失败的情况有,双击 startup.bat 文件,就会出现一个小黑窗口一闪而来。 这个时候,失败的原因基本上都是因为没有配置好 JAVA_HOME 环境变量。

常见的 JAVA_HOME 配置错误有以下几种情况:

一:JAVA_HOME 必须全大写。

二:JAVA_HOME 中间必须是下划线,不是减号

三:JAVA_HOME 配置的路径只需要配置到 jdk 的安装目录即可。不需要带上 bin 目录。

另一种启动 tomcat 服务器的方式

1、打开命令行

2、cd 到 你的 Tomcat 的 bin 目录下

image-20200628185714795

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 配置文件。

image-20200628185853831

平时上百度: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 安装目录:

image-20200628192235232

就可以通过创建一个 Model 查看是不是配置成功!!!

image-20200628192606822

IDEA 中动态 web 工程的操作

IDEA 中创建动态 web 工程

1、创建一个新模块:

2、输入你的模块名,点击【Finish】完成创建。

image-20200628194307747

Web 工程的目录介绍

image-20200628194332514

如何给动态 web 工程添加额外 jar 包

1、可以打开项目结构菜单操作界面,添加一个自己的类库:

image-20200628201241823

2、添加你你类库需要的 jar 包文件。

3、选择你添加的类库,给哪个模块使用:

4、选择 Artifacts 选项,将类库,添加到打包部署中:

如何在 IDEA 中部署工程到 Tomcat 上运行

1、建议修改 web 工程对应的 Tomcat 运行实例名称:

image-20200628202033046

image-20200628202005725

2、确认你的 Tomcat 实例中有你要部署运行的 web 工程模块:

image-20200628202126857

3、你还可以修改你的 Tomcat 实例启动后默认的访问地址:

image-20200628202158593

修改工程访问路径

image-20200628202357256

修改运行的端口号

image-20200628202414550

配置资源热部署

image-20200628202436433

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

image-20200628091701025

  1. 创建一个Empty Project
  2. 解压zip压缩包到Project的目录
  3. 进入解压目录,并创建一个目录,命名为home , 并将conf、webapps目录移入 home 目录中
  4. 在当前目录下创建一个 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>
  1. maven导入这个工程

image-20200628111454515

  1. 配置idea的启动类, 配置 MainClass , 并配置 VM 参数

image-20200628120802182

‐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

image-20200628120859741

  1. 启动主方法, 运行Tomcat , 访问Tomcat 。

image-20200628161055960

出现上述异常的原因,是我们直接启动org.apache.catalina.startup.Bootstrap的时候没 有加载JasperInitializer,从而无法编译JSP。解决办法是在tomcat的源码ContextConfig 中的configureStart函数中手动将JSP解析器初始化:

context.addServletContainerInitializer(new JasperInitializer(), null);

image-20200628161153182

重启tomcat就可以正常访问了。

Tomcat架构

Http工作原理

HTTP协议是浏览器与服务器之间的数据传送协议。作为应用层协议,HTTP是基于TCP/IP 协议来传递数据的(HTML文件、图片、查询结果等),HTTP协议不涉及数据包 (Packet)传输,主要规定了客户端和服务器之间的通信格式。

image-20200628161254662

从图上你可以看到,这个过程是:

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服务器会把响应发送给客户端。

image-20200628162714630

Tomcat整体架构

我们知道如果要设计一个系统,首先是要了解需求,我们已经了解了Tomcat要实现两个 核心功能:

1) 处理Socket连接,负责网络字节流与Request和Response对象的转化。

2) 加载和管理Servlet,以及具体处理Request请求。

因此Tomcat设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。连接器负责对外交流,容器负责内部处理。

image-20200628163155992

连接器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容器具有很好的灵活性。

image-20200628171154788

各个组件的含义 :

容器 描述
Engine 表示整个Catalina的Servlet引擎,用来管理多个虚拟站点,一个Service 多只能有一个Engine,但是一个引擎可包含多个Host
Host 代表一个虚拟主机,或者说一个站点,可以给Tomcat配置多个虚拟主机地址,而一个虚拟主机下可包含多个Context
Context 表示一个Web应用程序, 一个Web应用可包含多个Wrapper
Wrapper 表示一个Servlet,Wrapper 作为容器中的底层,不能包含子容器

我们也可以再通过Tomcat的server.xml配置文件来加深对Tomcat容器的理解。Tomcat 采用了组件化的设计,它的构成组件都是可配置的,其中外层的是Server,其他组件 按照一定的格式要求配置在这个顶层容器中。

image-20200628171449998

那么,Tomcat是怎么管理这些容器的呢?你会发现这些容器具有父子关系,形成一个树 形结构,你可能马上就想到了设计模式中的组合模式。没错,Tomcat就是用组合模式来 管理这些容器的。

具体实现方法是,所有容器组件都实现了Container接口,因此组合模 式可以使得用户对单容器对象和组合容器对象的使用具有一致性。这里单容器对象指的 是底层的Wrapper,组合容器对象指的是上面的Context、Host或者Engine

image-20200628173733233

Container 接口中提供了以下方法(截图中只是一部分方法) :

在上面的接口看到了getParent、SetParent、addChild和removeChild等方法。Container接口扩展了LifeCycle接口,LifeCycle接口用来统一管理各组件的生命周期,后面用专门的篇幅去详细介绍。

Tomcat 启动流程

流程

image-20200628174037610

步骤 :

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

image-20200628174321421

2️⃣各组件的默认实现

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

image-20200628174404578

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

image-20200628174602373

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 。

image-20200628183141669

那上面这幅图只是描述了根据请求的URL如何查找到需要执行的Servlet , 那么下面我们再来解析一下 , 从Tomcat的设计架构层面来分析Tomcat的请求处理。

image-20200628183251655

步骤如下:

  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对象的处理方法。

请求流程源码解析

image-20200628183755711

在前面所讲解的Tomcat的整体架构中,我们发现Tomcat中的各个组件各司其职,组件之间松耦合,确保了整体架构的可伸缩性和可拓展性,那么在组件内部,如何增强组件的灵活性和拓展性呢? 在Tomcat中,每个Container组件采用责任链模式来完成具体的 请求处理。

在Tomcat中定义了Pipeline 和 Valve 两个接口,Pipeline 用于构建责任链, 后者代表责 任链上的每个处理器。Pipeline 中维护了一个基础的Valve,它始终位于Pipeline的末端 (后执行),封装了具体的请求处理和输出响应的过程。当然,我们也可以调用 addValve()方法, 为Pipeline 添加其他的Valve, 后添加的Valve 位于基础的Valve之 前,并按照添加顺序执行。Pipiline通过获得首个Valve来启动整合链条的执行 。

Jasper

//todo

posted @ 2020-08-25 07:22  Whatever_It_Takes  阅读(614)  评论(0编辑  收藏  举报