WEB服務(wù)器
只要Web上的Server都叫Web Server,但是大家分工不同,解決的問(wèn)題也不同,所以根據(jù)Web Server提供的功能,每個(gè)Web Server的名字也會(huì)不一樣。
按功能分類(lèi),Web Server可以分為:
|- Web Server
|- Http Server
|- Application Server
|- Servlet Container
|- CGI Server
|- ......
Http服務(wù)器
HTTP Server本質(zhì)上也是一種應(yīng)用程序——它通常運(yùn)行在服務(wù)器之上,綁定服務(wù)器的IP地址并監(jiān)聽(tīng)某一個(gè)tcp端口來(lái)接收并處理HTTP請(qǐng)求,這樣客戶(hù)端(一般來(lái)說(shuō)是IE, Firefox,Chrome這樣的瀏覽器)就能夠通過(guò)HTTP協(xié)議來(lái)獲取服務(wù)器上的網(wǎng)頁(yè)(HTML格式)、文檔(PDF格式)、音頻(MP4格式)、視頻(MOV格式)等等資源。
一個(gè)HTTP Server關(guān)心的是HTTP協(xié)議層面的傳輸和訪(fǎng)問(wèn)控制,所以在A(yíng)pache/Nginx上你可以看到代理、負(fù)載均衡等功能。
客戶(hù)端通過(guò)HTTP Server訪(fǎng)問(wèn)服務(wù)器上存儲(chǔ)的靜態(tài)資源(HTML文件、圖片文件等等)。
通過(guò)CGI/Servlet技術(shù),也可以將處理過(guò)的動(dòng)態(tài)內(nèi)容通過(guò)HTTP Server分發(fā),但是一個(gè)HTTP Server始終只是把服務(wù)器上的文件如實(shí)的通過(guò)HTTP協(xié)議傳輸給客戶(hù)端。
HTTP Server中經(jīng)常使用的是Apache、Nginx兩種,HTTP Server主要用來(lái)做靜態(tài)內(nèi)容服務(wù)、代理服務(wù)器、負(fù)載均衡等。直面外來(lái)請(qǐng)求轉(zhuǎn)發(fā)給后面的應(yīng)用服務(wù)(Tomcat,django什么的)。
|- Http Server
|- Apache
|- Nginx
Application Server
Application Server 是一個(gè)應(yīng)用執(zhí)行的服務(wù)器。它首先需要支持開(kāi)發(fā)語(yǔ)言的 Runtime(對(duì)于 Tomcat 來(lái)說(shuō),就是 Java),保證應(yīng)用能夠在應(yīng)用服務(wù)器上正常運(yùn)行。其次,需要支持應(yīng)用相關(guān)的規(guī)范,例如類(lèi)庫(kù)、安全方面的特性。與HTTP Server相比,Application Server能夠動(dòng)態(tài)的生成資源并返回到客戶(hù)端。
|- Application Server
|- Tomcat
|- Jetty
當(dāng)初在A(yíng)pache Server開(kāi)發(fā)時(shí)還未出現(xiàn)Servlet的概念,所以Apache不能內(nèi)置支持Servlet。實(shí)際上,除了Apache,其他許多HTTP Server軟件都不能直接支持Servlet。為了支持Servlet,通常要單獨(dú)開(kāi)發(fā)程序,這種程序一般稱(chēng)為服務(wù)器小程序容器(Servlet Container),有時(shí)也叫做服務(wù)器小程序引擎(Servlet Engine)。它是Web服務(wù)器或應(yīng)用程序服務(wù)器的一部分,用于在發(fā)送的請(qǐng)求和響應(yīng)之上提供網(wǎng)絡(luò)服務(wù),解碼基于MIME的請(qǐng)求,格式化基于MIME的響應(yīng),它在Servlet的生命周期內(nèi)包容和管理Servlet,是一個(gè)實(shí)時(shí)運(yùn)行的外殼程序。運(yùn)行時(shí)由Web服務(wù)器軟件處理一般請(qǐng)求,并把Servlet調(diào)用傳遞給“容器”來(lái)處理。
比如,對(duì)于 Tomcat 來(lái)說(shuō),就是需要提供 JSP/Sevlet 運(yùn)行需要的標(biāo)準(zhǔn)類(lèi)庫(kù)、Interface 等。為了方便,應(yīng)用服務(wù)器往往也會(huì)集成 HTTP Server 的功能,但是不如專(zhuān)業(yè)的 HTTP Server 那么強(qiáng)大,所以Application Server往往是運(yùn)行在 HTTP Server 的背后,執(zhí)行應(yīng)用,將動(dòng)態(tài)的內(nèi)容轉(zhuǎn)化為靜態(tài)的內(nèi)容之后,通過(guò) HTTP Server 分發(fā)到客戶(hù)端。
Tomcat運(yùn)行在JVM之上,它和HTTP服務(wù)器一樣,綁定IP地址并監(jiān)聽(tīng)TCP端口,同時(shí)還包含以下指責(zé):
1. 管理Servlet程序的生命周期;
2. 將URL映射到指定的Servlet進(jìn)行處理;
3. 與Servlet程序合作處理HTTP請(qǐng)求——根據(jù)HTTP請(qǐng)求生成HttpServletRequest/Response對(duì)象并傳遞給Servlet進(jìn)行處理,將Servlet中的HttpServletResponse對(duì)象生成的內(nèi)容返回給瀏覽器;
所以 Tomcat 屬于是一個(gè)「Application Server」,但是更準(zhǔn)確的來(lái)說(shuō),是一個(gè)「Servlet/JSP」應(yīng)用的容器(Ruby/Python 等其他語(yǔ)言開(kāi)發(fā)的應(yīng)用也無(wú)法直接運(yùn)行在 Tomcat 上)。
Tomcat工作原理
Tomcat 的結(jié)構(gòu)很復(fù)雜,但是 Tomcat 也非常的模塊化,找到了 Tomcat 最核心的模塊,您就抓住了 Tomcat 的“七寸”。下面是 Tomcat 的總體結(jié)構(gòu)圖:
從上圖可以看出Tomcat的核心是兩個(gè)組件:連接器(Connector)和容器(Container)。Connector組件是負(fù)責(zé)生成請(qǐng)求對(duì)象和響應(yīng)對(duì)象的,Tomcat默認(rèn)的是HttpConnector,負(fù)責(zé)根據(jù)收到的Http請(qǐng)求報(bào)文生成Request對(duì)象和Response對(duì)象,并把這兩個(gè)對(duì)象傳遞給Container,然后根據(jù)Response中的內(nèi)容生成相應(yīng)的HTTP報(bào)文。
Container是容器的父接口,所有子容器都必須實(shí)現(xiàn)這個(gè)接口,簡(jiǎn)單來(lái)說(shuō)就是服務(wù)器部署的項(xiàng)目是運(yùn)行在Container中的。Container里面的項(xiàng)目獲取到Connector傳遞過(guò)來(lái)對(duì)應(yīng)的的Request對(duì)象和Response對(duì)象進(jìn)行相應(yīng)的操作。
Connector可以根據(jù)不同的設(shè)計(jì)和應(yīng)用場(chǎng)景進(jìn)行替換。一個(gè)Container可以選擇對(duì)應(yīng)多個(gè)Connector。多個(gè)Connector和一個(gè)Container就形成了一個(gè)Service,有了Service就可以對(duì)外提供服務(wù)了。
Tomcat要為一個(gè)Servlet的請(qǐng)求提供服務(wù),需要做四件事:
- 創(chuàng)建一個(gè)request對(duì)象并填充那些有可能被所引用的Servlet使用的信息,如參數(shù),頭部、cookies、查詢(xún)字符串等。一個(gè)request對(duì)象就是javax.servlet.ServletRequest或javax.servlet.http.ServletRequest接口的一個(gè)實(shí)例。
- 創(chuàng)建一個(gè)response對(duì)象,所引用的servlet使用它來(lái)給客戶(hù)端發(fā)送響應(yīng)。一個(gè)response對(duì)象是javax.servlet.ServletResponse或javax.servlet.http.ServletResponse接口的一個(gè)實(shí)例。
- 調(diào)用servlet的service方法,并傳入request和response對(duì)象。這里servlet會(huì)從request對(duì)象取值,給response寫(xiě)值。
- 根據(jù)servlet返回的response生成相應(yīng)的HTTP響應(yīng)報(bào)文。
既然我們已經(jīng)抓到Tomcat的“七寸”,兩個(gè)核心組件:連接器(Connector)和容器(Container),那這樣從連接器(Connector)入手,來(lái)看下Tomcat處理HTTP請(qǐng)求的流程。
很多開(kāi)源應(yīng)用服務(wù)器都是集成tomcat作為web container的,而且對(duì)于tomcat的servlet container這部分代碼很少改動(dòng)。這樣,這些應(yīng)用服務(wù)器的性能基本上就取決于Tomcat處理HTTP請(qǐng)求的connector模塊的性能。
Tomcat架構(gòu)模塊
- Server(服務(wù)器)是Tomcat構(gòu)成的頂級(jí)構(gòu)成元素,所有一切均包含在Server中,Server的實(shí)現(xiàn)類(lèi)StandardServer可以包含一個(gè)到多個(gè)Services;
- 次頂級(jí)元素Service的實(shí)現(xiàn)類(lèi)為StandardService調(diào)用了容器(Container)接口,其實(shí)是調(diào)用了Servlet Engine(引擎),而且StandardService類(lèi)中也指明了該Service歸屬的Server;
- 接下來(lái)次級(jí)的構(gòu)成元素就是容器(Container):主機(jī)(Host)、上下文(Context)和引擎(Engine)均繼承自Container接口,所以它們都是容器。但是,它們是有父子關(guān)系的,在主機(jī)(Host)、上下文(Context)和引擎(Engine)這三類(lèi)容器中,引擎是頂級(jí)容器,直接包含是主機(jī)容器,而主機(jī)容器又包含上下文容器,所以引擎、主機(jī)和上下文從大小上來(lái)說(shuō)又構(gòu)成父子關(guān)系,雖然它們都繼承自Container接口。
- 連接器(Connector)將Service和Container連接起來(lái),首先它需要注冊(cè)到一個(gè)Service,它的作用就是把來(lái)自客戶(hù)端的請(qǐng)求轉(zhuǎn)發(fā)到Container(容器),這就是它為什么稱(chēng)作連接器的原因。
Tomcat運(yùn)行流程
假設(shè)來(lái)自客戶(hù)的請(qǐng)求為:http://localhost:8080/test/index.jsp
請(qǐng)求被發(fā)送到本機(jī)端口8080,被在那里偵聽(tīng)的Coyote HTTP/1.1 Connector獲得;
Connector把該請(qǐng)求交給它所在的Service的Engine來(lái)處理,并等待Engine的回應(yīng);
Engine獲得請(qǐng)求localhost:8080/test/index.jsp,匹配它所有虛擬主機(jī)Host;
Engine匹配到名為localhost的Host(即使匹配不到也把請(qǐng)求交給該Host處理,因?yàn)樵揌ost被定義為該Engine的默認(rèn)主機(jī));
localhost Host獲得請(qǐng)求/test/index.jsp,匹配它所擁有的所有Context;
Host匹配到路徑為/test的Context(如果匹配不到就把該請(qǐng)求交給路徑名為""的Context去處理);
path="/test"的Context獲得請(qǐng)求/index.jsp,在它的mapping table中尋找對(duì)應(yīng)的servlet;
Context匹配到URL PATTERN為*.jsp的servlet,對(duì)應(yīng)于JspServlet類(lèi);
構(gòu)造HttpServletRequest對(duì)象和HttpServletResponse對(duì)象,作為參數(shù)調(diào)用JspServlet的doGet或doPost方法;
Context把執(zhí)行完了之后的HttpServletResponse對(duì)象返回給Host;
Host把HttpServletResponse對(duì)象返回給Engine;
Engine把HttpServletResponse對(duì)象返回給Connector;
Connector把HttpServletResponse對(duì)象返回給客戶(hù)browser;
Connector 組件
Connector 組件是 Tomcat 中兩個(gè)核心組件之一,它的主要任務(wù)是負(fù)責(zé)接收瀏覽器的發(fā)過(guò)來(lái)的 tcp 連接請(qǐng)求,創(chuàng)建一個(gè) Request 和 Response 對(duì)象分別用于和請(qǐng)求端交換數(shù)據(jù),然后會(huì)產(chǎn)生一個(gè)線(xiàn)程來(lái)處理這個(gè)請(qǐng)求并把產(chǎn)生的 Request 和 Response 對(duì)象傳給處理這個(gè)請(qǐng)求的線(xiàn)程,處理這個(gè)請(qǐng)求的線(xiàn)程就是 Container 組件要做的事了。
由于這個(gè)過(guò)程比較復(fù)雜,大體的流程可以用下面的順序圖來(lái)解釋?zhuān)?/p>
Tomcat5 中默認(rèn)的 Connector 是 Coyote,這個(gè) Connector 是可以選擇替換的。Connector 最重要的功能就是接收連接請(qǐng)求然后分配線(xiàn)程讓 Container 來(lái)處理這個(gè)請(qǐng)求,所以這必然是多線(xiàn)程的,多線(xiàn)程的處理是 Connector 設(shè)計(jì)的核心。
當(dāng) Connector 將 socket 連接封裝成 request 和 response 對(duì)象后接下來(lái)的事情就交給 Container 來(lái)處理了。
Servlet 容器“Container”
Container 是容器的父接口,所有子容器都必須實(shí)現(xiàn)這個(gè)接口,Container 容器的設(shè)計(jì)用的是典型的責(zé)任鏈的設(shè)計(jì)模式,它有四個(gè)子容器組件構(gòu)成,分別是:Engine、Host、Context、Wrapper,這四個(gè)組件不是平行的,而是父子關(guān)系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。通常一個(gè) Servlet class 對(duì)應(yīng)一個(gè) Wrapper,如果有多個(gè) Servlet 就可以定義多個(gè) Wrapper,如果有多個(gè) Wrapper 就要定義一個(gè)更高的 Container 了,如 Context,Context 通常就是對(duì)應(yīng)下面這個(gè)配置:
<Context
path="/library"
docBase="D:\projects\library\deploy\target\library.war"
reloadable="true"
/>
容器的總體設(shè)計(jì)
Context 還可以定義在父容器 Host 中,Host 不是必須的,但是要運(yùn)行 war 程序,就必須要 Host,因?yàn)?war 中必有 web.xml 文件,這個(gè)文件的解析就需要 Host 了,如果要有多個(gè) Host 就要定義一個(gè) top 容器 Engine 了。而 Engine 沒(méi)有父容器了,一個(gè) Engine 代表一個(gè)完整的 Servlet 引擎。
當(dāng) Connector 接受到一個(gè)連接請(qǐng)求時(shí),將請(qǐng)求交給 Container,Container 是如何處理這個(gè)請(qǐng)求的?這四個(gè)組件是怎么分工的,怎么把請(qǐng)求傳給特定的子容器的呢?又是如何將最終的請(qǐng)求交給 Servlet 處理。下面是這個(gè)過(guò)程的時(shí)序圖:
參考