模擬實現 Tomcat 的核心模塊:NIO,HTTP,容器和集羣

如果你想看 Tomcat 源碼但又無從入手,不妨從這個項目開始,代碼量不多,但包含了 Tomcat 的核心處理流程,並且源碼中有相當豐富的註釋。相信通過此項目你能瞭解:

  • NIO 基本編程、HTTP 協議的本質、基本的單元測試
  • Tomcat 應用部署、自定義類加載器的實現、Servlet 的管理和加載運行以及靜態資源的處理和緩存等
  • Maven 生成可執行 jar,生成 javadoc,使用 assembly 構建項目,使用 release 插件發佈到 git等

文末有源碼地址。本文就 NIO 模型、HTTP 協議解析、Digester 工具以及Servlet 容器這些核心模塊的設計和實現的難點以及重點進行簡單介紹。

1. NIO 服務器的實現

由於非阻塞的特性,NIO 的編寫相比於 BIO 很複雜,而原生 NIO 編程就更復雜了,關鍵是處理好通道和處理器的映射,以及各種狀態的管理。此模塊的名稱是 rxtomcat-net,結構如下:

rxtomcat-net

主要實現了以下功能:

  • Acceptor 使用信號量對總連接數進行控制
  • Acceptor 和 Poller 使用隊列協作完成新連接通道的註冊,因爲直接註冊可能會造成死鎖
  • Poller 既能通知非阻塞讀寫事件,也能通知模擬阻塞的讀寫事件
  • Poller 在通知 I/O 事件時,也會模擬 Tomcat 把通道就緒的事件從關注事件集合中移除
  • 利用兩個用於讀和寫的 CountDownLatch 實現模擬阻塞
  • Poller 還對通道處理超時,通道超時或關閉時會移除它對應的 Processor,防止內存泄露
  • 因爲是非阻塞,所以當處理中途發現讀或寫的數據不完整,要再次處理時,需要找到原先的處理器,Handler 內部就是使用 ConcurrentHashMap 保持通道和處理器的映射
  • NioChannel 是對 SocketChannel 的封裝,主要包含兩個 ByteBuffer 用於讀和寫以及兩個模擬阻塞讀和寫使用的閉鎖,以及提供實際的非阻塞和模擬阻塞讀寫功能
  • 爲了適配不同協議的處理器定義了一個 Processor 接口

EchoProcessor 是實現的一個回顯處理器,它包含一個 main 方法,可直接運行進行測試。

有一點需要注意,從通道讀取字節到處理請求都是一個線程,只有在非阻塞讀取不完整的請求頭數據時,纔有可能切換線程

2. HTTP 協議的解碼和編碼

完整的實現 HTTP 協議是很複雜的,這裏的實現比較簡單,模塊的名稱是 rxtomcat-http,結構如下:

rxtomcat-http

主要實現了以下功能:

  • 消息行(請求或響應)的解析和構造,解析時採用有限狀態機的方法,這是非阻塞編程常用的手段
  • chunked 和 identity 消息體的解析和構造
  • 實現 keepAlive 長連接
  • 特殊 URL 解碼,比如 param=%E5%88%9B+a
  • 爲容器提供底層 Processor 的回調機制,ActionHook

解析協議麻煩的地方在於處理 TCP 粘包拆包的問題,以及各種緩衝區的清空和重用。在實現時,緩存區大部分使用的是 ByteBuffer。

3. 簡單的 Servlet 容器

實現一個簡單的 Servlet 容器,模塊的名稱是 rxtomcat-container,結構如下:

rxtomcat-container

簡單起見,只設計了 Context 和 Wrapper 兩個容器,主要實現了以下功能:

  • Pipeline 和 Valve 的管道處理模型,以及容器 Lifecycle 生命週期的設計
  • DefaultServlet 靜態資源的處理和緩存
  • 根據 web.xml 部署應用,提取 Servlet 和 Filter 及其配置的映射
  • 打破雙親委託的類加載器 Loader,實現從 WEB-INF/classes 和 WEB-INF/lib 加載類,以及 class 文件熱加載的功能
  • 實現 Servlet 的三種 URL 路由規則,以及規範中的 Cookie, HttpSession, FilterChain, HttpServletRequest, HttpServletResponse
  • 實現 Session 以及它的管理器 Manager
  • 實現了 ServletInputStream 用於支持文件上傳的處理

這部分的實現稍微繁瑣,也基本復現了 Tomcat 的處理流程,其中唯一有點繞的就是使用 Lifecycle 實現的觀察模式,觸發特定的生命週期事件,使用特定的類來配置和初始化 Context。

4. 其他工具

模塊 rxtomcat-utils 主要是一些工具類:

  • 簡單實現了 Digester XML 解析工具
  • 實現了一個字節數組功能類,主要有字節數組轉整形,轉十六進制字符串

5. Maven 構建模塊

模塊 rxtomcat-bootstrap 使用 maven-assembly-plugin 打包發佈二進制版本,最終構建生成的項目運行目錄結構是:

目錄結構

6. 小結

造輪子確實很費時間,但效果很好。平時寫代碼,知道原理是什麼,但在編寫時卻無從下手,這就是代碼寫的少,模仿的少導致的。所以,如果時間充裕,不妨多造造輪子。

  • 本文模擬實現的 Tomcat 源碼地址是:「github.com/tonwu/rxtomcat」
  • 使用的版本是 Tomcat 6.0.53,公衆號「頓悟源碼」後臺回覆關鍵字「Tomcat」可獲取帶有比較詳細中文代碼註釋的,可直接導入 Eclipse 運行的 Tomcat 工程。

讀完一個完整的開源項目,實在太費時間了v_v,後續時間充足的話,計劃繼續實現集羣、異步 Servlet 和 websocket 的代碼,歡迎 star 關注

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章