Tomcat核心原理--学习笔记

Posted 若曦`

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat核心原理--学习笔记相关的知识,希望对你有一定的参考价值。


文中部分图片来自黑马程序员的视频截图

1. Http服务器与Tomcat

在网上的说法有很多种,比如Tomcat内嵌http服务器,或者说Tomcat本质就是一个Http服务器,其实两种说法都没问题,那Http服务器是什么呢?

从百度百科中可以得知,HTTP服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,可以处理浏览器等Web客户端的请求并返回相应响应。常见的Web服务器有Apache、 nginx 、IIS、Tomcat。

(1) Http服务器的请求处理过程

浏览器给服务器发送的是一个Http格式的请求,Http服务器收到这个请求后,需要调用服务端程序来处理请求,这个服务端程序在Javaweb中也就是指我们编写的Java类;那么不同的请求则需要不同的Java类来处理。

原始的Http服务器调用过程

在这种方式下呢,Http为了判断需要调用哪种业务类,需要在其中写许多if else等逻辑判断代码,严重耦合,为了进行解耦,Tomcat在其中添加了Servlet容器来进行解耦。

(2) Tomcat中Http服务器的请求处理方式


那么这时,当浏览器发送一个url请求,这时需要哪个业务类来进行对于的处理操作,这时不再由Http服务器进行判断,Http服务器会直接转发给Servlet容器,让Servlet容器去进行判断,调用相应的业务类.

其实这里的业务类其实就是Servlet,当业务类实现了Servlet接口之后,便成为了一个Servlet,也就能被用于Servlet容器中,这也就是在原生的Javaweb中,为什么业务类都需要实现Servlet或HttpServlet子接口的缘故。

那么这里我们就遇见了老朋友Servlet接口;

Servlet接口

package javax.servlet;   //Tomcat源码版本:6.0.20    
  import java.io.IOException;    
      
  public interface Servlet        
  
  //负责初始化Servlet对象。容器一旦创建好Servlet对象后,就调用此方法来初始化Servlet对象      
     public void init(ServletConfig config) throws ServletException;        
  
   //方法负责响应客户的请求,提供服务。当容器接收到客户端要求访问特定的servlet请求时,就会调用Servlet的service方法      
      public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;        
    /*   
      *Destroy()方法负责释放Servlet 对象占用的资源,当servlet对象结束生命周期时,servlet容器调用此方法来销毁servlet对象.   
    **/    
      public void destroy();    
      /*   
      说明:Init(),service(),destroy() 这三个方法是Servlet生命周期中的最重要的三个方法。   
      **/       
      /*   
      *返回一个字符串,在该字符串中包含servlet的创建者,版本和版权等信息   
      **/    
      public String getServletInfo();      
          
      /*   
     GetServletConfig: 返回一个ServletConfig对象,该对象中包含了Servlet初始化参数信息    
     **/        
      public ServletConfig getServletConfig();      
   

接着1.2中Servlet容器判断请求后,想调用对应的Servlet类时,如果对应Servlet类还未加载到容器中,那么就会通过反射机制,调用Servlet的init()方法去初始化对应的Servlet。

那么其中的逻辑处理,也就是调用的Servlet中的Service()方法。

整个处理流程也就如下图

Servlet容器的作用

  • 定位Servlet :Http服务器发送过来一个ServletRequest请求,判断需要哪个Servlet去处理
  • 加载Servlet:通过反射机制,调用init()方法,将业务类封装成一个servlet加载到servlet容器中
  • 调用Servlet:调用Servlet的service()方法,将结果集封装成一个ServletResponse返回给Http服务器

tomcat容器创建servlet实例的过程

当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件,然后对xml文件进行解析,并读取servlet注册信息。

然后,将每个应用中注册的servlet类都进行加载,并通过反射的方式实例化。(有时候也是在第一次请求时实例化)

在servlet注册时加上1如果为正数,则在一开始就实例化,如果不写或为负数,则第一次请求实例化。

2. Tomcat的整体架构

通过1.1与1.2,就可以明白实现Tomcat的大致思想,在Tomcat要实现两个核心功能

  • 处理Socket连接,负责网络字节流与Request和Response对象的转化
  • 加载和管理Servlet,处理Request请求

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

(1) Tomcat的连接器–Coyote

连接器的作用

  • 将Socket请求转换为一个ServletRequest对象,并交予容器进行处理
  • 将容器返回的ServletResponse对象解析后,响应给前端

Coyote(连接器)与Catalina(容器)

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

Catalina也就是Tomcat容器框架的名称。

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

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

Catalina最终使用HttpServletResponse将结果集写入输出流,谈到Response输出流,首先得明白Request和Response,这里就再概括一下

Request与Response

当浏览器发送一个请求,当Http服务器接受之后,就会创建一个request和response对象,然后把请求发送的数据封装到request对象中;

在Catalina中会将Request和Response对象封装成HttpServletRequest及HttpServletResponse;然后将这两个对象作为参数传递给Servlet的Service()方法。

Reponse的响应正文

Response的响应正文也就是响应流,通俗点说就是响应对象;响应正文是在浏览器上真正显示的内容。

Response的响应正文分为响应字节流与响应字节,本质是通过IO流操作的。

  • getWriter(); 字符流
  • getOutputStream(); 字节流

这里就不探讨Request与Response获取数据的细节了,知识点可以看这几篇博客

Request的功能_获取请求行数据和获取请求头数据
HttpServletresponse响应数据的方式

Response响应正文知识点的补充

① Tomcat支持的I/O模型

在Coyote中,Tomcat支持多种I/O模型和应用层协议;

IO模型简介
BIO阻塞式IO,即Tomcat使用传统的java.io进行操作。该模式下每个请求都会创建一个线程,对性能开销大,不适合高并发场景。优点是稳定,适合连接数目小且固定架构。
NIO非阻塞式IO,jdk1.4 之后实现的新IO。该模式基于多路复用选择器监测连接状态在通知线程处理,从而达到非阻塞的目的。比传统BIO能更好的支持并发性能。Tomcat 8.0之后默认采用该模式
AIO异步非阻塞式IO,jdk1.7后之支持 。与nio不同在于不需要多路复用选择器,而是请求处理线程执行完程进行回调调知,已继续执行后续操作。Tomcat 8之后支持。
APR全称是 Apache Portable Runtime/Apache可移植运行库),是Apache HTTP服务器的支持库。可以简单地理解为,Tomcat将以JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作。使用需要编译安装APR 库

在 8.0 之前,Tomcat 默认采用的I/O方式为 BIO,之后改为 NIO;无论是 NIO、NIO2还是 APR, 在性能方面均优于以往的BIO。

补充点:

Tomcat为了实现支持多种I/O模型和应用层协议,一个容器可能对接多个连接器,就好比
一个房间有多个门;
容器与连接器组装后这个整体叫作Service组件。Service本身是在连接器和容器外面多包了一层,把它们组装在一起;
一个Tomcat服务器内可能有多个Service组件,这样的设计也是出于灵活性的考虑。通过在Tomcat中配置多个Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。

② 连接器的组件


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

2) Tomcat 并没有EndPoint 接口,而是提供了一个抽象类AbstractEndpoint ,里面定义了两个内部类:Acceptor(监听者)和SocketProcessor(套接字处理器)。

Acceptor用于监听Socket连接请求
SocketProcessor用于处理接收到的Socket请求它实现了Runnable接口,在Run方法里调用协议处理组件Processor进行处理。
为了提高处理能力,SocketProcessor被提交到线程池来执行;而这个线程池叫作标准线程执行器(StandardThreadExecutor)

Tomcat没有使用JDK的原生线程池,而是在其基础上进行了拓展,这是因为原生的JDK线程池比较适合CPU密集的操作,而Tomcat通常需要IO密集的操作

源码层面的分析可以看这篇博客 原生线程池这么强大,Tomcat 为何还需扩展线程池?

应用方面的理解可以看这篇 对Tomcat线程池的一些理解

(2) Tomcat的容器–Catalina

Tomcat是一个由一系列可配置的组件构成的Web容器,而Catalina是Tomcat的servlet容器。

从Tomcat模块分层图可以看出,Catalina便是Tomcat中最重要的部分。

Tomcat 本质上就是一款 Servlet 容器,因此Catalina 才是 Tomcat 的核心其他模块
都是为Catalina 提供支撑的。

  • 通过Coyote 模块提供链接通信;
  • Jasper 模块提供JSP引擎;
  • Naming 提供JNDI 服务;
  • Juli 提供日志服务;

① 容器的层级结构

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

也可以通过Tomcat的server.xml配置来加深对Tomcat容器层级结构的理解。

持续更新中…

以上是关于Tomcat核心原理--学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

Tomcat核心原理--学习笔记

tomcat学习笔记系统架构和原理

tomcat学习笔记系统架构和原理

Tomcat学习笔记

Docker学习笔记

Docker学习笔记