Tomcat学习笔记(一)-Tomcat的整体架构介绍

Scroll Down

Tomcat学习笔记(一)-Tomcat的整体架构介绍

浏览器访问Tomcat服务器流程分析

image-20200605150558547

  1. 用户通过浏览器进行了一个操作,比如输入网址并回车,或者是点击链接,接着浏览器获取了这个事件。

  2. 浏览器向服务端发出 TCP 连接请求。

  3. 服务程序接受浏览器的连接请求,并经过 TCP 三次握手建立连接(三次握手全流程解析)。

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

  5. 浏览器将该数据包推入网络,数据包经过网络传输,最终达到端服务程序。

  6. 服务端程序拿到这个数据包后,同样以 HTTP 协议格式解包,获取到客户端的意图。

  7. 得知客户端意图后进行处理,比如提供静态文件或者调用服务端程序获得动态结果。

  8. 服务器将响应结果(可能是 HTML 或者图片等)按照 HTTP 协议格式打包。

  9. 服务器将响应数据包推入网络,数据包经过网络传输最终达到到浏览器。

  10. 浏览器拿到数据包后,以 HTTP 协议的格式解包,然后解析数据,假设这里的数据是 HTML。

  11. 浏览器将 HTML 文件展示在页面上。

什么是servlet规范和servlet容器?

浏览器发送一个Http格式的请求,Http服务接收到请求后,需要调用具体的Java类来进行处理,如何能够找到相应的Java类呢?

最简单的就是使用if/else来实现,但是这样耦合性太高了,所以就定义了一个Servlet接口,实现了Servlet接口的业务类称为Servlet,而请求具体定位到哪一个Servlet,Servlet如何来实例化,这里就需要引入一个Servlet容器来进行解耦合

Servlet 容器用来加载和管理业务类。HTTP 服务器不直接跟业务类打交道,而是把请求交给 Servlet 容器去处理,Servlet 容器会将请求转发到具体的 Servlet,如果这个 Servlet 还没创建,就加载并实例化这个 Servlet,然后调用这个 Servlet 的接口方法。因此 Servlet 接口其实是Servlet 容器跟具体业务类之间的接口

Http服务器直接访问Java类 VS 使用Servlet容器访问Java类

image-20200605154958863

Servlet接口与Servlet容器介绍

Servlet接口

Servlet接口定义了5个方法

public interface Servlet {
    void init(ServletConfig config) throws ServletException;
    
    ServletConfig getServletConfig();
    
    void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;
    
    String getServletInfo();
    
    void destroy();
}

service是最重要的方法,业务类在这个方法中实现处理逻辑,方法中有两个参数,ServletRequestServletResponse,ServletRequest用于封装请求信息,ServletResponse用于封装返回信息,本质上都是对通信协议的封装.

init方法和destroy方法是和Servlet容器的生命周期相关的方法,Servlet容器在加载Servlet类的时候,会调用init方法,卸载的时候会调用destroy方法,我们可以在init方法中初始化一些资源,并在destroy方法中释放一些资源,例如SpringMVC中的DispatcherServlet就是在init方法中创建了自己的Spring容器

ServletConfig类的作用就是封装 Servlet 的初始化参数。你可以在 web.xml 给 Servlet 配置参数,并在程序里通过 getServletConfig 方法拿到这些参数,例如SpringMVC中通过ServletConfig配置配置文件的地址

Servlet 规范提供了 GenericServlet 抽象类,我们可以通过扩展它来实现 Servlet。虽然 Servlet 规范并不在乎通信协议是什么,但是大多数的 Servlet 都是在 HTTP 环境中处理的,因此 Servet 规范还提供了 HttpServlet 来继承 GenericServlet,并且加入了 HTTP 特性。这样我们通过继承 HttpServlet 类来实现自己的 Servlet,只需要重写两个方法:doGet 和 doPost。

Servlet容器

Servlet的工作流程
image-20200605163230782

当⽤户请求某个URL资源时:

  1. HTTP服务器会把请求信息使⽤ServletRequest对象封装起来
  2. 进⼀步去调⽤Servlet容器中某个具体的Servlet
  3. 在上一步中,Servlet容器拿到请求后,根据URL和Servlet的映射关系,找到相应的Servlet
  4. 如果Servlet还没有被加载,就⽤反射机制创建这个Servlet,并调⽤Servlet的init⽅法来完成初始化
  5. 接着调⽤这个具体Servlet的service⽅法来处理请求,请求处理结果使⽤ServletResponse对象封装
  6. 把ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端

Tomcat整体架构介绍

tomcat整体架构需要具有两个功能:

  • 和客户端浏览器进⾏交互,进⾏socket通信,将字节流和Request/Response等对象进⾏转换
  • Servlet容器处理业务逻辑

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

image-20200605163644150

Tomcat中的连接器-Coyote

连接器对 Servlet 容器屏蔽了协议及 I/O 模型等的区别,无论是 HTTP 还是 AJP,在容器中获取到的都是一个标准的 ServletRequest 对象。

Coyote 是Tomcat 中连接器的组件名称 , 是对外的接⼝。客户端通过Coyote与服务器建⽴连接、发送请
求并接受响应,具体做了以下4件事:

  1. Coyote 封装了底层的⽹络通信(Socket 请求及响应处理)
  2. Coyote 使Catalina 容器(容器组件)与具体的请求协议及IO操作⽅式完全解耦
  3. Coyote 将Socket 输⼊转换封装为 Request 对象,进⼀步封装后交由Catalina 容器进⾏处理,处理请求完成后, Catalina 通过Coyote 提供的Response 对象将结果写⼊输出流
  4. Coyote 负责的是具体协议(应⽤层)和IO(传输层)相关内容

Tomcat支持的的 IO模型与协议
Tomcat⽀持多种应⽤层协议和I/O模型,如下:

image-20200605164534565

Coyote的内部组件及流程介绍

Tomcat的设计者设计了三大组件,来实现网络通信,应用层协议解析,socket与Request/Response对象转换三大功能,分别是EndPoint,Processor和Adapter

网络通信的 I/O 模型是变化的,可能是非阻塞 I/O、异步 I/O 或者 APR。应用层协议也是变化的,可能是 HTTP、HTTPS、AJP。浏览器端发送的请求信息也是变化的。但是整体的处理逻辑是不变的,EndPoint 负责提供字节流给 Processor,Processor 负责提供 Tomcat Request 对象给 Adapter,Adapter 负责提供 ServletRequest 对象给容器。

如果要支持新的 I/O 方案、新的应用层协议,只需要实现相关的具体子类,上层通用的处理逻辑是不变的。

由于 I/O 模型和应用层协议可以自由组合,比如 NIO + HTTP 或者 NIO2 + AJP。Tomcat 的设计者将网络通信和应用层协议解析放在一起考虑,设计了一个叫 ProtocolHandler 的接口来封装这两种变化点。各种协议和通信模型的组合有相应的具体实现类。比如:Http11NioProtocol 和 AjpNioProtocol。
image-20200605164613859

image-20200605164618844

具体各大组件的作用:

组件作用描述
ProtocolHandlerCoyote 协议接⼝, 通过Endpoint 和 Processor , 实现针对具体协议的处
理能⼒。Tomcat 按照协议和I/O 提供了6个实现类 : AjpNioProtocol ,
AjpAprProtocol, AjpNio2Protocol , Http11NioProtocol ,
Http11Nio2Protocol ,Http11AprProtocol
EndPointEndPoint 是通信端点,即通信监听的接口,是具体的 Socket 接收和发送处理器,是对传输层的抽象,因此 EndPoint 是用来实现 TCP/IP 协议的。
ProcessorProcessor 用来实现 HTTP 协议,Processor 接收来自 EndPoint 的 Socket,读取字节流解析成 Tomcat Request 和 Response 对象,并通过 Adapter 将其提交到容器处理,Processor 是对应用层协议的抽象。
Adapter由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat 定义了自己的 Request 类来“存放”这些请求信息。ProtocolHandler 接口负责解析请求并生成 Tomcat Request 类。但是这个 Request 对象不是标准的 ServletRequest,也就意味着,不能用 Tomcat Request 作为参数来调用容器。Tomcat 设计者的解决方案是引入 CoyoteAdapter,这是适配器模式的经典运用,连接器调用 CoyoteAdapter 的 Sevice 方法,传入的是 Tomcat Request 对象,CoyoteAdapter 负责将 Tomcat Request 转成 ServletRequest,再调用容器的 Service 方法

EndPoint-Processor-Adapter处理过程

EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方法会调用 Processor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法。

Tomcat中的Servlet容器-Catalina

Tomcat的整体架构

Tomcat是⼀个由⼀系列可配置(conf/server.xml)的组件构成的Web容器,⽽Catalina是Tomcat的
servlet容器。从另⼀个⻆度来说,Tomcat 本质上就是⼀款 Servlet 容器, 因为 Catalina 才是 Tomcat 的核⼼ , 其他模块都是为Catalina 提供⽀撑的。 ⽐如 : 通过 Coyote 模块提供链接通信,Jasper 模块提供 JSP 引
擎,Naming 提供JNDI 服务,Juli 提供⽇志服务。

image-20200605171648794

容器的层次结构

Tomcat 设计了 4 种容器,分别是 Engine、Host、Context 和 Wrapper。这 4 种容器不是平行关系,而是父子关系。

image-20200605171758413

Container 组件的具体结构

​ Container组件下有⼏种具体的组件,分别是Engine、Host、Context和Wrapper。这4种组件(容器)
是⽗⼦关系。Tomcat通过⼀种分层的架构,使得Servlet容器具有很好的灵活性。

Engine:

  • 表示整个Catalina的Servlet引擎,⽤来管理多个虚拟站点,⼀个Service最多只能有⼀个Engine,但是⼀个引擎可包含多个Host

Host:

  • 代表⼀个虚拟主机,或者说⼀个站点,可以给Tomcat配置多个虚拟主机地址,⽽⼀个虚拟主机下可包含多个Context

Context:

  • 表示⼀个Web应⽤程序, ⼀个Web应⽤可包含多个Wrapper

Wrapper:

  • 表示⼀个Servlet,Wrapper 作为容器中的最底层,不能包含⼦容器

上述组件的配置其实就体现在conf/server.xml中,Tomcat 采用了组件化的设计,它的构成组件都是可配置的,最外层的是 Server,其他组件按照一定的格式要求配置在这个顶层容器中