深入分析 Tomcat 原理
早期Web容器早期的 Web 應(yīng)用主要用于瀏覽新聞等靜態(tài)頁(yè)面,HTTP 服務(wù)器(比如 Apache、Nginx)向?yàn)g覽器返回靜態(tài) HTML,瀏覽器負(fù)責(zé)解析 HTML,將結(jié)果呈現(xiàn)給用戶。
Servlet規(guī)范
隨著互聯(lián)網(wǎng)發(fā)展,往往更多的是需要?jiǎng)討B(tài)的交互。所以 Sun 公司推出了 Servlet 技術(shù):servlet 規(guī)范!目前最新是 Servlet 4.0 ,它支持 HTTP2.0!
符合 Servlet 規(guī)范的 Web 流程:由 Servlet 容器來(lái)創(chuàng)建和管理 Servlet,客戶端的請(qǐng)求 會(huì)被封裝成 ServletRequest 和 ServletResponse,其本質(zhì)上就是對(duì)通信協(xié)議的封裝。
Tomcat簡(jiǎn)介
Tomcat 就是一個(gè) Servlet 容器,實(shí)現(xiàn)了對(duì) Servlet 和 JavaServer Page(JSP)的支持。同時(shí),它還具有 HTTP 服務(wù)器的功能。所以,「Tomcat = HTTP服務(wù)器 + Servlet容器」,一般我們給這種組件稱為:「輕量級(jí)web容器」!
Tomcat架構(gòu)
Tomcat-Server
- 「Server」:一個(gè) Server 就是一個(gè) Tomcat 實(shí)例,下載 Tomcat 壓縮包,執(zhí)行 /bin/startup.sh,就可以啟動(dòng)一個(gè) Tomcat 實(shí)例。
- 「Service」:一個(gè)對(duì)外服務(wù)的整體,它包括多個(gè) Connector 和一個(gè) Engine。同時(shí),一個(gè) Server 可以配置多個(gè) Service。實(shí)際上 Service 只是將組件組合到一起,本身并沒(méi)有實(shí)現(xiàn)什么功能。
Tomcat-連接器
連接器,啟動(dòng) ServerSocket,負(fù)責(zé)監(jiān)聽(tīng) Socket 請(qǐng)求,將數(shù)據(jù)轉(zhuǎn)換成 Tomcat Request,交給 Engine 處理。一個(gè) Service 可以有多個(gè) Connector,表示它可以監(jiān)聽(tīng)多個(gè)端口。
Tomcat-容器
即 Servlet 容器,它是 Tomcat 容器的最頂層組件,它會(huì)管理多個(gè)虛擬主機(jī) Host,一個(gè) Service 只能有一個(gè) Engine,但是一個(gè) Engine 可以配置多個(gè) Host。Tomcat 的 Servlet 容器是具有明顯的分層架構(gòu)的。
- 「Host」:虛擬主機(jī),默認(rèn)為 localhost,也就是 127.0.0.1。也可以配置不同的 IP 地址,訪問(wèn)不同的 IP 地址就可以訪問(wèn)到不同的虛擬主機(jī)。一個(gè) Host 可以部署多個(gè) Context。
- 「Context」:應(yīng)用程序,一般會(huì)把我們實(shí)現(xiàn)的 Servlet 應(yīng)用打包成 war,放到 Tomcat 的 webapps 目錄下,Tomcat 會(huì)將其解壓并部署映射成一個(gè) Context 組件,表示一個(gè)應(yīng)用上下文。一個(gè) Context 可以管理多個(gè) Wrapper,畢竟一個(gè) web 應(yīng)用肯定有多個(gè) Servlet。
- 「Wrapper」:這個(gè)組件 Tomcat 配置文件并沒(méi)有,因?yàn)樗窃?web.xml 配置,它就是 Servlet。確切地說(shuō),是 Tomcat 用 Wrapper 包裹了我們自己實(shí)現(xiàn)的 Servlet。一個(gè)請(qǐng)求最終就會(huì)到 Wrapper 來(lái)執(zhí)行。
Tomcat生命周期管理
「Tomcat 設(shè)計(jì)眾多組件來(lái)保證高內(nèi)聚低耦合,保證可擴(kuò)展性」。但是,組件數(shù)量多也會(huì)帶來(lái)其它問(wèn)題,比如組件的管理,在啟動(dòng)、關(guān)閉和銷毀需要涉及多個(gè)組件的操作。Tomcat 設(shè)計(jì)了 LifeCycle 接口,它定義生命周期鉤子函數(shù):init()、start()、stop() 和 destroy(),組件都實(shí)現(xiàn)這個(gè)接口,定義自己的處理邏輯。并且,上層組件在觸發(fā)自己生命周期鉤子函數(shù)的同時(shí),會(huì)觸發(fā)它管理的下層組件的鉤子函數(shù)。其實(shí)國(guó)外設(shè)計(jì)框架很喜歡設(shè)計(jì)這個(gè) LifeCycle 接口,新版 Apache Dubbo 也加入了這一特性。
Tomcat 在實(shí)現(xiàn)組件生命周期管理,充分利用了「組合模式、觀察者模式和模板模式」。
- 「組合模式」:Tomcat 通過(guò)組合模式,用上層組件來(lái)管理它下一級(jí)組件,每個(gè)組件都是這樣的管理方式。這樣暴露給用戶的是,只需要對(duì)一個(gè)組件進(jìn)行訪問(wèn),則可以達(dá)到一個(gè)完整系統(tǒng)調(diào)用的一致性效果。以 Tomcat 最頂級(jí)的組件 Server 來(lái)看,它的 init() 方法:
- 「觀察者模式」:Tomcat 考慮自身的可擴(kuò)展性,避免版本升級(jí)就得修改生命周期鉤子函數(shù),它引入了監(jiān)聽(tīng)器 LifecycleListener 和 LifecycleState。它設(shè)計(jì)了一套貫穿組件生命周期全過(guò)程的狀態(tài)集合,例如:當(dāng)組件剛創(chuàng)建則處于 NEW 狀態(tài),調(diào)用了 init() 方法處于 INITIALIZED 狀態(tài)...在調(diào)用生命周期方法的前后,會(huì)改變組件的狀態(tài),而狀態(tài)的改變會(huì)被封裝成為一個(gè)個(gè)事件 LifecycleEvent,由監(jiān)聽(tīng)器來(lái)處理這些事件。
- 「模板模式」:主要體現(xiàn)了代碼實(shí)現(xiàn)上,實(shí)際上狀態(tài)的轉(zhuǎn)變、事件的創(chuàng)建以及監(jiān)聽(tīng)器的回調(diào),這些操作其實(shí)沒(méi)有必要在每個(gè)組件中自己實(shí)現(xiàn),這會(huì)造成重復(fù)代碼。Tomcat 在實(shí)現(xiàn)這個(gè)功能的時(shí)候,把這些通用邏輯抽象了出來(lái),定義為一個(gè) LifecycleBase 抽象類,它會(huì)定義骨架方法,比如 init() 方法。
Tomcat連接器
Tomcat 連接器,用來(lái)監(jiān)聽(tīng) Socket 連接,將 TCP 底層的字節(jié)流數(shù)據(jù),轉(zhuǎn)換為 Request 和 Response;連接器主要有3個(gè)組件:「EndPoint、Processor、Adapter」。
- ?「Endpoint」 負(fù)責(zé)提供字節(jié)流給 Processor。
- 「Processor」 負(fù)責(zé)提供 Tomcat Request 對(duì)象給 Adapter。
- 「Adapter」 負(fù)責(zé)轉(zhuǎn)換 ServletRequest 對(duì)象給容器。
其中,Tomcat 將 EndPoint 和 Processor 組合到一起,組成了 ProtocolHandler,這其實(shí)就是一種組合設(shè)計(jì)模式的使用。
Tomcat連接器-NioEndpoint
「Tomcat 使用 NioEndPoint 基于 java 的 nio 包實(shí)現(xiàn)了 I/O 多路復(fù)用模型」,主要包含了 LimitLatch、Acceptor、Poller、SocketProcessor 和 Executor 共 5 個(gè)組件。它的工作過(guò)程如下:
- 「LimitLatch」:負(fù)責(zé)控制最大連接數(shù),NIO 模式下默認(rèn)是 10000,達(dá)到這個(gè)閾值后,連接請(qǐng)求被拒絕。它是基于 AQS 實(shí)現(xiàn),原理就跟 Lock 一樣。
- 「Acceptor」:獨(dú)立線程,不斷調(diào)用 ServerSocketChannel 的 accept() 方法來(lái)接收新連接,一旦有新的連接請(qǐng)求到來(lái),返回 SocketChannel 對(duì)象,然后將其封裝在一個(gè) PollerEvent 對(duì)象中,并將 PollerEvent 對(duì)象壓入 Poller 的 Queue 里(「典型的生產(chǎn)者 - 消費(fèi)者模式」)。
- 「Poller」:獨(dú)立運(yùn)行在一個(gè)線程里,底層就是一個(gè) Selector,每個(gè) Poller 線程可能同時(shí)被多個(gè) Acceptor 線程調(diào)用來(lái)注冊(cè) PollerEvent。Poller 不斷的通過(guò)內(nèi)部的 Selector 對(duì)象向內(nèi)核查詢 Channel 的狀態(tài),一旦可讀就生成任務(wù)類 SocketProcessor 交給 Executor 去處理。
- 「SocketProcessor」:實(shí)現(xiàn)了 Runable 接口,主要是調(diào)用 Http11Processor 來(lái)處理請(qǐng)求。Tomcat 會(huì)將 Socket 包裝成一個(gè) SocketWrapper,Http11Processor 會(huì)調(diào)用 SocketWrapper 來(lái)讀寫(xiě)數(shù)據(jù)。
- 「Executor」:自定義的線程池,負(fù)責(zé)運(yùn)行 SocketProcessor ,會(huì)調(diào)用 Http11Processor 來(lái)讀取和解析請(qǐng)求數(shù)據(jù)。Http11Processor 是應(yīng)用層協(xié)議的封裝,它會(huì)調(diào)用容器獲得響應(yīng),再把響應(yīng)通過(guò) Channel 寫(xiě)出。
Tomcat連接器-Nio2Endpoint
Tomcat 還支持了異步 I/O,基于 Java AIO 實(shí)現(xiàn) - Nio2Endpoint 的組件跟 NioEndpoint 類似,但是 Nio2Endpoint 中沒(méi)有 Poller 組件,也就是沒(méi)有 Selector。這是因?yàn)樵诋惒?I/O 模式下,Selector 的工作交給內(nèi)核來(lái)做了。
- 「LimitLatch」:跟 NioEndPoint 一樣,連接控制器,它負(fù)責(zé)控制最大連接數(shù)。
- 「Nio2Acceptor」:擴(kuò)展了 Acceptor,自己就是處理連接的回調(diào)類,用異步 I/O 的方式來(lái)接收新連接后,得到一個(gè) AsynchronousSocketChannel,它會(huì)將其封裝成一個(gè) Nio2SocketWrapper,并創(chuàng)建一個(gè)SocketProcessor 任務(wù)類交給線程池處理。
- 「Nio2SocketWrapper」:實(shí)際讀取 Channel 內(nèi)的數(shù)據(jù),并提供接口給 Http11Processor 讀寫(xiě)。但是由于異步 I/O 的性質(zhì),Http11Processor 讀取 Nio2SocketWrapper 時(shí)很有可能內(nèi)核還沒(méi)有將數(shù)據(jù)準(zhǔn)備好,為了解決這個(gè)問(wèn)題,Http11Processor 采用了2次 read 調(diào)用:通過(guò)注冊(cè)回調(diào)類 readCompletionHandler。
最后
其實(shí) Tomcat 的實(shí)現(xiàn)細(xì)節(jié)很多,沒(méi)辦法一一重現(xiàn),大部分情況下需要自己去對(duì)著源碼跑一遍,像很多實(shí)際運(yùn)用:
- ContainerBackgroundProcessor,實(shí)現(xiàn)熱更新機(jī)制:熱加載和熱部署。
- 對(duì)象池技術(shù),典型的以空間換時(shí)間的思路,通過(guò) SynchronizedStack 減少 SocketWrapper 和SocketProcessor 的創(chuàng)建和銷毀。
- Servlet 3.0 中引入的異步 Servlet,Tomcat 對(duì)其做了支持,它的思想是:讓業(yè)務(wù)線程和 Tomcat I/O 線程分離開(kāi),將復(fù)雜耗時(shí)的業(yè)務(wù)計(jì)算移到業(yè)務(wù)線程池中進(jìn)行,釋放 Tomcat的I/O 線程,以便可以及時(shí)響應(yīng)其它請(qǐng)求。
- 會(huì)話管理...
- 集群管理...