SpringMVC与Servlet容器[Tomcat]
- [1. Servlet容器:Java Web应用的运行基石](#1. Servlet容器:Java Web应用的运行基石)
-
- [1.1 容器的本质与核心职责](#1.1 容器的本质与核心职责)
- [1.2 从传统部署到嵌入式演进](#1.2 从传统部署到嵌入式演进)
- [2. Tomcat与Undertow对比](#2. Tomcat与Undertow对比)
-
- [2.1 设计哲学与特性](#2.1 设计哲学与特性)
- [2.2 性能差异的根源:线程模型 vs 事件驱动](#2.2 性能差异的根源:线程模型 vs 事件驱动)
- [2.3 选型建议:没有最好,只有最合适](#2.3 选型建议:没有最好,只有最合适)
- [3. HTTP与WebSocket:协作而非替代](#3. HTTP与WebSocket:协作而非替代)
-
- [3.1 核心差异:通信模式的不同](#3.1 核心差异:通信模式的不同)
- [3.2 清晰界定:两种协议的角色与协同](#3.2 清晰界定:两种协议的角色与协同)
- [4. 总结](#4. 总结)
- [5. Tomcat与Spring Boot](#5. Tomcat与Spring Boot)
-
- [5.1 Tomcat与Spring Boot的分工🏗️](#5.1 Tomcat与Spring Boot的分工🏗️)
- [5.2 Tomcat的核心作用:连接器、线程池与容器🧵](#5.2 Tomcat的核心作用:连接器、线程池与容器🧵)
- [5.3 Tomcat的容量:线程、连接与队列📊](#5.3 Tomcat的容量:线程、连接与队列📊)
- [5.4 总结](#5.4 总结)
姊妹篇 :Idea2023创建Servlet项目
在构建【Java Web应用】时,无论我们使用的是 传统的Spring MVC 还是 现代化的Spring Boot(依赖 spring-boot-starter-web),一个核心组件总是在幕后默默工作 ------ 【Servlet容器】 。它就像是数字世界中的 "引力",虽然不经常被直接讨论,++却决定了 应用 如何 与 网络世界连接、通信及扩展++ 。常见的Servlet容器有:Tomcat、Undertow、Jetty、Weblogic 等 。同时,为满足实时交互的需求,WebSocket协议也已成为现代应用的标配。
1. Servlet容器:Java Web应用的运行基石
1.1 容器的本质与核心职责
"容器"一词非常形象。我们可以把它理解为 专门为"Servlet"这类程序提供生存、运行和管理环境的托管平台 。Servlet 本身是一个标准的【Java接口】 (javax.servlet.Servlet,jakarta.servlet.Servlet(Java EE已经正式更名为Jakarta EE[雅加达])):定义了处理网络请求和响应的生命周期方法。
Servlet容器的核心职责包括以下4个方面:
- 生命周期管理 :负责Servlet类文件的加载、实例化、初始化(调用
init())、服务调用(调用service())以及最终的销毁(调用destroy())。 - 通信协议抽象 :容器接管了复杂的网络I/O操作。++它将原始的、基于TCP/IP的HTTP字节流,解析并封装成Java开发者友好的
HttpServletRequest和HttpServletResponse对象++。开发者无需处理套接字编程,只需专注于业务逻辑。 - 资源与环境管理 :提供统一的
ServletContext环境来存放共享数据,管理用户会话(HttpSession),并处理线程池、连接池等基础资源。 - 规范与可移植性保障:所有遵循 Java Servlet规范的容器(如Tomcat, Undertow, Jetty, WebLogic等),其行为都是一致的。这确保了基于Servlet开发的应用可以 "一次编写,到处运行",在不同容器间迁移时,核心代码无需修改。


1.2 从传统部署到嵌入式演进
Servlet容器的角色演进:
- 传统部署模式(十几年前) :Tomcat 作为一个独立的、系统级的服务器进程存在(需要先独立安装、配置Tomcat)。开发者需要将应用程序打包成
.war文件,然后将其部署到 Tomcat 的指定目录(如webapps)。应用作为容器的 "租客" 运行。 - 现代嵌入式模式(Spring Boot时代) :Spring Boot 通过
spring-boot-starter-web等组件,将Tomcat 或 Undertow 作为 内嵌的库(JAR包) 直接打包进应用。当执行java -jar或者YourApplication.main()启动应用时,Tomcat容器 从应用内部启动,成为 jvm进程 的一部分。这种方式简化了部署,实现了应用的自包含,完美契合了微服务和云原生理念。
2. Tomcat与Undertow对比
虽然都符合Servlet规范,但 Apache Tomcat 和 Red Hat的Undertow 在 架构设计 和 性能特性上有着显著差异,这直接影响了它们在不同场景下的适用性。
2.1 设计哲学与特性
| 对比维度 | Apache Tomcat | Undertow (Red Hat) |
|---|---|---|
| 出身与定位 | Apache基金会旗舰项目,历史最悠久、生态最成熟、事实上的行业标准。定位为通用、稳健的Web应用服务器。 | 源于JBoss/WildFly应用服务器,以高性能、低开销、可嵌入为核心设计目标 的现代化Web服务器。 |
| 架构与I/O模型 | 早期基于 阻塞式I/O(BIO) ,后引入 非阻塞I/O(NIO/NIO2) 选项。采用较为传统的连接器(Connector)架构。 |
从头至尾采用非阻塞I/O ,基于高性能的XNIO框架。采用 事件驱动、回调式的架构,核心JAR包仅约1MB,极其轻量。 |
| 性能特点 | 均衡稳健。在常规的请求-响应式Web应用(如MVC、REST API)中表现可靠。在高并发长连接场景下,默认的线程池模型可能带来更高的内存开销和上下文切换成本。 | 高并发、低延迟、低内存占用 。其事件驱动模型在处理 大量并发持久连接(如WebSocket、HTTP Streaming)和文件传输时优势明显,资源利用率极高。 |
| 内存与启动速度 | 相对较高(核心模块约8MB),启动速度适中。 | 极致轻量(核心模块约1MB),启动速度通常更快,非常适合资源受限的微服务和Serverless环境。 |
| 功能与扩展 | 功能全面,内置JSP编译器、WebSocket、安全管理等大量企业级特性,生态系统庞大。 | "核心精简,按需扩展"。专注于Servlet和WebSocket核心,其他功能需通过插件或编程方式添加,提供了极高的灵活性。 |
2.2 性能差异的根源:线程模型 vs 事件驱动
两者性能差异的本质源于其处理请求的模型不同。我们可以通过一个 "餐厅后厨" 的类比来理解:
-
Tomcat 的线程池模型 :好比一个拥有固定厨师团队(工作线程)的餐厅。每个顾客订单(HTTP请求)需要由一位厨师 全程负责,从接单到上菜。如果某道菜制作很慢(如慢SQL查询),对应的厨师就会被一直占用,即使他大部分时间在等待。当厨师全部被占用时,新顾客只能排队等候(请求队列)。这种模型简单可靠,但在面对大量 "慢订单" 或 需要长时间保持连接的"包间服务"(WebSocket)时,资源效率会下降。
-
Undertow 的事件驱动模型 :如同一个高度协同的现代化厨房。厨师被细分为 接单员、配菜员、炒菜员、摆盘员 (相当于多个事件处理器)。一个订单被拆解成多个步骤,每个步骤由最合适的专员快速处理,然后立即转交下一步,专员不会被单个订单长期阻塞。这种模式特别擅长处理 海量并发的小任务或需要长时间维持但交互稀疏的连接,资源复用率极高。
2.3 选型建议:没有最好,只有最合适
- 选择 Tomcat,如果 :项目是典型的Web应用(如企业内部管理系统、电商前台),追求 极致的稳定性、广泛的社区支持和丰富的现成文档;或者项目团队对其有深厚的技术积累。Tomcat 依然是 Spring Boot的默认选择,这本身就证明了其普适性。
- 选择 Undertow,如果 :应用是 高并发的API网关、实时通信服务(如聊天、游戏) ,或运行在资源严格受限的 微服务/Serverless环境 中;对 极致的吞吐量和低延迟有明确要求,并愿意为了性能进行更精细的配置。
3. HTTP与WebSocket:协作而非替代
在理解容器之后,我们还需要看清在其上运行的协议。WebSocket 常被误解为 HTTP 的替代品,实则不然,它们是互补的协作关系。
3.1 核心差异:通信模式的不同
-
HTTP协议:
- HTTP协议是Web的基石,但其 【请求-响应(Request-Response) 模型】存在天然限制:通信永远由客户端发起,服务器无法主动推送信息。这对于需要实时性的应用(如股票行情、在线协作)是致命缺陷。
-
WebSocket协议:
- WebSocket协议 的出现 正好填补了这一空白。它在一次HTTP "握手" 升级后,建立一条 全双工、持久化的TCP通道。此后,服务器和客户端可以随时、主动地向对方发送消息,实现了真正的双向平等通信。
3.2 清晰界定:两种协议的角色与协同
下图清晰地展示了一次典型的 WebSocket连接建立过程,以及两种协议如何协同工作:
服务器 (Servlet容器) 客户端 (浏览器) 服务器 (Servlet容器) 客户端 (浏览器) 第一阶段: HTTP握手升级 第二阶段: WebSocket全双工通信 loop [持续双向通信] HTTP GET请求 含头: Upgrade: websocket HTTP 101 Switching Protocols 同意升级协议 (服务器可主动推送) "您有新的消息" (客户端随时发送) "用户回复:你好!" 推送数据... 发送数据...
从图中可以看出,HTTP与WebSocket是前后衔接、各司其职的:
- 连接建立 :WebSocket连接始于一个特殊的 HTTP请求 (使用
Upgrade: websocket头部)。服务器响应101状态码,表示协议 "升级" 成功。 - 持久通信 :握手完成后,连接即切换至 WebSocket协议。此后,双方在 同一TCP连接上 进行轻量级的帧通信,头部开销极小,效率远高于频繁建立HTTP连接。
- 协作共存 :在现代应用中,一个页面通常会同时使用两种协议:用 HTTP 加载静态资源、调用REST API提交数据;用 WebSocket 维持一个实时通道,接收服务器推送的通知、聊天消息或实时数据更新。
因此,Tomcat 和 Undertow 作为现代 Servlet容器,都 同时支持HTTP和WebSocket协议,成为支撑这种混合通信模式的基础平台。
4. 总结
通过以上分析,我们可以清晰地看到一条技术演进的脉络:
- Servlet容器 作为标准化平台,是Java Web应用不可动摇的根基,++它让开发者从【底层网络细节】中解放出来++。
- Tomcat 与 Undertow 代表了这一根基下的两种不同发展路径:① Tomcat沿着"功能全面、稳定优先"的道路成为行业支柱;② Undertow则沿着"极致性能、轻量灵活"的道路,迎合了云原生时代对效率和资源密度的新需求。技术选型应基于实际业务场景,而非盲目追求性能指标。
- HTTP 与 WebSocket 的共存,体现了Web协议为解决不同问题而进行的自然扩展与协作。从单向请求到双向实时通信,协议的发展持续赋能更丰富的Web体验。
5. Tomcat与Spring Boot
5.1 Tomcat与Spring Boot的分工🏗️
我们可以把整个 Java Web应用 看成一栋提供服务的 "大楼":
- Tomcat :是这栋楼的 前台、门卫和后勤调度中心。它负责与外界(网络)打交道,处理所有接入、派发和基础的维护工作。
- Spring Boot (Spring MVC) :是楼里各个专业的 业务部门(如销售部、客服部)。它们专注于处理具体的业务逻辑。
当一个HTTP请求到来时,流程是这样的:
- 网络请求 → Tomcat(监听端口、解析协议、分配线程) → Spring MVC的
DispatcherServlet(中央路由器) →@Controller(具体处理者) → 原路返回响应。
org.springframework.web.servlet.DispatcherServlet

5.2 Tomcat的核心作用:连接器、线程池与容器🧵
Tomcat在这里面主要干了三件至关重要的事:
-
网络连接与协议解析(连接器 - Connector)
- 作用 :它在一个端口(如默认的8080)上 "竖起耳朵" 监听。当浏览器发来一个原始的、基于TCP/IP的HTTP请求数据流时,Tomcat 负责读取这个数据流,并按照 HTTP协议规范,将其解析成 Java代码可以方便操作的
HttpServletRequest和HttpServletResponse对象。这是所有 Web交互的基础。
- 作用 :它在一个端口(如默认的8080)上 "竖起耳朵" 监听。当浏览器发来一个原始的、基于TCP/IP的HTTP请求数据流时,Tomcat 负责读取这个数据流,并按照 HTTP协议规范,将其解析成 Java代码可以方便操作的
-
请求调度与线程管理(线程池 - Thread Pool)
- Tomcat 内部维护了一个或多个 线程池 (通常是
java.util.concurrent.ThreadPoolExecutor的变体)。 - Tomcat 在启动时就创建好了一组线程(称为工作线程)。这些线程一直在等待任务。
- 当一个新的HTTP请求被连接器接收后,Tomcat会从它的 ++线程池中取出一个空闲的 工作线程++ ,将这个请求(及上面提到的
Request/Response对象)【分配】给它 。然后,这个 Tomcat 的工作线程 【全程负责】 调用后续的 Spring MVC流程(包括Controller方法),直到返回响应。处理完毕后,该线程被释放回线程池,等待下一个请求 。所以,++处理请求的线程是 Tomcat 的,不是 Spring 或 Controller 创建的++。
- Tomcat 内部维护了一个或多个 线程池 (通常是
-
生命周期与组件管理(容器 - Container)
- Tomcat 管理着
Servlet(如Spring MVC的核心 【DispatcherServlet】)、Filter等组件的加载、初始化和销毁。在 Spring Boot 中,虽然我们通过内嵌方式启动,但 Tomcat 依然履行着这个职责。
- Tomcat 管理着
5.3 Tomcat的容量:线程、连接与队列📊
Tomcat处理请求的能力受几个关键配置参数控制,它们共同作用,形成了一个处理管道:

graph LR
subgraph A [Tomcat 请求处理管道]
direction LR
Client[客户端请求] --> Connector[连接器<br>Acceptor线程接收];
Connector --> B{有空闲工作线程?};
B -- 是 --> WorkerThread[工作线程处理<br>(执行Controller方法)];
B -- 否 --> Queue[等待队列<br>(容量: acceptCount)];
Queue --> C{队列未满?};
C -- 是 --> Wait[请求在队列等待];
C -- 否 --> Reject[立即拒绝连接];
Wait --> D[有空闲线程时出队处理];
WorkerThread --> Response[返回响应];
end
1. 处理线程数 (maxThreads)
- 含义 :Tomcat工作线程池的 最大线程数 。这直接决定了 应用同时能处理多少个请求。
- 默认值:通常是200(不同版本和配置有差异)。
- 影响 :如果应用所有Controller方法都很快,那么200个线程足以支撑很高的并发(因为线程复用)。但如果某个请求处理很慢(比如查询耗时2秒),那么在这2秒内,这个线程就被占用着。当200个线程全部被慢请求占用时,第201个请求就必须等待,即使CPU还很空闲。
2. 等待队列 (acceptCount)
- 含义 :当所有工作线程都在忙时,新来的请求不会立即被拒绝,而是进入一个 等待队列。这个参数就是队列的长度。
- 默认值:通常是100。
- 影响 :结合上面的例子,当200个线程都忙时,接下来的100个请求可以在队列中排队等待。队列也满后,第301个及以后的请求就会被 Tomcat 立即拒绝,返回连接失败的错误。
3. 最大连接数 (maxConnections)
- 含义 :Tomcat在任一时刻能够接收和管理的最大 网络连接数(包括正在处理的请求和保持活跃但空闲的连接)。
- 默认值 :对于NIO模式(默认),通常是
10000。 - 与线程数的区别 :一个连接(如Keep-Alive连接)可以被多个顺序发生的请求复用。
maxConnections限制的是TCP连接层面的数量,通常远大于maxThreads。
5.4 总结
Tomcat 与 Spring Boot 的核心分工如下:
| 组件 | 核心职责 | 类比 |
|---|---|---|
| Tomcat | HTTP服务器 + Servlet容器 : 1. 网络监听、协议解析。 2. 管理线程池,为每个请求分配工作线程 。 3. 管理请求队列,控制流量洪峰。 4. 加载和管理Servlet生命周期。 | 公司的前台、行政和IT基础部门:负责接电话、分配工位、提供电脑、保证办公室能运转。 |
| Spring Boot (Spring MVC) | Web框架 + 业务容器 : 1. 提供 DispatcherServlet(总路由器) 。 2. 将请求路由到对应的 @Controller/@RequestMapping 。 3. 处理参数绑定、数据验证、视图渲染等。 4. 管理业务Bean、事务、安全等。 |
公司的各个业务部门:如销售部(OrderController)、用户部(UserController),他们使用公司提供的基础设施来专注处理专业业务。 |
理解了这个分工,也就能明白为什么我们常说 "Spring Boot应用" 而不是 "Tomcat应用" ------ 因为Tomcat 已经变成了这个应用内部的一个 【标准化的、负责底层通信的组件】,而业务逻辑完全由Spring 框架掌控。
笑一个吧,加油啊!努力生活!!!
