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响应数据的方式
① 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核心原理--学习笔记的主要内容,如果未能解决你的问题,请参考以下文章