理解 Tomcat 如何將 HTTP 請求路由到具體 Servlet,對排查 404、多 Host 配置、反向代理轉發至關重要。本文基於 Tomcat 8.0.36 源碼邏輯,梳理完整匹配鏈路,並揭示該版本兩個關鍵 Bug 及規避方案。
HTTP Request
│
▼
Connector (端口、協議、線程池)
│
▼
ProtocolHandler (Http11NioProtocol 等)
│
▼
Adapter (CoyoteAdapter) --> 將 Request 轉為 ServletRequest
│
▼
Mapper (MapperListener) 核心路由邏輯
│
├── 1. 選擇 Service (通常唯一)
├── 2. 選擇 Engine (通常唯一)
├── 3. 【關鍵】匹配 Host (域名/虛擬主機)
│ └─ server.xml 中 /
├── 4. 匹配 Context (Web 應用路徑 /app1, /app2)
│ └─ docBase、path、web.xml、Servlet 映射
└── 5. 匹配 Wrapper (Servlet 定義)
└─ url-pattern: /api/*, *.do, /exact/path
Wrapper --> Filter Chain --> Servlet.service()
│
▼
Response 返回
| 組件 | 職責 | 配置位置 |
|---|---|---|
| Connector | 監聽端口、解析 HTTP 協議、管理 I/O 線程 | server.xml |
| Mapper | **核心路由器**,按 Host → Context → Wrapper 層級匹配 | 內部組件,由 MapperListener 維護映射表 |
| Host | 虛擬主機容器,綁定域名,持有多個 Context | server.xml |
| Context | 單個 Web 應用,管理 Servlet、Filter、Listener、資源 | server.xml |
| Wrapper | 單個 Servlet 定義,持有 url-pattern 集合 | web.xml |
Request.getCookies() 返回 null 或拋 IllegalArgumentException,導致 Session 失效、登錄態異常。Valve 或 Filter 捕獲異常並降級處理allowTrace="true" 才允許 TRACE 請求,默認 false 返回 405。此非 Bug 但常被誤以為故障。Tomcat **不支持泛域名匹配**(如
*.example.com),Host.name必須是確切域名或localhost。
| 方案 | 適用場景 | 實現方式 |
|---|---|---|
| 默認 Host 兜底 | 域名少、固定、無需隔離 | ,所有未匹配域名落入默認 Host,應用內再按域名分流 |
| Nginx 域名路由 + 統一 Host | 域名多、需 SSL 卸載、靜態資源分離 | Nginx server_name 匹配 → proxy_pass 統一上游 Tomcat Host |
| 動態 Host 註冊 (代碼侵入) | SaaS 多租戶、運行時新增域名 | 實現 LifecycleListener,運行時調用 engine.addChild(new StandardHost()) |
| 修改 Tomcat 源碼 | 極少見、強定制需求 | 修改 Mapper.map() 邏輯支持通配符,維護成本極高 |
server.xml 添加 <Valve className="org.apache.catalina.valves.AccessLogValve" pattern="%h %t %r %s %b %{Host}i %{X-Forwarded-For}i" />Host Header 與實際路由的 Context PathCatalina:type=Mapper,host=*,context=* 觀察映射表實時變化CoyoteAdapter.postParseRequest() → Mapper.map()Tomcat 請求匹配遵循 **Connector → Mapper (Host → Context → Wrapper) → Servlet** 鏈路。8.0.36 存在**嚴重 Cookie 解析 Bug**,生產環境**必須升級至 8.0.37+**。泛域名需求請在 **Nginx 層解決**,勿強行改造 Tomcat。掌握 Mapper 邏輯與 Access Log 分析,能快速定位 99% 的 404、Session 丟失、多域名路由錯誤問題。