Tomcat分析
Tomcat 整体架构
Tomcat 是一个免费的、开源的、轻量级的 Web 应用服务器。适合在并发量不是很高的中小企业项目中使用。
文件目录结构
以下是 Tomcat 8 主要目录结构
| 目录 | 功能说明 |
|---|---|
| bin | 存放可执行的文件,如 startup 和 shutdown |
| conf | 存放配置文件,如核心配置文件 server.xml 和应用默认的部署描述文件 web.xml |
| lib | 存放 Tomcat 运行需要的jar包 |
| logs | 存放运行的日志文件 |
| webapps | 存放默认的 web 应用部署目录 |
| work | 存放 web 应用代码生成和编译文件的临时目录 |
功能组件结构
Tomcat 的核心功能有两个,分别是负责接收和反馈外部请求的连接器 Connector,和负责处理请求的容器 Container。其中连接器和容器相辅相成,一起构成了基本的 web 服务 Service。每个 Tomcat 服务器可以管理多个 Service。
| 组件 | 功能 |
|---|---|
| Connector | 负责对外接收反馈请求。它是 Tomcat 与外界的交通枢纽,监听端口接收外界请求,并将请求处理后传递给容器做业务处理,最后将容器处理后的结果反馈给外界。 |
| Container | 负责对内处理业务逻辑。其内部由Engine、Host、Context 和 Wrapper 四个容器组成,用于管理和调用 Servlet 相关逻辑。 |
| Service | 对外提供的 Web 服务。主要包含连接器和容器两个核心组件,以及其他功能组件。Tomcat 可以管理多个 Service,且各 Service 之间相互独立。 |

Tomcat 连接器核心原理
Tomcat 连接器框架——Coyote
连接器核心功能
一、监听网络端口,接收和响应网络请求。
二、网络字节流处理。将收到的网络字节流转换成 Tomcat Request 再转成标准的 ServletRequest 给容器,同时将容器传来的 ServletResponse 转成 Tomcat Response 再转成网络字节流。
连接器模块设计
为满足连接器的两个核心功能,我们需要一个通讯端点来监听端口;需要一个处理器来处理网络字节流;最后还需要一个适配器将处理后的结果转成容器需要的结构。
| 组件 | 功能 |
|---|---|
| Endpoint | 端点,用来处理 Socket 接收和发送的逻辑。其内部由 Acceptor 监听请求、Handler 处理数据、AsyncTimeout 检查请求超时。具体的实现有 NioEndPoint、AprEndpoint 等。 |
| Processor | 处理器,负责构建 Tomcat Request 和 Response 对象。具体的实现有 Http11Processor、StreamProcessor 等。 |
| Adapter | 适配器,实现 Tomcat Request、Response 与 ServletRequest、ServletResponse之间的相互转换。这采用的是经典的适配器设计模式。 |
| ProtocolHandler | 协议处理器,将不同的协议和通讯方式组合封装成对应的协议处理器,如 Http11NioProtocol 封装的是 HTTP + NIO。 |
对应的源码包路径 org.apache.coyote 。对应的结构图如下

Tomcat 容器核心原理
Tomcat 容器框架——Catalina
容器结构分析
每个 Service 会包含一个容器。容器由一个引擎可以管理多个虚拟主机。每个虚拟主机可以管理多个 Web 应用。每个 Web 应用会有多个 Servlet 包装器。Engine、Host、Context 和 Wrapper,四个容器之间属于父子关系。
| 容器 | 功能 |
|---|---|
| Engine | 引擎,管理多个虚拟主机。 |
| Host | 虚拟主机,负责 Web 应用的部署。 |
| Context | Web 应用,包含多个 Servlet 封装器。 |
| Wrapper | 封装器,容器的最底层。对 Servlet 进行封装,负责实例的创建、执行和销毁功能。 |
对应的源码包路径 org.apache.coyote 。对应的结构图如下

容器请求处理
容器的请求处理过程就是在 Engine、Host、Context 和 Wrapper 这四个容器之间层层调用,最后在 Servlet 中执行对应的业务逻辑。各容器都会有一个通道 Pipeline,每个通道上都会有一个 Basic Valve(如StandardEngineValve), 类似一个闸门用来处理 Request 和 Response 。其流程图如下。

Tomcat 请求处理流程
上面的知识点已经零零碎碎地介绍了一个 Tomcat 是如何处理一个请求。简单理解就是连接器的处理流程 + 容器的处理流程 = Tomcat 处理流程。哈!那么问题来了,Tomcat 是如何通过请求路径找到对应的虚拟站点?是如何找到对应的 Servlet 呢?
映射器功能介绍
这里需要引入一个上面没有介绍的组件 Mapper。顾名思义,其作用是提供请求路径的路由映射。根据请求URL地址匹配是由哪个容器来处理。其中每个容器都会它自己对应的Mapper,如 MappedHost。不知道大家有没有回忆起被 Mapper class not found 支配的恐惧。在以前,每写一个完整的功能,都需要在 web.xml 配置映射规则,当文件越来越庞大的时候,各个问题随着也会出现
HTTP请求流程
打开 tomcat/conf 目录下的 server.xml 文件来分析一个http://localhost:8080/docs/api 请求。
第一步:连接器监听的端口是8080。由于请求的端口和监听的端口一致,连接器接受了该请求。
第二步:因为引擎的默认虚拟主机是 localhost,并且虚拟主机的目录是webapps。所以请求找到了 tomcat/webapps 目录。
第三步:解析的 docs 是 web 程序的应用名,也就是 context。此时请求继续从 webapps 目录下找 docs 目录。有的时候我们也会把应用名省略。
第四步:解析的 api 是具体的业务逻辑地址。此时需要从 docs/WEB-INF/web.xml 中找映射关系,最后调用具体的函数。
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<!-- 连接器监听端口是 8080,默认通讯协议是 HTTP/1.1 -->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- 名字为 Catalina 的引擎,其默认的虚拟主机是 localhost -->
<Engine name="Catalina" defaultHost="localhost">
<!-- 名字为 localhost 的虚拟主机,其目录是 webapps-->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
</Host>
</Engine>
</Service>
</Server>


浙公网安备 33010602011771号