Tomcat服務(wù)器作為目前比較流行的一種服務(wù)器容器已經(jīng)被廣泛用于后臺(tái)服務(wù)器的搭建姆吭、后臺(tái)集成框架的嵌入(如SpringBoot)冷溃,不同于Apache油昂、Nginx本身(注意是本身震贵,其實(shí)可以搭配后臺(tái)腳本實(shí)現(xiàn)動(dòng)態(tài)網(wǎng)頁(yè))僅僅支持靜態(tài)網(wǎng)頁(yè)擎宝,由于Tomcat其實(shí)是一種Servlet規(guī)范的實(shí)現(xiàn),而Servlet又支持JSP涡上,所以Tomcat實(shí)際上是完美支持動(dòng)態(tài)頁(yè)面的渲染的趾断。了解Tomcat的工作原理,有助于了解實(shí)際的后臺(tái)服務(wù)器是采用什么策略邏輯來(lái)實(shí)現(xiàn)的吩愧。網(wǎng)上有好多文章談及模塊組成芋酌、源碼解讀,但就我個(gè)人而言并不是很好理解耻警,所以參考了songxinjianqwe的開(kāi)源代碼手寫(xiě)簡(jiǎn)化版Web服務(wù)器隔嫡、劉光瑞大佬的書(shū)Tomcat架構(gòu)解析的第二章談一下個(gè)人的理解甸怕!其中songxinjianqwe的開(kāi)源代碼使用簡(jiǎn)明的邏輯簡(jiǎn)化實(shí)現(xiàn)了tomcat的一些基礎(chǔ)性的設(shè)計(jì)思想甘穿,劉光瑞的書(shū)籍從宏觀上由淺入深解釋了Tomcat的各個(gè)模塊是如何形成的。這里梢杭,我先講框架温兼,然后再分別結(jié)合songxinjianqwe的開(kāi)源代碼和Tomcat的原生代碼,較基礎(chǔ)的談一下Tomcat的設(shè)計(jì)思想武契。由于Tomcat的代碼復(fù)雜度很高募判,以我現(xiàn)在的水平基本很難理解更深的層次,而且個(gè)人認(rèn)為我現(xiàn)在的階段也不適合研究過(guò)深咒唆!
好了届垫,如果你已經(jīng)學(xué)習(xí)了基礎(chǔ)的Java多線程、Java網(wǎng)絡(luò)編程全释,讓你設(shè)計(jì)一個(gè)Http服務(wù)器你會(huì)怎么做装处?
1、初始化一個(gè)serversocket
2浸船、監(jiān)聽(tīng)客戶端連接妄迁,并通過(guò)accept創(chuàng)建客戶端socket
3、解析socket流李命,并構(gòu)造響應(yīng)數(shù)據(jù)
于是你的結(jié)構(gòu)圖是這樣的:
這是最簡(jiǎn)單的模型登淘,在這個(gè)模型里所有的Socket創(chuàng)建、連接封字、流操作都在一個(gè)模塊里黔州,但是Tomcat實(shí)際上為了多協(xié)議(不僅僅是Http耍鬓,還包括AJP等),所以為了使特定協(xié)議與Server解耦合流妻,Tomcat的設(shè)計(jì)者把連接和流操作分離開(kāi)來(lái)界斜,并讓Server分別持有連接、流操作合冀,于是結(jié)構(gòu)圖變成了這樣:
其中一個(gè)啟動(dòng)中的Server就是一個(gè)Tomcat應(yīng)用程序?qū)嵗鬓保粋€(gè)Server可以有多個(gè)Connector以管理多個(gè)連接協(xié)議,所以也可以持有不同的對(duì)應(yīng)的Engine來(lái)進(jìn)行流處理君躺。但是這里有個(gè)問(wèn)題就是峭判,N個(gè)Connector是如何對(duì)應(yīng)到M個(gè)Engine處理器以實(shí)現(xiàn)特定的連接交給特定的流處理器來(lái)執(zhí)行任務(wù)呢?為了解決這個(gè)問(wèn)題棕叫,Tomcat設(shè)計(jì)了Service模塊林螃,并變成了下面的結(jié)構(gòu)圖模型:
其中一個(gè)Service模塊維護(hù)多個(gè)Connector形成Connector集合并對(duì)應(yīng)一個(gè)Engine,而一個(gè)Server可以有多個(gè)Service俺泣!至此疗认,一個(gè)Tomcat基礎(chǔ)模型就構(gòu)建完成了,后續(xù)所有的功能模塊伏钠、線程設(shè)計(jì)均基于這個(gè)模型構(gòu)建横漏!我們首先要看的是Engine模塊,在流處理中依然有很多問(wèn)題要解決熟掂!第一個(gè)問(wèn)題缎浇,如果我這臺(tái)主機(jī)提供多個(gè)域名服務(wù),這時(shí)我們?cè)L問(wèn)不同的域名其實(shí)就得交由不同的應(yīng)用邏輯來(lái)處理赴肚,但是如果Engine只有一個(gè)(我們當(dāng)然可以創(chuàng)建多個(gè)Service進(jìn)而實(shí)現(xiàn)多個(gè)Engine素跺,最簡(jiǎn)單的方法就是再啟動(dòng)一個(gè)Tomcat),那么就必須在請(qǐng)求到達(dá)Engine后執(zhí)行分配邏輯誉券,來(lái)處理不同的域名下的服務(wù)指厌,Tomcat設(shè)計(jì)了虛擬主機(jī)的模塊來(lái)執(zhí)行這個(gè)功能,關(guān)于虛擬主機(jī)是這樣的一個(gè)概念:
在一臺(tái)Tomcat服務(wù)器中可以同時(shí)管理多個(gè)站點(diǎn)踊跟,即可以將多個(gè)站點(diǎn)配置在同一臺(tái)Tomcat服務(wù)器上踩验,而對(duì)于用戶(瀏覽器)而言,是不知道具體哪些網(wǎng)站是布置在同一臺(tái)Tomcat(服務(wù)器)之上的琴锭,對(duì)于用戶(瀏覽器)而言晰甚,每個(gè)站點(diǎn)都像是運(yùn)行在各自獨(dú)立的服務(wù)器上。此時(shí)每個(gè)網(wǎng)站就是運(yùn)行在同一臺(tái)這是服務(wù)器中各自對(duì)應(yīng)的虛擬主機(jī)上决帖。此時(shí)厕九,簡(jiǎn)單的理解,每個(gè)網(wǎng)站就可以認(rèn)為是一個(gè)虛擬主機(jī)地回。
添加了虛擬主機(jī)的模塊扁远,Tomcat于是就變成了下面的結(jié)構(gòu)模型:
當(dāng)請(qǐng)求過(guò)來(lái)的時(shí)候俊鱼,會(huì)通過(guò)解析當(dāng)前請(qǐng)求的域名來(lái)處理不同Hosts的數(shù)據(jù)流,所以一個(gè)Engine可以管理多個(gè)Hosts畅买!第二個(gè)問(wèn)題并闲,我們一直強(qiáng)調(diào)“流處理”,其實(shí)流處理就是根據(jù)當(dāng)前上下文的所有變量來(lái)實(shí)現(xiàn)Web應(yīng)用的功能邏輯罷了谷羞,如socket帝火、session、cookie等等湃缎,這些都是在一個(gè)流程處理中的所有有用變量或者對(duì)象犀填,而且這些對(duì)象會(huì)在不同類之間相互調(diào)用,那么如何保證這些變量或者對(duì)象可以靈活的獲取和構(gòu)造并保證一致性嗓违,Tomcat于是設(shè)計(jì)了context組件九巡,于是,就有了下面的模型:
這里的context其實(shí)就是ServletContext蹂季,實(shí)際上一個(gè)ServletContex就是代表了一個(gè)Web應(yīng)用冕广!所以一個(gè)Host下顯然可以有不同的Web應(yīng)用,即一個(gè)Host可以擁有多個(gè)context偿洁!因?yàn)檫@里我們談及了Servlet撒汉,上文也說(shuō)過(guò)Tomcat其實(shí)就是Servlet的規(guī)范實(shí)現(xiàn),所以一個(gè)Web應(yīng)用自然可以包含多個(gè)Servlet父能,Tomcat中定義為Wrapper神凑,于是就有了下面的模型圖:
Servlet的加載時(shí)通過(guò)反射的形式加載的,因?yàn)楫?dāng)我們創(chuàng)建Http服務(wù)器的時(shí)候何吝,我們所有的Servlet都是繼承HttpServlet,這樣當(dāng)讀取web.xml文件的時(shí)候就可以按照類似下面的代碼構(gòu)建我們創(chuàng)建的Servlet:
HttpServlet httpServlet = (HttpServlet) Class.forName(“自定義Servlet的包路徑”).newInstance();
至此,關(guān)于Engine部分的內(nèi)容基本構(gòu)造完成鹃唯,但是Tomcat設(shè)計(jì)時(shí)為了統(tǒng)一管理右側(cè)的這些組件爱榕,實(shí)際把他們抽象成了一個(gè)被稱為container的組件,使得四個(gè)組件都繼承這個(gè)container坡慌,也就是我們說(shuō)的容器概念黔酥,然后統(tǒng)一管理各個(gè)組件的聲明周期!于是的Tomcat模型可以擴(kuò)充成這樣:
這里變成虛線洪橘,是為了體現(xiàn)各個(gè)組件實(shí)際上是由父類Container容器實(shí)現(xiàn)的“弱依賴”關(guān)系跪者,把原先的實(shí)現(xiàn)改為了虛線!至此我們看到一個(gè)較為完整的組件關(guān)系圖熄求,于是你們見(jiàn)到最多的關(guān)于Tomcat的結(jié)構(gòu)圖就是下面一幅:
這個(gè)架構(gòu)圖展示了上述Tomcat的核心組件渣玲,并表示出了各個(gè)組件間的組合關(guān)系:一個(gè)Server包含多個(gè)Service,一個(gè)Service包含多個(gè)Connector形成的一個(gè)集合弟晚,并對(duì)應(yīng)一個(gè)Container組件忘衍!
接下來(lái)逾苫,我們?cè)撎接慍onnector組件的實(shí)現(xiàn)細(xì)節(jié)!
我們提到Connector組件可以支持不同的協(xié)議枚钓,默認(rèn)是Http和AJP铅搓,實(shí)際上Connector還支持不同的I/O模型:如BIO、NIO搀捷、APR等等星掰,所以如何針對(duì)不同類型的連接方式來(lái)建立客戶端的通信,Tomcat設(shè)計(jì)了被稱為協(xié)議處理器的上層組件:ProtocolHandler嫩舟,如ProtocolHandler包含又包含兩個(gè)組件蹋偏,一個(gè)是抽象的AbstractEndpoint,用于實(shí)現(xiàn)不同的協(xié)議至壤、I/O模型威始。NioEndpoint指代非阻塞式SocketIO,另外還包含Processer組件用于實(shí)際的對(duì)應(yīng)容器的邏輯處理像街;于是就出現(xiàn)了類似Http11NioProtocol這樣的實(shí)現(xiàn)來(lái)處理支持Http1.1和NIO的連接方式黎棠,這樣我們的模型圖就變成了這樣:
這里面我們忽略一個(gè)上述文章中重要的一個(gè)組件功能:Service。這個(gè)組件如文章所述是建立Connector和Container(我們前文說(shuō)的是Engine镰绎,但是注意自從建立了Container組件脓斩,實(shí)際上Engine等四個(gè)組件被統(tǒng)稱為Container組件的子組件)的重要橋梁,所以實(shí)際上談?wù)揅onnector就必須談?wù)摰木褪荢ervice是如何實(shí)現(xiàn)兩者的映射關(guān)系的畴栖!當(dāng)Processor讀取請(qǐng)求信息后需要按照請(qǐng)求的信息映射到相應(yīng)的容器來(lái)處理随静,這里的映射信息是由Service的Maper和MaperListener來(lái)實(shí)現(xiàn)的,這里使用了觀察者模式來(lái)監(jiān)聽(tīng)客戶端的連接并注冊(cè)到Maper中吗讶,后續(xù)只需要根據(jù)Maper維護(hù)的映射信息即可找到對(duì)應(yīng)的容器燎猛。實(shí)際上為了實(shí)現(xiàn)Connector和Service的解耦運(yùn)用了適配器模式,所以Processor的一個(gè)默認(rèn)適配器實(shí)現(xiàn)被稱為CoyoteAdapter照皆;我們的模型圖變成了下面的結(jié)構(gòu):
但是Tomcat整合的考慮更多:既然可以由Container來(lái)統(tǒng)一表示這4個(gè)組件重绷,那么圖中所有的組件其實(shí)都包含啟動(dòng)膜毁、停止等各個(gè)生命周期,為了高效的管理個(gè)組件的運(yùn)行瘟滨,我們有必要把所有的組件在抽象成一個(gè)更通用的組件來(lái)進(jìn)行統(tǒng)一的管理,這里Tomcat設(shè)計(jì)了Lifecycle組件杂瘸,于是實(shí)際的Tomcat變成了下面的模型結(jié)構(gòu)圖:
至此倒淫,一個(gè)基本的Tomcat模型就創(chuàng)造出來(lái)了胧沫!