Tomcat 是一個(gè) Web 應(yīng)用服務(wù)器,它是對(duì) HTTP 和 Servlet 規(guī)范的實(shí)現(xiàn),簡單來說它做了這幾件事:處理 HTTP 協(xié)議院仿、執(zhí)行 Servlet 和處理網(wǎng)絡(luò) I/O。
這里以 6.0.53 版本為例(實(shí)現(xiàn)了 HTTP/1.1、Servlet2.5)门扇,研究其基本結(jié)構(gòu)。 關(guān)于源碼版本偿渡,我使用的是 tomcat6臼寄,因?yàn)?7 為了重構(gòu)有太多的抽象,看著實(shí)在費(fèi)勁溜宽,6 代碼雖有冗余但讀起來很直觀吉拳,并且低版本也不影響理解 Tomcat 的核心流程。
體系結(jié)構(gòu)
從 server.xml 中就能夠看出 Tomcat 各組件的層次結(jié)構(gòu)适揉,具體結(jié)構(gòu)圖如下:
?Tomcat 架構(gòu)
?Server:代表整個(gè)容器留攒,它可能包含一個(gè)或多個(gè) Service 和全局 JNDI 資源;
?Service:包含一個(gè)或多個(gè) Connector嫉嘀,這些連接器與一個(gè) Engine 相關(guān)聯(lián)炼邀;
?Engine:表示請(qǐng)求處理流水線(pipeline),它接收所有連接器的請(qǐng)求剪侮,并將響應(yīng)交給適當(dāng)?shù)倪B接器返回給客戶端拭宁;
??Host:網(wǎng)絡(luò)名稱(域名)與 Tomcat 服務(wù)器的關(guān)聯(lián),默認(rèn)主機(jī)名 localhost,一個(gè) Engine 可包含多個(gè) Host红淡;
?Connector:處理與客戶端的通信不狮,網(wǎng)絡(luò) I/O;
??Context:表示一個(gè) Web 應(yīng)用程序在旱,一個(gè) Host 包含多個(gè)上下文摇零。
學(xué)習(xí)群64弍46衣3凌9,資料群69似64陸0吧3
?服務(wù)器模型
服務(wù)器模型(或 I/O 模型)桶蝎,描述的是 TCP 連接的處理方式驻仅,以及 Socket 讀寫時(shí)線程的狀態(tài)。
Java 里常用的是 BIO 和 NIO登渣,分別對(duì)應(yīng)同步阻塞和同步非阻塞兩種模型噪服,Tomcat 中的 Connector 組件就是對(duì)這兩種的封裝實(shí)現(xiàn)。
BIO - 阻塞式
Tomcat 實(shí)現(xiàn)了一個(gè)一連接一線程的簡單服務(wù)器模型胜茧,內(nèi)部采用線程池做了優(yōu)化粘优,設(shè)計(jì)如下:
Tomcat BIO 模型
?當(dāng) Acceptor 接收到一個(gè) TCP 連接時(shí),線程池分配一個(gè)線程進(jìn)行處理呻顽;
?線程調(diào)用 read() 方法讀取 Socket 輸入流中的字節(jié)雹顺,此時(shí)線程阻塞(Block),直到收到客戶端發(fā)送的數(shù)據(jù)廊遍;
?收到數(shù)據(jù)后嬉愧,進(jìn)行解碼、業(yè)務(wù)處理喉前、編碼没酣,最后把響應(yīng)發(fā)送到客戶端,關(guān)閉連接卵迂。
由此可以看出裕便,合理的分配線程池大小可以一定程度上提高系統(tǒng)的并發(fā)能力。
NIO - 非阻塞
BIO 的缺點(diǎn)在于不管當(dāng)前連接有沒有數(shù)據(jù)傳輸狭握,它始終阻塞占用線程池內(nèi)的一個(gè)線程闪金,而 NIO 的處理方式是若通道無數(shù)據(jù)可讀取,此時(shí)線程不阻塞直接返回论颅,可用于處理其他連接哎垦,提高了線程利用率。那怎么知道什么時(shí)候處理數(shù)據(jù)的讀寫呢恃疯?當(dāng)通道可讀或可寫時(shí)漏设,內(nèi)核會(huì)通知用戶程序進(jìn)行處理。
?NIO 的編程比較復(fù)雜今妄,常用的是 Reactor 模式郑口,它描述了一個(gè)利用多路復(fù)用 I/O鸳碧,基于事件驅(qū)動(dòng)的服務(wù)器處理模型,(這里) 基于 Doug Lea 的 Scalable IO in Java 對(duì) Reactor 進(jìn)行了實(shí)現(xiàn)犬性。
Tomcat 的設(shè)計(jì)略有不同瞻离,其設(shè)計(jì)如下:
Tomcat NIO 模型
?Acceptor 以阻塞模式接收 TCP 連接,然后將連接注冊(cè)到 Poller 上乒裆;
?Poller 以非阻塞模式處理 SSL 握手和 HTTP 請(qǐng)求頭的讀忍桌;
?BlockPoller 模擬阻塞處理 HTTP 請(qǐng)求體的讀取和發(fā)送響應(yīng)鹤耍。 值得注意的是肉迫,兩類 Poller 都只是負(fù)責(zé)事件的通知,I/O 操作都是由線程池中的線程完成稿黄。
那么喊衫,ServerSocketChannel 為什么阻塞?為什么要模擬阻塞處理請(qǐng)求體和 Servlet 響應(yīng)杆怕?
相關(guān)的討論可參考:
??Why Tomcat's Non-Blocking Connector is using a blocking socket?
?Getting my head around NIO 'simulated' blocking (trying to)
Servlet API 的實(shí)現(xiàn) Servlet 規(guī)范描述了容器如何加載和運(yùn)行 Servlet族购,如和將請(qǐng)求映射到用戶配置的 Servlet, 如何處理請(qǐng)求和響應(yīng)等相關(guān)問題财著。
Tomcat 主要實(shí)現(xiàn)了以下 API:
?ServletConfig :Servlet 名字和初始化參數(shù)联四;
?ServletContext :定義了 Web 應(yīng)用程序撑碴,Servlet 運(yùn)行的上下文撑教;
?ServletRequest :封裝客戶端請(qǐng)求;
??ServletResponse :封裝服務(wù)端響應(yīng)醉拓;
?FilterChain :請(qǐng)求過濾調(diào)用鏈伟姐;
?FilterConfig :過濾配置對(duì)象;
?RequestDispatcher :轉(zhuǎn)發(fā)請(qǐng)求亿卤,將請(qǐng)求轉(zhuǎn)發(fā)給 JSP 或另一個(gè) Servlet 處理愤兵。 其他如 Servlet、Filter排吴、GenericServlet秆乳、HttpServlet 接口或類則由用戶程序來實(shí)現(xiàn),更多詳細(xì)的介紹請(qǐng)參考規(guī)范內(nèi)容钻哩。
?小結(jié)
Tomcat 架構(gòu)看著挺簡單但做起來難屹堰,難的就是把復(fù)雜的問題抽象化、簡單化街氢,體現(xiàn)到代碼上就是如何設(shè)計(jì)和抽象出類并且優(yōu)雅的組織在一起扯键,它作為一個(gè)流行的中間件,其內(nèi)部代碼的實(shí)現(xiàn)以及優(yōu)化手段珊肃,也是值得我們?nèi)パ芯亢湍7碌摹?/p>