嘘~ 正在从服务器偷取页面 . . .

Tomcat 执行流程


Tomcat 执行流程

一、文件结构

列举比较重要的

----tomcat-x.x.x
  |
  |------bin/                  # 存放Tomcat 启动停止等相关的脚本文件
  |      |
  |      |------startup.bat    # Windows 环境启动脚本
  |      |
  |      |------startup.sh     # Linux 环境启动脚本
  |      |
  |      |------shutdown.bat   # Windows 环境停止脚本
  |      |
  |      |------shutdown.sh    # Linux 环境停止脚本
  |
  |------conf/                 # Tomcat 相关配置文件的目录
  |      |
  |      |------Catalina       # 存放每个虚拟机的Conext配置
  |      |
  |      |------catalina.properties   # Tomcat 的环境变量配置
  |      |
  |      |------catalina.policy       # Tomcat 运行安全策略配置
  |      |
  |      |------context.xml     # 定义web应用均需要加载的Context配置,若web应用指定了自己的context.xml,该文件会被覆盖
  |      |
  |      |------logging.properties    # Tomcat 的环境变量配置
  |      |
  |      |------server.xml            # Tomcat 服务的核心配置文件
  |      |
  |      |------tomcat-users.xml      # 设置 Tomcat m默认的用户和角色映射信息配置
  |      |
  |      |------web.xml               # Tomcat 中所有应用默认的部署描述文件,主要定义基础默认的servlet 和 MIME 映射
  |    
  |------lib/                 # Tomcat 服务器依赖的.jar文件的目录
  |
  |------logs/                # Tomcat 默认的日志文件的目录
  |
  |------temp/                # 存放Tomcat 启运行时的临时文件的目录
  |
  |------webapps/             # Tomcat 默认的web应用工程部署目录
  |
  |------work/                # Web 应用中的jsp文件编译生成的 .java 和 .calss 文件存放目录
  |      |
  |      |
  |
  |------其他文件

二、HTTP 协议

1、基本概念

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。

HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。

HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。

主要特点:

  • 无连接 : 默认每次连接只处理一个轻轻。收到响应就会断开连接。
  • 无状态 : 指的是HTTP协议对事务处理没有记忆能力。
  • 媒体独立 : 任何类型的数据都可以使用HTTP发送,使用MIME-type指定内容类型即可。

2、HTTP 消息结构

HTTP是基于客户端/服务端(C/S)的架构模型。

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成。

客户端返回一个HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

下面是百度找的一张轻轻格式图:

HTTP Request

响应的格式基本一致,也找一张书里的图:

HTTP Response

请求方法:

HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法。

HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。

3、HTTP 工作过程:

HTTP 工作原理

(1)浏览器URL输入之后回车,或者点击某个超链接,浏览器开始获事件开始执行。

(2)访问域名会被DNS服务器解析成对应IP地址,默认端口是80。有了IP和端口,浏览器像服务端端发出TCP请求

(3)服务器程序接受浏览器连接请求,并行经过TCP三次握手建立连接。

(4)浏览器将请求数据打包成一个 HTTP协议 格式的数据包。

(5)浏览器将该该数据包发送到网络,经过网络传输到达(之前TCP握手之后建立的连接)服务端。

(6)服务端程序拿到这个数据包后,也使用 HTTP协议 格式解析数据包,获取客户端的资源请求。

(7)解析请求之后,根据相关调用(静态文件或动态程序)进行处理,获得返回结果。

(8)服务器将结果(HTML文件或其他格式文件)按照 HTTP协议格式打包。

(9)服务器将打包的结果发生到网络,经过传输之后,返回给浏览器。

(10)浏览器得到响应的数据包之后,使用 HTTP协议格式解析数据包,做出相关事件动作。(如HTML则进行文件解析展示,其他格式可能是下载)

三、Tomcat 架构

1、HTTP请求处理

HTTP 配合Servlet 工作

HTTP 服务把请求交给 Servlet容器 来处理,Servlet容器,再通过Servlet接口调用业务类。使用 Servlet容器Servlet接口 实现HTTP服务和业务解耦Servlet容器Servlet接口 相关使用的规范称为Servlet规范

Tomcat 按照 Servlet规范 的要求实现了 Servlet容器,同时他们也具有HTTP服务的功能。

2、Servlet容器工作流程

Servlet 工作流程

主要工作流程:

(1)HTTP服务器接收到客户端请求后,会将客户端请求信息封装到一个 ServletRequest 对象中。

(2)封装完成后,调用Servlet容器service() 方法。

(3)Servlet容器 获取到请求信息后,根据请求的URL 和 Servlet 的映射关系,找到对应的 Servlet (如果Servlet没有被加载,则使用反射创建对应的Servlet实例,并调用Servlet的 init()方法来完成初始化)。

(4)接着,调用对应的 Servlet 的 service() 方法来处理请求。

(5)请求处理完成完成后,将结果封装成 ServletResponse 对象返回给HTTP服务器。

(6)HTTP服务器再打包HTTP格式,响应给客户端。

3、Tomcat 架构

Tomcat 架构 基于上述工作流程。

Tomcat的2个核心功能:

(1)处理Socket连接,将请求与Request对象转换,将Response对象转换成响应。

(2)加载和管理Servlet,记忆具体处理Request请求。

因此Tomcat设计两个核心组件,连接器(Connector)和容器(Container)来分别完整这两个功能。Connector连接器负责对外交流,Container 负责对收到的请求做内部处理。

Servlet 架构

Tomcat 中有多个Services。

处理流程:

(1)客户端发起请求,tomcat的Connector连接器会接收Socket请求,并且将接收到的Socket 请求转换成ServletRequest对象。

(2)接着,将ServletRequest对象转交给Container容器进行处理。

(3)Container容器接收到ServletRequest 之后就去定位对应的Servlet,找到可以执行的Servlet,如果对应的Servlet没有加载,就先加载Servlet,找到之后定位对应的逻辑方法进行处理。

(4)对应的Servlet处理完成之后将处理的结果封装成ServletResponse对象响应给Connector连接器。

(5)Connector连接器接收到ServletResponse对象之后解析ServletResponse对象,给客户端的Socket连接最终响应。

4、链接器 Coyote

4.1 Coyote 架构

Coyote 是tomcat的链接器框架的名字,是tomcat服务器提供的供客户端访问的外部接口。客户端通过 Coyote 与服务器建立连接、发送请求并接收响应。

Coyote 封装了网络通信 Socket 请求和响应的处理,为 Catalina 容器提供了统一的接口,使 Catalina 容器与具体的请求协议以及IO操作完全解耦。

Coyote 与 Catalina 交互

Coyote 将Socket输入转换封装成Request对象,交由Catalina 容器进行处理,处理请求完成后,Catalina 通过Coyote 返回的Response对象将结果输出流返回。

Coyote 作为独立模块,只负责具体协议和IO操作,与 Servlet 规范实现没有直接关系,因此 Request 和 Response 对象也并未实现 Servlet 规范对应的接口,而是在 Catalina 容器中将他们进一步封装为 ServletRequest 和ServletResponse 。

4.2 IO模型与协议

tomcat 支持的IO:

版本IO模型描述
< 8.5BIO阻塞I/O,8.0之前默认的方式。
>= 8.5NIO非阻塞I/O,Java NIO类库实现
>= 8.5NIO2/AIO异步非阻塞I/O,采用最新的NIO类库实现
>= 8.5APR采用Apache可移植运行库实现,是C/C++编写的本地库。如果选择该方案,需要单独安装APR库。

tomcat 支持的应用层协议:

应用层协议描述
HTTP/1.1这是大部分web应用采用的访问协议
AJP用于和web服务器基础(如Apache),以实现对静态资源的优化以及集群部署,当前支持AJP/1.3
HTTP/2HTTP 2.0 大幅提升web性能,下一代HTTP协议,8.5及以后版本支持

协议分层:

--------------------------------------
应用层       HTTP     AJP     HTTP2
               processor
--------------------------------------
传输层       NIO     NIO2     APR
                Endpoint
--------------------------------------

Tomcat 本身支持多种IO模型和应用层协议,一个容器可以对接多个链接器。单独的链接器或单独的容器都不能对外提供服务,二者组装到一起才能工作,这个组装之后的整体成为Service组件

Service组件本身没有做什么特别的事情,只是将链接器和容器进行了包装,将二者组装到了一起。

Tomcat 内可能有多个Service,配置多个Service 可以实现通过不同端口号来访问同一个机器上部署的不同应用。

4.3 链接器组件

Tomcat 链接器组件

链接器各个组件作用:

EndPont 组件

(1)EndPoint :Coyote 通信端点,通信监听的接口,是具体Socket接收和发生处理器,是对传输层的抽象,因此 EndPoint 用来实现TCP/IP协议的。

(2)Tomcat 中并没有 EndPoint 接口,而是有一个AbstractEndPoint,里面定义了两个内部类,Acceptor 和 SocketProcessor 。Acceptor 用于监听Socket请求,SocketProcessor 用于处理接收到的Socket的请求,塔实现Runnable 接口,在Run方法里面调用协议处理组件Processor 进行处理。为了提高处理能力,SocketProcessor 被提交到线程池来执行。而这个线程池叫做执行器Executor。

Processor 组件

Processor :coyote 协议处理接口,如果说EndPoint 是用来实现TCP/IP协议的,那么Processor 用来实现HTTP协议,Processor 接收来自 EndPoint 的Socket请求,读取字节流解析成 Tomcat的 Request 和 Response 对象,并通过Adapter将其交到容器处理,Processor 是对应用层协议的抽象。

ProtocolHandler 组件

ProtocolHandler:Coyote 协议接口,通过EndPointProcessor ,实现针对具体协议的处理能力,Tomcat 按照协议和I/O提供6个实现类:AjpNioProtocolAjpAprProtocolAjpNio2ProtocolHttp11NioProtocolHttp11Nio2ProtocolHttp11AprProtocol

在配置tomcat/conf/server.xml时,至少要指定具体的ProtocolHandler,也可以指定协议名称如:HTTP/1.1,如果按照了APR,那么将使用HttpAprProtocol,否则使用Http11NioProtocol

Adapter 组件

由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat 定义了自己的Request 对来存在这些请求信息。

ProtocolHandler接口负责解析请求并生成Tomcat的Request类,但是这个Request对象不是标准的ServletRequest对象,即不能用Tomcat的Request作为参数来调用容器。于是有了这个 CoyoteAdapter适配器,适配器模式的经典运行。

链接器Coyote 调用CoyoteAdapter的Service方法,传入的是Tomcat的Request对象,CoyoteAdapter负责将

Tomcat的Request对象转换成ServletRequest,再调用容器的Service方法。

5、容器 Catalina

Tomcat 是由一系列的可配置组件构成的web容器,Tomcat的Servlet容器就叫做 Catalina

Catalina 是Servlet 容器的实现,包含前面学习的Coyote组件等。它通过松耦合的方法集成Coyote,以完成按照请求协议进行数据读写。同时,还包括启动入口,shell程序等。

5.1 Catalina 地位

Tomcat 组件分层结构:

Tomcat 的本质是Servlet 容器,因此 Catalina 是Tomcat的核心,其他模块都是为Catalina 模块提供支撑。如:通过Coyote 模块提供连接通信,Jasper模块提供JSP引擎,Naming 提供JNDI服务,Juli提供日志服务。

5.2 Catalina 结构

Catalina 结构

Catalina 负责管理Server,而Server表示整个服务器,Server下面有多个服务Service,每个服务都包含着多个链接器组件Connector(Coyote实现)和一个容器组件 Container。

在Tomcat 启动的时候,会初始化一个Catalina的实例。

书中的结构图:

Catalina 结构

Catalina 各个组件的职责:

组件职责
Catalina负责解析Tomcat 配置文件,创建服务器Server组件,并根据命令来进行管理。
ServerServer服务器表示整个Catalina Servlet容器以及其他组件,负责组装并启动Servlet引擎,Tomcat连接器。Server通过实现Lifecycle接口,提供一个优雅的启动和关系系统的方式。
Service服务就是Server内部的组件,一个Server可以包含多个Service。它将若干个Connector组件绑定到一个Container(Engine)上
Connector连接器,处理与客户端通信,负责接收客户端请求,然后传给相关容器处理,最后向客户端返回响应结果。
Container容器,负责处理用户的Servlet请求,并返回对应给web用户的模块

5.3 Container 结构

在 Container 中,tomcat 设计了4种容器分别是Engine、Host、Context和Wrapper。

Container 结构

Container 各个组件的职责:

组件职责
Engine表示整个Catalina的Servlet引擎,用来管理多个虚拟站点,一个Service最多只能有一个Engine,但是一个引擎可以包含多个Host。
HostHost代表一个虚拟主机(站点),可以给tomcat配置多个虚拟主机地址,而一个虚拟主机下可以包含多个Context。
Context表示一个web应用程序,一个web应用可以包含多个Wrapper。
Wrapper表示一个Servlet,Wrapper 作为容器的最底层,不能包含子容器。

这些关系如果不去看源码,也可以在server.xml中体现,这是一个删除了大部分注释的默认配置:

<?xml version="1.0" encoding="UTF-8"?>
 <Server port="8005" shutdown="SHUTDOWN">

  <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
  <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/>
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>

  <GlobalNamingResources>
    <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
  </GlobalNamingResources>

  <Service name="Catalina">

    <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>

    <Engine defaultHost="localhost" name="Catalina">

      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>

      <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">

        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log" suffix=".txt"/>

      <Context docBase="z-blog-BS" path="/zblog" reloadable="true" source="org.eclipse.jst.jee.server:z-blog-BS"/></Host>
    </Engine>
  </Service>
</Server>

6、Tomcat 启动流程

6.1、主要流程

Tomcat 启动流程图

主要步骤:

(1)Tomcat 启动执行启动脚本startup.batstartup.sh,在启动脚本中调用catalina.bat (catalina.sh)

(2)在catalina.bat 脚本中,调用Tomcat的 Bootstrap 类的main方法。

(3)Bootstrap 类的main方法调用 init 方法,来创建 Catalina 并 初始化类加载器。

(4)init 方法执行之后,调用 load 方法,load 方法中调用 Catalina 的 load 方法。

(5)在 Catalina 的 load 方法中,进行一些初始化操作,构造Digester 对象解析XML 。

(6)然后解析server.xml之后,就可以根据配置一层一层的将组件进行初始化。

(7)初始化然后之后,调用start 方法,整体过程基本同上,一层一层的进行 start 方法调用,启动组件。

6.2、主要接口

(1)Lifecycle 接口

抽象所有组件的初始化、启动、停止等声明周期方法,进行生命周期管理。

Server、Service、Container、Executor、Connector 组件都实现了改接口,也有了Lifecycle 接口核心方法:

  • init() :初始化组件
  • start() :启动组件
  • stop() :停止组件
  • destroy() :销毁组件

(2)组件接口的默认实现

其中,EndPoint 组件,没有对应的 EndPoint 接口,而是使用AbstractEndPoint抽象类,该抽象类下有三个实现类:NioEndPoint、Nio2EndPoint、AprEndPoint。这三个实现类,对应着Coyote链接器支持的三种IO模型:NIO、NIO2(AIO)、APR。(Tomcat 8.5版默认采用是NioEndPoint)

ProtocolHandler :coyote 协议接口,通过封装 EndPoint 和 Processor ,实现对具体协议的处理功能。Tomcat 安装协议和IO提供了6个实现类:

AJP协议:

  • AjpNioProtocol :采用NIO 的IO模型。
  • AjpNio2Protocol:采用NIO的IO模型。
  • AjpAprProtocol:采用APR的IO模型,依赖APR库。

HTTP协议:

  • Http11NioProtocol :采用NIO 的IO模型。默认使用的协议,如果服务器没有安装APR。
  • Http11Nio2Protocol :采用NIO2 的IO模型。
  • Http11AprProtocol:采用APR的IO模型,需要依赖APR库。

(3)源码main 方法入口:

(4)小结

从起点流程图就可以看出Tomcat的启动过程很标准化,统一安装Lifecycle 的定义进行启动。首先调用 init() 方法进行组件的逐级初始化操作,然后再调用 start() 方法进行启动。

每一层级组件除了完成自身的处理,还要负责调用子组件相应的生命周期管理方法,组件与组件之间是松耦合,因为可以使用配置文件进行修改和替换。

7、tomcat 处理流程

7.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://zhangxiaocai.cn/webdemo/findUsers

+--------------------------------------------------------------------------------+
                                                                     /findUsers
                                                       /webdemo     +-- Wrapper  
                              www.zhangxiaocai.cn   +- Context -+---+
                                          +------+  +               +-- Wrapper  
                                        +-+ Host +--+
                                        + +------+  +                                          
                                        +           +   /grabpic    +-- Wrapper 
                                        +           +- Context -+---+
       +-----------+    +-----------+   +                           +-- Wrapper 
       +           +    +  Service  +   +
URL -->+ Connector +--> +           +---+
       +           +    +  Engine   +   +
       +-----------+    +-----------+   +
                                        +              /blog        +-- Wrapper 
                                        +           +---Context--+--+
                                        + +------+  +               +-- Wrapper 
                                        +-+ Host +--+
                                          +------+  +
                              hut.zhangxiaocai.cn   +  /bbs         +-- Wrapper 
                                                    +- Context -+---+
                                                                    +-- Wrapper 

+---------------------------------------------------------------------------------+ 

Tomcat 请求处理流程图:

![](/images/http/

主要步骤:

(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容器的管道开始处理,流程与Engine类似,最后执行Context 的Pipeline。

(8)Context容器的管道开始处理,流程与Engine类似,最后执行Wrapper容器的Pipeline。

(9)Context 容器的管道开始处理,流程与Engine类似,最后执行Wrapper容器对应的Servlet对象的处理方法。

Tomcat 请求处理流程

Tomcat 中的各个组件各自处理各自的事情,组件之间松耦合,确保整体架构的可伸缩性和扩展性,在组件内部如何增强组件灵活性和扩展性?在Tomcat的每个Container 中采用责任链模式来完成具体的请求处理。

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

四、其他

Tomcat 相关的不少,配置部分单独。

学习参考资料:
(1)Tomcat 源码分析教程B站视频原链接
(2)《Tomcat 架构解析》
(3)网络资料整理。



版权声明: 本博客所有文章除特別声明外,均采用 CC BY-SA 4.0 许可协议。转载请注明来源 Small-Rose / 张小菜 !
评论
  目录