Tomcat源碼解析 | 整體架構及組件
前言
Tomcat,昔日名為 Catalina,本是輕巧的 Servlet 容器。Catalina,美國加州海岸線上一顆璀璨的明珠?;蛟S,Tomcat 的締造者寄望于此,期冀將 Tomcat 塑造為一款既優雅又輕盈的 Web 服務器。自 4.x 版本起,Tomcat 不再局限于 Servlet 的支持,而是增添了諸多新功能,如 JSP、EL、命名服務等,從而超越了 Catalina 的范疇。
既然 Tomcat 的核心身份乃 Servlet 容器,我們便應更加關注 Servlet 本身。
何謂 Servlet?
追溯至互聯網的萌芽時期,Sun 公司(后被 Oracle 納入麾下)洞察到了這一時代機遇,便推出了 Applet 以支持 Web 應用。然而,Applet 并未如預期般在業界掀起波瀾。Sun 在反思之后,意識到機遇與市場前景皆不可辜負,于是決定投身于制定一套新的規范,Servlet 便應運而生。
Servlet,乃 Sun 公司為使 Java 語言能夠編織動態且富有交互性的網頁,進而邁入 Web 編程之殿堂,所精心制定的一套規范。一個 Servlet 的核心職責可概括為以下三端:
- 構建并充實 Request 對象,囊括 URI、參數、請求方式、頭部訊息、以及請求本體等。
- 創建 Response 對象。
- 運行業務邏輯,并將成果經由 Response 的輸出流,傳遞至客戶端。
Servlet 自身并不包含 main 方法,故其執行需依托于一容器之中,此容器之存在,正為支撐 Servlet 之功能。Tomcat,便是這樣一種 Servlet 容器的具象實現。
整體架構圖:
圖片
在 Tomcat 的架構圖之中,兩個核心組件——連接器(Connector)與容器(Container)扮演著心臟般的關鍵角色,它們的重要性不言而喻。以下是它們各自的職責:
- 連接器(Connector)負責處理與連接相關的事務,它將 Socket 連接轉化為 Request 和 Response 對象,以便進行后續處理。
- 容器(Container)則負責封裝與管理工作中的 Servlet,并具體承擔起處理 Request 請求的重任。
在 Tomcat 的體系結構中,僅存在一個 Server 實例,而一個 Server 可以容納多個 Service。每個 Service 僅關聯一個 Container,但可以配備多個 Connectors。這樣的設計意味著一個服務能夠通過多個連接器來接納連接,例如同時提供 HTTP 與 HTTPS 協議的鏈接,或者針對同一協議的不同端口提供服務。架構的示意圖如下(后續將詳細介紹 Engine、Host、Context 等組件):
圖片
image.png
在 Tomcat 的宏偉藍圖中,多個 Connector 與一個 Container 共同構成了 Service,正是 Service 使得 Tomcat 能夠向外提供服務。然而,Service 本身需要一個生存的依托,需要一個能夠賦予其生命、掌控其存亡的主宰,這個角色非 Server 莫屬。因此,Tomcat 的整個生命周期都受到 Server 的調控。
此外,這些組件之間的包含關系,或者說是層級關系,均可在 Tomcat 配置的心臟——conf目錄下的server.xml文件中一覽無余。在這個配置文件中,我們可以洞察到 Tomcat 的骨骼架構,理解各個組件如何相互連接、協作,共同支撐起整個服務器的運行。
圖片
上邊的配置文件,還可以通過下邊的一張結構圖更清楚的理解:
圖片
image.png
在 Tomcat 的架構中,各個組件各司其職,共同織就了一張精密的服務網絡。下面,讓我們逐一探究這些組件的獨特功能:
- Server:作為整個服務器的代表,Server 提供了一種簡潔而優雅的方式,用以啟動和停止整個 Tomcat 系統。它使得我們無需單獨對連接器和容器進行繁瑣的啟停操作。
- Service:Service 代表著服務本身,一個 Server 可以運行多個 Service。例如,在單個 Tomcat 實例中,可以同時運行訂單服務、支付服務和用戶服務等。
- Connector:Connector 是連接的橋梁,一個 Service 可以包含多個 Connector,以支持多種通信協議。例如,可以同時支持 AJP、HTTP 和 HTTPS 協議,每種協議都可以通過特定的 Connector 來實現。但是每種協議最終執行的 servlet 是相通的。
- Container:Container 是 Servlet 的棲息之地,它負責管理和執行 Servlet。
- Engine:作為引擎,Engine 是 Container 的最高層級,它可以包含多個 Host。
- Host:Host 代表虛擬主機,每個 Host 可以包含多個 Context。
- Context:Context 是 Web 應用的上下文,它定義了 Web 應用的部署參數和路徑。
- Wrapper:Wrapper 是 Servlet 的包裝器,它負責 Servlet 的生命周期管理和資源分配。
- Service 服務之下還有各種支撐組件。
Manager:Manager 是會話管理器,它負責管理用戶的會話(Session)。
Logger:Logger 是日志管理器,它負責記錄和管理日志信息。
Loader:Loader 是類加載器,它與類的加載機制相關,通常由 Context 使用。
Pipeline:Pipeline 是管道組件,它與 Valve 一起實現過濾器功能。
Valve:Valve 是閥門組件,它與 Pipeline 配合,用于實現請求和響應的過濾處理。
Realm:Realm 是安全域,它負責認證和授權。
在 Tomcat 的架構中,除了連接器和容器之外,Pipeline 和 Valve 也扮演著至關重要的角色。它們共同構成了 Tomcat 強大的過濾和處理能力。通過一張圖,我們可以更直觀地理解這兩個組件如何在請求處理流程中發揮作用。
圖片
Connector 和 Container 的微妙關系
在 Tomcat 的宏偉舞臺之上,當一個請求翩翩而至,它的旅程便開始了:
- Service:請求首先抵達 Service,這是 Tomcat 服務的起點,調度著整個服務的流程。
- Connector:隨后,請求被引導至 Connector,它是 Tomcat 的忠實門衛,負責接收來自遠方的請求。Connector 將原始的網絡連接轉化為符合 HTTP 協議的 Request 和 Response 對象。
- Container:Request 和 Response 在 Connector 的精心包裝后,便被遞交至 Container。它負責管理和執行 Servlet,對請求進行細致的處理。
- 處理與返回:Container 在處理完請求后,將結果交回 Connector。Connector 再次承擔起信使的角色,通過 Socket 將處理結果沿著 TCP/IP 協議的路徑,送回客戶端。
在整個過程中,Connector 不僅是溝通客戶端與服務器的橋梁,更是實現 TCP/IP 協議和 HTTP 協議的使者。它確保了請求和響應的準確傳遞,使得整個 Web 服務順利的流轉起來
Connector 架構分析
連接器(Connector)猶如一扇溝通外界與應用系統的窗口,負責接收來自客戶端的請求,將其轉化為標準化的 Request 和 Response 對象,并交給容器(Container)進行處理。Container 處理完后再交給 Connector 返回給客戶端。從功能上看,我們可以將連接器拆解為以下三個核心環節:
- 請求的捕獲: 連接器如何精準地捕捉到來自客戶端的海量請求?
- 請求與響應的封裝: 連接器是如何將紛繁復雜的原始請求數據,規范地封裝成 Request 對象,并將容器處理后的結果打包成 Response 對象的?
- 請求的傳遞與響應的回傳: 封裝后的 Request 對象如何被高效地傳遞給容器,而容器生成的 Response 對象又如何準確地返回給客戶端?
我們看下 Connector 的結構圖,如下:
圖片
Connector 的核心組件與工作原理
Connector 通過 ProtocolHandler 來處理各種類型的網絡請求。不同的 ProtocolHandler 對應不同的連接方式,如Http11Protocol使用傳統的阻塞式 Socket,而 Http11NioProtocol 則采用高效的非阻塞式 NIO Socket。ProtocolHandler 主要由 Endpoint、Processor 和 Adapter 三個組件構成:
- Endpoint: 負責底層網絡連接的管理,包括 Socket 的創建、監聽、接受以及連接的關閉。它相當于一個“門衛”,負責把控所有進出系統的網絡流量。Endpoint 通常實現了 TCP/IP 協議棧的部分功能,用于建立可靠的網絡連接。
- Processor: 專門負責將 Endpoint 接收到的原始 Socket 數據解析為標準化的 HTTP 請求(Request)。它可以看作是一個“翻譯官”,將底層的網絡字節流轉化為應用程序可以理解的請求信息。Processor 實現了 HTTP 協議的具體細節,包括請求行的解析、請求頭的處理、請求體的讀取等。
- Adapter: 充當了連接器與容器之間的適配器。它將 Processor 處理好的 Request 對象傳遞給 Container,以便容器中的 Servlet 或其他組件對請求進行具體的處理。Adapter 的作用類似于一個“中介”,將不同層次的組件聯系起來。
Endpoint 內部機制Endpoint 的抽象實現 AbstractEndpoint 定義了幾個重要的內部類和接口:
- Acceptor: 負責監聽來自客戶端的連接請求,一旦有新的連接到來,Acceptor 就會創建一個新的 Socket,并將其交給 Handler 處理。
- AsyncTimeout: 用來監控異步請求的超時情況。對于長時間未得到響應的異步請求,AsyncTimeout 會采取相應的處理措施,比如關閉連接或者觸發超時事件。
- Handler: 是一個接口,定義了處理新連接的具體邏輯。當 Acceptor 接收到一個新的 Socket 時,會創建一個 Handler 實例,并將其與該 Socket 關聯起來。Handler 會調用 Processor 來解析請求,并將處理結果返回給客戶端。
小結:
Connector 通過這三個組件的協同工作,實現了從接收客戶端請求到返回處理結果的整個過程。Endpoint 負責建立連接,Processor 負責解析請求,Adapter 負責將請求傳遞給容器。這種分層設計使得 Connector 具有良好的擴展性和可維護性。
Container 如何處理請求的
容器(Container)采用 Pipeline-Valve 管道機制來處理請求,這是一種基于責任鏈模式的設計。在請求處理過程中,多個 Valve 會依次對請求進行處理,每個 Valve 負責特定的任務。與傳統的責任鏈模式不同,Pipeline-Valve 具有以下特點:
- 固定終點: 每個 Pipeline 都有一個特殊的 Valve,即 BaseValve。BaseValve 位于 Pipeline 的末端,不可刪除,確保每個請求都經過它的處理。
- 層級調用: 上層容器的 BaseValve 會調用下層容器的 Pipeline,形成一個嵌套的責任鏈,使得請求在不同層次的容器中依次得到處理。
Tomcat 中的四個標準容器(Engine、Host、Context、Wrapper)分別對應一個 BaseValve:StandardEngineValve、StandardHostValve、StandardContextValve、StandardWrapperValve。
Pipeline 的處理流程圖如下:
圖片
當 Connector 接收到一個請求時,它會將請求委派給最頂層的容器——Engine。Engine 擁有一個 Pipeline,就像一條流水線,流水線上的每個工位就是一個 Valve。這些 Valve 按照預定的順序對請求進行處理。
- Engine 管道: 請求首先進入 Engine 的 Pipeline。在這個管道中,一系列 EngineValve 會依次對請求進行處理,例如進行全局性的日志記錄、安全檢查等。最后,StandardEngineValve 會將請求轉發給下一層容器——Host。
- 逐級傳遞: Host、Context、Wrapper 等容器都擁有自己的 Pipeline,它們會按照同樣的方式處理請求。每個容器的 BaseValve(如 StandardHostValve、StandardContextValve)負責將請求傳遞給下一層容器。
- FilterChain: 當請求到達 Wrapper 容器時,StandardWrapperValve 會創建一個 FilterChain。FilterChain 包含了與該請求匹配的所有 Filter 和 Servlet。這些 Filter 和 Servlet 會按照配置的順序依次執行 doFilter 方法,最終調用 Servlet 的 service 方法來處理請求。
- 返回響應: 一旦請求處理完畢,響應結果會沿著原路返回,經過各個 Valve,最終由 Connector 發送回客戶端。
好了,我們已經從整體上看到了 Tomcat 的結構,對于每個組件并沒有詳細分析。后續章節我們會從幾個方面來學習 Tomcat