目录
Tomcat 基础
1.1 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
:不安全协议。 速度快
1.2 常见的web服务器
1.2.1
概念
1
)
.
服务器:安装了服务器软件的计算机
2
)
.
服务器软件:接收用户的请求,处理请求,做出响应
3
)
. web
服务器软件:接收用户的请求,处理请求,做出响应。
在
web
服务器软件中,可以部署
web
项目,让用户通过浏览器来访问这些项目
1.2.2
常见
web
服务器软件
1). webLogic
:
oracle
公司,大型的
JavaEE
服务器,支持所有的
JavaEE
规范,收费的。
2). webSphere
:
IBM
公司,大型的
JavaEE
服务器,支持所有的
JavaEE
规范,收费的。
3). JBOSS
:
JBOSS
公司的,大型的
JavaEE
服务器,支持所有的
JavaEE
规范,收费的。
4). Tomcat
:
Apache
基金组织,中小型的
JavaEE
服务器,仅仅支持少量的
JavaEE
规范
servlet/jsp
。开源的,免费的
1.5 Tomcat 目录结构
Tomcat
的主要目录文件如下 :
2.Tomcat 架构
2.1 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
服务器,在这个过程中都做了些什么事情呢?主
要是接受连接、解析请求数据、处理请求和发送响应这几个步骤
2.2 Tomcat整体架构
2.2.1 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
帮我们处理了。
2.2.2 Servlet
容器工作流程
为了解耦,
HTTP
服务器不直接调用
Servlet
,而是把请求交给
Servlet
容器来处理,那
Servlet
容器又是怎么工作的呢?
当客户请求某个资源时,
HTTP
服务器会用一个
ServletRequest
对象把客户的请求信息封
装起来,然后调用
Servlet
容器的
service
方法,
Servlet
容器拿到请求后,根据请求的
URL
和
Servlet
的映射关系,找到相应的
Servlet
,如果
Servlet
还没有被加载,就用反射机制创
建这个
Servlet
,并调用
Servlet
的
init
方法来完成初始化,接着调用
Servlet
的
service
方法
来处理请求,把
ServletResponse
对象返回给
HTTP
服务器,
HTTP
服务器会把响应发送给
客户端.
2.2.3 Tomcat整体架构
我们知道如果要设计一个系统,首先是要了解需求,我们已经了解了
Tomcat
要实现两个
核心功能:
1
) 处理
Socket
连接,负责网络字节流与
Request
和
Response
对象的转化。
2
) 加载和管理
Servlet
,以及具体处理
Request
请求。
因此
Tomcat
设计了两个核心组件连接器(
Connector
)和容器(
Container
)来分别做这
两件事情。连接器负责对外交流,容器负责内部处理
2.3 连接器 - Coyote
2.3.1
架构介绍
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
。
2.3.2 IO
模型与协议
在
Coyote
中 ,
Tomcat
支持的多种
I/O
模型和应用层协议,具体包含哪些
IO
模型和应用层
协议,请看下表:
Tomcat
支持的
IO
模型(自
8.5/9.0
版本起,
Tomcat
移除了 对
BIO
的支持)
Tomcat
支持的应用层协议 :
Tomcat
为了实现支持多种
I/O
模型和应用层协议,一个容器可能对接多个连接器,就好比
一个房间有多个门。但是单独的连接器或者容器都不能对外提供服务,需要把它们组装
起来才能工作,组装后这个整体叫作
Service
组件。这里请你注意,
Service
本身没有做什
么重要的事情,只是在连接器和容器外面多包了一层,把它们组装在一起。
Tomcat
内可
能有多个
Service
,这样的设计也是出于灵活性的考虑。通过在
Tomcat
中配置多个
Service
,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。
2.3.3 连接器组件
连接器中的各个组件的作用如下:
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
线程池。
Processor
Processor
:
Coyote
协议处理接口 ,如果说
EndPoint
是用来实现
TCP/IP
协议的,那么
Processor
用来实现
HTTP
协议,
Processor
接收来自
EndPoint
的
Socket
,读取字节流解
析成
Tomcat Request
和
Response
对象,并通过
Adapter
将其提交到容器处理,
Processor
是对应用层协议的抽象
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
。
Adapter
由于协议不同,客户端发过来的请求信息也不尽相同,
Tomcat
定义了自己的
Request
类
来
“
存放
”
这些请求信息。
ProtocolHandler
接口负责解析请求并生成
Tomcat Request
类。
但是这个
Request
对象不是标准的
ServletRequest
,也就意味着,不能用
Tomcat
Request
作为参数来调用容器。
Tomcat
设计者的解决方案是引入
CoyoteAdapter
,这是
适配器模式的经典运用,连接器调用
CoyoteAdapter
的
Sevice
方法,传入的是
Tomcat
Request
对象,
CoyoteAdapter
负责将
Tomcat Request
转成
ServletRequest
,再调用容
器的
Service
方法
2.4 容器 - Catalina
Tomcat
是一个由一系列可配置的组件构成的
Web
容器,而
Catalina
是
Tomcat
的
servlet
容
器。
Catalina
是
Servlet
容器实现,包含了之前讲到的所有的容器组件,以及后续章节涉及到
的安全、会话、集群、管理等
Servlet
容器架构的各个方面。它通过松耦合的方式集成
Coyote
,以完成按照请求协议进行数据读写。同时,它还包括我们的启动入口、
Shell
程
序等。
2.4.1 Catalina 地位
Tomcat
的模块分层结构图, 如下:
Tomcat
本质上就是一款
Servlet
容器, 因此
Catalina
才是
Tomcat
的核心 , 其他模块
都是为
Catalina
提供支撑的。 比如 : 通过
Coyote
模块提供链接通信,
Jasper
模块提供
JSP
引擎,
Naming
提供
JNDI
服务,
Juli
提供日志服务。
2.4.2 Catalina 结构
Catalina
的主要组件结构如下:
如上图所示,
Catalina
负责管理
Server
,而
Server
表示着整个服务器。
Server
下面有多个
服务
Service
,每个服务都包含着多个连接器组件
Connector
(
Coyote
实现)和一个容器
组件
Container
。在
Tomcat
启动的时候, 会初始化一个
Catalina
的实例。
Catalina
各个组件的职责:
2.4.3 Container 结构
Tomcat
设计了
4
种容器,分别是
Engine
、
Host
、
Context
和
Wrapper
。这
4
种容器不是平
行关系,而是父子关系。,
Tomcat
通过一种分层的架构,使得
Servlet
容器具有很好的灵
活性。
各个组件的含义 :
我们也可以再通过
Tomcat
的
server.xml
配置文件来加深对
Tomcat
容器的理解。
Tomcat
采用了组件化的设计,它的构成组件都是可配置的,其中最外层的是
Server
,其他组件
按照一定的格式要求配置在这个顶层容器中。
那么,
Tomcat
是怎么管理这些容器的呢?你会发现这些容器具有父子关系,形成一个树
形结构,你可能马上就想到了设计模式中的组合模式。没错,
Tomcat
就是用组合模式来
管理这些容器的。具体实现方法是,所有容器组件都实现了
Container
接口,因此组合模
式可以使得用户对单容器对象和组合容器对象的使用具有一致性。这里单容器对象指的
是最底层的
Wrapper
,组合容器对象指的是上面的
Context
、
Host
或者
Engine
。
2.5 Tomcat
启动流程
2.5.1
流程
步骤
:
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
的配置文件,初始化容器组件 ,监听对应的端口号, 准备接受客户端请求
2.5.2
源码解析
2.5.2.1 Lifecycle
由于所有的组件均存在初始化、启动、停止等生命周期方法,拥有生命周期管理的特
性, 所以
Tomcat
在设计的时候, 基于生命周期管理抽象成了一个接口
Lifecycle
,而组
件
Server
、
Service
、
Container
、
Executor
、
Connector
组件 , 都实现了一个生命周期
的接口,从而具有了以下生命周期中的核心方法:
1
)
init
():初始化组件
2
)
start
():启动组件
3
)
stop
():停止组件
4
)
destroy
():销毁组件
从启动流程图中以及源码中,我们可以看出
Tomcat
的启动过程非常标准化, 统一按照生
命周期管理接口
Lifecycle
的定义进行启动。首先调用
init()
方法进行组件的逐级初始化操
作,然后再调用
start()
方法进行启动。
每一级的组件除了完成自身的处理外,还要负责调用子组件响应的生命周期管理方法,
组件与组件之间是松耦合的,因为我们可以很容易的通过配置文件进行修改和替换。
2.6 Tomcat
请求处理流程
2.6.1
请求流程
设计了这么多层次的容器,
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
。
步骤如下
:
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
中的各个组件各司其职,组件
之间松耦合,确保了整体架构的可伸缩性和可拓展性,那么在组件内部,如何增强组件
的灵活性和拓展性呢? 在
Tomcat
中,每个
Container
组件采用责任链模式来完成具体的
请求处理。
在
Tomcat
中定义了
Pipeline
和
Valve
两个接口,
Pipeline
用于构建责任链, 后者代表责
任链上的每个处理器。
Pipeline
中维护了一个基础的
Valve
,它始终位于
Pipeline
的末端
(最后执行),封装了具体的请求处理和输出响应的过程。当然,我们也可以调用
addValve()
方法, 为
Pipeline
添加其他的
Valve
, 后添加的
Valve
位于基础的
Valve
之
前,并按照添加顺序执行。
Pipiline
通过获得首个
Valve
来启动整合链条的执行 。