第一章:Tomcat架構概述
1.1 Tomcat的角色與定位:Web服務器 vs Servlet容器
Tomcat 是什么?它既是一種輕量級 Web 服務器,也是一種符合 Java EE 規范的 Servlet 容器。
Web服務器:類似 Nginx、Apache HTTP Server,處理靜態資源請求(如 HTML、CSS、JS)。
Servlet容器:它能解析 Java Web 應用,執行 Servlet 邏輯,是 J2EE 架構中的核心組件。
Tomcat 專注于 Servlet/JSP 執行環境,是大多數 Java Web 項目的默認運行平臺。你可以理解為:
🧠 “Nginx 負責搬運磚(靜態內容),而 Tomcat 負責燒菜(動態內容)。”
1.2 核心功能:網絡連接器(Connector)與Servlet容器(Container)
Tomcat 架構的設計核心是 分離連接(Connector)與處理(Container)。
Connector 負責“接收請求”:它監聽端口、解析協議(如 HTTP/AJP),把原始 Socket 請求轉換成 Java 對象(如
ServletRequest
)。Container 負責“處理請求”:它解析 URL、找到對應的 Servlet、執行業務邏輯并返回響應。
兩者通過 Service 組件進行綁定,形成完整的請求處理路徑。
1.3 架構圖描述(文字形式)
用文字描述 Tomcat 的核心架構圖,幫助建立層級結構的直觀印象:
┌────────────────────┐
│ Server │ ← Tomcat 最頂層組件,負責整體生命周期
└────────┬───────────┘│┌─────▼─────┐│ Service │ ← 每個 Server 可包含多個 Service└─────┬─────┘│┌───────▼────────┐│ Connector │ ← 監聽端口,接收并轉換 HTTP/AJP 請求└────────┬───────┘│┌───────▼────────────┐│ Engine │ ← 請求處理的核心入口,屬于 Container└──────┬─────────────┘│┌─────▼─────┐│ Host │ ← 虛擬主機,用于支持多域名部署└─────┬─────┘│┌────▼─────┐│ Context │ ← 每個 Web 應用一個 Context(對應一個 WAR 包)└────┬─────┘│┌───▼────┐│Wrapper │ ← 每個 Servlet 一個 Wrapper,最終執行點└────────┘
從上圖可見,請求從最底層的 Connector
發起,最終由 Wrapper
調用 Servlet 實現類處理業務邏輯。這就是 Tomcat 的核心處理鏈路。
第二章:核心組件詳解
2.1 Server組件:管理Tomcat實例的生命周期
Server 是 Tomcat 的頂級組件,代表整個 Tomcat 實例,它的職責是控制整個服務的生命周期。
代表類:
org.apache.catalina.core.StandardServer
主要職責:
統一管理所有
Service
。監聽
SHUTDOWN
命令端口(默認8005),優雅關閉。觸發
init()
,start()
,stop()
生命周期方法。
💡 類比理解:
Server 就像是一個酒店的總經理,下面每個 Service 是一個功能部門,比如前臺、后廚、客房管理。
🔍 架構圖描述(Server層):
┌────────────────────┐
│ Server │
│ 監聽8005關閉端口 │
│ 管理多個Service │
└────────┬───────────┘↓[多個Service]
2.2 Service組件:整合Connector與Engine
Service 是連接請求(Connector)和業務處理(Engine)的橋梁。
代表類:
org.apache.catalina.core.StandardService
主要職責:
一個 Service 包含:
一個 Engine(處理業務邏輯)
一個或多個 Connector(接收外部請求)
多個 Connector 可以綁定同一個 Engine,實現多協議共享邏輯處理。
💡 類比理解:
Service 就像酒店里的“接待部門”:門童(Connector)負責迎客,帶到接待柜臺(Engine)處理入住流程。
🔍 架構圖描述(Service層):
┌──────────────────────────┐
│ Service │
│ ┌─────────────────────┐ │
│ │ Engine │ │ ← 業務處理核心
│ └─────────────────────┘ │
│ ┌────────────┐ ┌────────────┐
│ │ Connector1 │ │ Connector2 │ ← 多個端口或協議接入
│ └────────────┘ └────────────┘
└──────────────────────────┘
2.3 Connector組件:協議解析與請求轉發
Connector 負責與客戶端打交道,是 Tomcat 與外部世界的接口。
代表類:
org.apache.coyote.http11.Http11NioProtocol
職責:
監聽指定端口(如 8080)。
解析 HTTP 或 AJP 協議,轉換為 Request/Response 對象。
將請求傳入對應的 Engine 繼續處理。
🌐 支持的協議實現:
模式 | 類名 | 特點 |
---|---|---|
BIO | Http11Protocol | 同步阻塞,低性能 |
NIO | Http11NioProtocol | 異步非阻塞,推薦 |
APR | Http11AprProtocol / AjpAprProtocol | 高性能,依賴本地庫 |
NIO2 | Http11Nio2Protocol | NIO 的改進版 |
🧪 示例:配置 NIO Connector(在server.xml
中)
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"connectionTimeout="20000"maxThreads="200" />
2.4 Container組件:Servlet容器的分層結構
Container 是 Tomcat 的核心處理器,負責執行 Servlet 邏輯。
它包含 4 層結構,層層包裹,類似俄羅斯套娃:
層級 | 代表類 | 作用 |
---|---|---|
Engine | StandardEngine | Service 中唯一的業務處理容器 |
Host | StandardHost | 虛擬主機,支持多域名部署 |
Context | StandardContext | 一個 Web 應用對應一個 Context |
Wrapper | StandardWrapper | 每個 Servlet 一個 Wrapper |
💡 類比理解:
Container 像一棟辦公樓:
Engine 是大樓
Host 是樓層(不同租戶)
Context 是部門
Wrapper 是員工(Servlet)
🔍 架構圖描述(Container層):
Engine└── Host (域名)└── Context (Web應用)└── Wrapper (Servlet)
? 本章小結
Server 管理整個 Tomcat 實例生命周期。
Service 是連接外部請求(Connector)與內部業務(Engine)的橋梁。
Connector 接收客戶端請求并解析協議。
Container 是核心執行單元,包含 Engine → Host → Context → Wrapper 四級結構。
第三章:請求處理流程
3.1 請求到達 Connector 的流程(Socket → ServletRequest)
瀏覽器發送 HTTP 請求
例如訪問http://localhost:8080/demo/hello
,TCP 三次握手后,請求數據會到達 Tomcat 監聽的端口(默認 8080)。Connector 接收請求
對應類:
org.apache.coyote.http11.Http11NioProtocol
監聽線程(Acceptor)接收連接請求,并交給 Poller 線程注冊到 Selector(NIO 模型)。
協議解析
使用
Http11Processor
解析 HTTP 協議。將解析結果封裝成
org.apache.coyote.Request
和org.apache.coyote.Response
對象。
適配成 Servlet API
CoyoteAdapter
將底層 Request/Response 轉換為HttpServletRequest
和HttpServletResponse
,進入容器處理流程。
🔍 流程圖(文字版):
瀏覽器 → TCP連接 → Connector監聽端口↓Acceptor線程接收連接↓Poller/Processor解析HTTP↓封裝為Request/Response↓CoyoteAdapter適配到Servlet API
3.2 Mapper組件的URL映射機制
Mapper 的作用是根據 URL 找到正確的 Servlet。
匹配規則(從粗到細):
匹配 Host(域名)
匹配 Context(Web 應用路徑)
匹配 Wrapper(Servlet 映射規則)
例如訪問 http://localhost:8080/demo/hello
:
Host:
localhost
Context:
/demo
Wrapper:匹配到
/hello
的 Servlet
關鍵類:
org.apache.catalina.mapper.Mapper
org.apache.catalina.core.StandardHost
org.apache.catalina.core.StandardContext
3.3 Pipeline-Valve機制:請求過濾與處理鏈
Tomcat 的容器(Engine、Host、Context、Wrapper)都有一個 Pipeline(管道),里面裝著多個 Valve(閥門)。
Pipeline:請求處理的有序鏈路。
Valve:具體的處理步驟,例如日志記錄、安全檢查、壓縮等。
基本原則:請求會沿著 Valve 鏈從上到下傳遞,最終交給 Servlet 處理。
示例:默認Valve鏈
EnginePipeline→ HostPipeline→ ContextPipeline→ WrapperPipeline→ StandardWrapperValve(最終調用Servlet.service())
可自定義Valve示例:
public class MyLogValve extends ValveBase {@Overridepublic void invoke(Request request, Response response) throws IOException, ServletException {System.out.println("請求URI: " + request.getRequestURI());getNext().invoke(request, response); // 繼續下一個Valve}
}
在 server.xml
中注冊即可生效。
3.4 Servlet的加載與執行(Wrapper → Servlet實例)
Wrapper找到Servlet
Mapper 找到的目標是某個Wrapper
,Wrapper 中保存了 Servlet 的配置信息。Servlet加載
如果 Servlet 未被加載,
StandardWrapper
會調用loadServlet()
創建并初始化 Servlet 實例(調用init()
方法)。
執行Servlet
最終由
StandardWrapperValve
調用Servlet.service()
,根據請求方法分發到doGet()
、doPost()
等方法。
返回響應
Servlet 處理完成后,將數據寫入
HttpServletResponse
,由 Connector 發送回客戶端。
? 本章小結
Connector:接收 Socket 連接并解析協議。
CoyoteAdapter:適配成 Servlet API。
Mapper:根據 URL 定位到具體 Servlet。
Pipeline-Valve:處理鏈路,可擴展。
Wrapper:管理 Servlet 的生命周期并調用其方法。
第四章:性能優化與調優
4.1 I/O模型選擇與性能對比
Tomcat 支持多種 I/O 模型,選擇合適的模型是性能優化的第一步。
I/O 模型 | 協議類名 | 特點 | 適用場景 |
---|---|---|---|
BIO(阻塞I/O) | Http11Protocol | 簡單穩定,但每個請求一個線程,連接多時性能差 | 老系統、小并發 |
NIO(非阻塞I/O) | Http11NioProtocol | 單線程管理多個連接,性能好,JDK自帶 | 推薦默認 |
NIO2(異步I/O) | Http11Nio2Protocol | JDK7+,AIO模型,適合高并發 | 高吞吐場景 |
APR(本地庫) | Http11AprProtocol | 使用Apache Portable Runtime,接近C語言性能 | 需要原生庫,追求極限性能 |
切換示例(server.xml
):
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"maxThreads="500" connectionTimeout="20000"/>
4.2 線程池配置調優策略
Tomcat 的 Connector
內部有線程池,決定了同時能處理多少請求。
核心參數:
maxThreads
:最大工作線程數(默認200)minSpareThreads
:啟動時的最小空閑線程數(默認10)acceptCount
:隊列長度,滿了會拒絕請求(默認100)connectionTimeout
:連接超時時間(毫秒)
調優思路:
根據 CPU 核數和業務特性,計算合適的線程數(CPU 密集型:2×核數;I/O 密集型:更高)。
壓測觀察線程池是否飽和,必要時增加
acceptCount
避免拒絕連接。調短
connectionTimeout
以減少無效連接占用。
配置示例:
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"maxThreads="800"minSpareThreads="50"acceptCount="300"connectionTimeout="15000"/>
4.3 內存泄漏問題與解決方案
Tomcat 在長時間運行中,可能因類加載器或未關閉的資源造成 PermGen/Metaspace 泄漏。
常見原因:
Web 應用熱部署后,老的
ClassLoader
未釋放。JDBC 連接、線程池、定時任務未關閉。
靜態集合引用持有大對象。
解決策略:
禁用頻繁熱部署,生產中使用全量重啟。
在
ServletContextListener.contextDestroyed()
中手動關閉資源。啟用
org.apache.catalina.loader.WebappClassLoaderBase
的內存泄漏檢測日志:<Context reloadable="false"><Loader leakDetection="true"/> </Context>
4.4 高并發場景下的配置優化案例
假設業務是一個 高并發API服務,每天有數百萬請求,可以做如下優化:
啟用NIO模型,提升多連接處理能力。
加大線程池:
maxThreads="1000" minSpareThreads="100" acceptCount="500"
壓縮響應(減少網絡傳輸量):
compression="on" compressionMinSize="1024" compressableMimeType="text/html,text/xml,text/plain,application/json"
Keep-Alive優化:
maxKeepAliveRequests="100" keepAliveTimeout="5000"
反向代理配合(Nginx + Tomcat):
Nginx 負責 SSL 終端和靜態資源。
Tomcat 專注處理動態請求,減少負載。
第五章:實戰案例與代碼示例
5.1 自定義 Valve 實現請求日志記錄
場景:我們希望記錄每個 HTTP 請求的 URI 和處理耗時,這可以幫助排查性能問題。
代碼實現
在 server.xml
中注冊:
package com.example.tomcat;import org.apache.catalina.Request;
import org.apache.catalina.Response;
import org.apache.catalina.Valve;
import org.apache.catalina.valves.ValveBase;import javax.servlet.ServletException;
import java.io.IOException;public class MyLogValve extends ValveBase {@Overridepublic void invoke(Request request, Response response) throws IOException, ServletException {long start = System.currentTimeMillis();String uri = request.getRequestURI();System.out.println("[MyLogValve] 請求URI: " + uri);// 調用下一個Valve或最終的ServletgetNext().invoke(request, response);long duration = System.currentTimeMillis() - start;System.out.println("[MyLogValve] 請求耗時: " + duration + "ms");}
}
這樣,所有到 localhost
的請求都會被我們的日志 Valve 攔截并記錄。
5.2 server.xml
配置優化示例
假設我們要優化一個高并發 API 服務的 Tomcat:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"><Valve className="com.example.tomcat.MyLogValve"/>
</Host>
優化要點:
NIO 模型:提升連接并發能力。
線程池加大:應對高并發。
響應壓縮:減少網絡帶寬消耗。
Keep-Alive 優化:避免連接長時間占用。
5.3 Servlet 生命周期代碼演示
場景:展示 Servlet 的 init()
、service()
、destroy()
調用時機。
示例代碼:
package com.example.servlet;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class LifeCycleServlet extends HttpServlet {@Overridepublic void init() throws ServletException {System.out.println("[Servlet] init() - 初始化Servlet");}@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {System.out.println("[Servlet] service() - 處理請求: " + req.getMethod());resp.getWriter().write("Hello, this is LifeCycleServlet");}@Overridepublic void destroy() {System.out.println("[Servlet] destroy() - 銷毀Servlet");}
}
web.xml
配置:
<servlet><servlet-name>lifeCycleServlet</servlet-name><servlet-class>com.example.servlet.LifeCycleServlet</servlet-class><load-on-startup>1</load-on-startup>
</servlet><servlet-mapping><servlet-name>lifeCycleServlet</servlet-name><url-pattern>/lifecycle</url-pattern>
</servlet-mapping>
運行結果:
第一次訪問
/lifecycle
時觸發init()
。每次請求調用
service()
。Tomcat 關閉時調用
destroy()
。