WEB服務器
只要Web上的Server都叫Web Server,但是大家分工不同,解決的問題也不同,所以根據Web Server提供的功能,每個Web Server的名字也會不一樣。
按功能分類,Web Server可以分為:
|- Web Server
|- Http Server
|- Application Server
|- Servlet Container
|- CGI Server
|- ......
Http服務器
HTTP Server本質上也是一種應用程序——它通常運行在服務器之上,綁定服務器的IP地址并監(jiān)聽某一個tcp端口來接收并處理HTTP請求,這樣客戶端(一般來說是IE, Firefox,Chrome這樣的瀏覽器)就能夠通過HTTP協(xié)議來獲取服務器上的網頁(HTML格式)、文檔(PDF格式)、音頻(MP4格式)、視頻(MOV格式)等等資源。
一個HTTP Server關心的是HTTP協(xié)議層面的傳輸和訪問控制,所以在Apache/Nginx上你可以看到代理、負載均衡等功能。
客戶端通過HTTP Server訪問服務器上存儲的靜態(tài)資源(HTML文件、圖片文件等等)。
通過CGI/Servlet技術,也可以將處理過的動態(tài)內容通過HTTP Server分發(fā),但是一個HTTP Server始終只是把服務器上的文件如實的通過HTTP協(xié)議傳輸給客戶端。
HTTP Server中經常使用的是Apache、Nginx兩種,HTTP Server主要用來做靜態(tài)內容服務、代理服務器、負載均衡等。直面外來請求轉發(fā)給后面的應用服務(Tomcat,django什么的)。
|- Http Server
|- Apache
|- Nginx
Application Server
Application Server 是一個應用執(zhí)行的服務器。它首先需要支持開發(fā)語言的 Runtime(對于 Tomcat 來說,就是 Java),保證應用能夠在應用服務器上正常運行。其次,需要支持應用相關的規(guī)范,例如類庫、安全方面的特性。與HTTP Server相比,Application Server能夠動態(tài)的生成資源并返回到客戶端。
|- Application Server
|- Tomcat
|- Jetty
當初在Apache Server開發(fā)時還未出現Servlet的概念,所以Apache不能內置支持Servlet。實際上,除了Apache,其他許多HTTP Server軟件都不能直接支持Servlet。為了支持Servlet,通常要單獨開發(fā)程序,這種程序一般稱為服務器小程序容器(Servlet Container),有時也叫做服務器小程序引擎(Servlet Engine)。它是Web服務器或應用程序服務器的一部分,用于在發(fā)送的請求和響應之上提供網絡服務,解碼基于MIME的請求,格式化基于MIME的響應,它在Servlet的生命周期內包容和管理Servlet,是一個實時運行的外殼程序。運行時由Web服務器軟件處理一般請求,并把Servlet調用傳遞給“容器”來處理。
比如,對于 Tomcat 來說,就是需要提供 JSP/Sevlet 運行需要的標準類庫、Interface 等。為了方便,應用服務器往往也會集成 HTTP Server 的功能,但是不如專業(yè)的 HTTP Server 那么強大,所以Application Server往往是運行在 HTTP Server 的背后,執(zhí)行應用,將動態(tài)的內容轉化為靜態(tài)的內容之后,通過 HTTP Server 分發(fā)到客戶端。
Tomcat運行在JVM之上,它和HTTP服務器一樣,綁定IP地址并監(jiān)聽TCP端口,同時還包含以下指責:
1. 管理Servlet程序的生命周期;
2. 將URL映射到指定的Servlet進行處理;
3. 與Servlet程序合作處理HTTP請求——根據HTTP請求生成HttpServletRequest/Response對象并傳遞給Servlet進行處理,將Servlet中的HttpServletResponse對象生成的內容返回給瀏覽器;
所以 Tomcat 屬于是一個「Application Server」,但是更準確的來說,是一個「Servlet/JSP」應用的容器(Ruby/Python 等其他語言開發(fā)的應用也無法直接運行在 Tomcat 上)。
Tomcat工作原理
Tomcat 的結構很復雜,但是 Tomcat 也非常的模塊化,找到了 Tomcat 最核心的模塊,您就抓住了 Tomcat 的“七寸”。下面是 Tomcat 的總體結構圖:

從上圖可以看出Tomcat的核心是兩個組件:連接器(Connector)和容器(Container)。Connector組件是負責生成請求對象和響應對象的,Tomcat默認的是HttpConnector,負責根據收到的Http請求報文生成Request對象和Response對象,并把這兩個對象傳遞給Container,然后根據Response中的內容生成相應的HTTP報文。
Container是容器的父接口,所有子容器都必須實現這個接口,簡單來說就是服務器部署的項目是運行在Container中的。Container里面的項目獲取到Connector傳遞過來對應的的Request對象和Response對象進行相應的操作。
Connector可以根據不同的設計和應用場景進行替換。一個Container可以選擇對應多個Connector。多個Connector和一個Container就形成了一個Service,有了Service就可以對外提供服務了。
Tomcat要為一個Servlet的請求提供服務,需要做四件事:
- 創(chuàng)建一個request對象并填充那些有可能被所引用的Servlet使用的信息,如參數,頭部、cookies、查詢字符串等。一個request對象就是javax.servlet.ServletRequest或javax.servlet.http.ServletRequest接口的一個實例。
- 創(chuàng)建一個response對象,所引用的servlet使用它來給客戶端發(fā)送響應。一個response對象是javax.servlet.ServletResponse或javax.servlet.http.ServletResponse接口的一個實例。
- 調用servlet的service方法,并傳入request和response對象。這里servlet會從request對象取值,給response寫值。
- 根據servlet返回的response生成相應的HTTP響應報文。
既然我們已經抓到Tomcat的“七寸”,兩個核心組件:連接器(Connector)和容器(Container),那這樣從連接器(Connector)入手,來看下Tomcat處理HTTP請求的流程。
很多開源應用服務器都是集成tomcat作為web container的,而且對于tomcat的servlet container這部分代碼很少改動。這樣,這些應用服務器的性能基本上就取決于Tomcat處理HTTP請求的connector模塊的性能。
Tomcat架構模塊

- Server(服務器)是Tomcat構成的頂級構成元素,所有一切均包含在Server中,Server的實現類StandardServer可以包含一個到多個Services;
- 次頂級元素Service的實現類為StandardService調用了容器(Container)接口,其實是調用了Servlet Engine(引擎),而且StandardService類中也指明了該Service歸屬的Server;
- 接下來次級的構成元素就是容器(Container):主機(Host)、上下文(Context)和引擎(Engine)均繼承自Container接口,所以它們都是容器。但是,它們是有父子關系的,在主機(Host)、上下文(Context)和引擎(Engine)這三類容器中,引擎是頂級容器,直接包含是主機容器,而主機容器又包含上下文容器,所以引擎、主機和上下文從大小上來說又構成父子關系,雖然它們都繼承自Container接口。
- 連接器(Connector)將Service和Container連接起來,首先它需要注冊到一個Service,它的作用就是把來自客戶端的請求轉發(fā)到Container(容器),這就是它為什么稱作連接器的原因。
Tomcat運行流程

假設來自客戶的請求為:http://localhost:8080/test/index.jsp
請求被發(fā)送到本機端口8080,被在那里偵聽的Coyote HTTP/1.1 Connector獲得;
Connector把該請求交給它所在的Service的Engine來處理,并等待Engine的回應;
Engine獲得請求localhost:8080/test/index.jsp,匹配它所有虛擬主機Host;
Engine匹配到名為localhost的Host(即使匹配不到也把請求交給該Host處理,因為該Host被定義為該Engine的默認主機);
localhost Host獲得請求/test/index.jsp,匹配它所擁有的所有Context;
Host匹配到路徑為/test的Context(如果匹配不到就把該請求交給路徑名為""的Context去處理);
path="/test"的Context獲得請求/index.jsp,在它的mapping table中尋找對應的servlet;
Context匹配到URL PATTERN為*.jsp的servlet,對應于JspServlet類;
構造HttpServletRequest對象和HttpServletResponse對象,作為參數調用JspServlet的doGet或doPost方法;
Context把執(zhí)行完了之后的HttpServletResponse對象返回給Host;
Host把HttpServletResponse對象返回給Engine;
Engine把HttpServletResponse對象返回給Connector;
Connector把HttpServletResponse對象返回給客戶browser;
Connector 組件
Connector 組件是 Tomcat 中兩個核心組件之一,它的主要任務是負責接收瀏覽器的發(fā)過來的 tcp 連接請求,創(chuàng)建一個 Request 和 Response 對象分別用于和請求端交換數據,然后會產生一個線程來處理這個請求并把產生的 Request 和 Response 對象傳給處理這個請求的線程,處理這個請求的線程就是 Container 組件要做的事了。
由于這個過程比較復雜,大體的流程可以用下面的順序圖來解釋:

Tomcat5 中默認的 Connector 是 Coyote,這個 Connector 是可以選擇替換的。Connector 最重要的功能就是接收連接請求然后分配線程讓 Container 來處理這個請求,所以這必然是多線程的,多線程的處理是 Connector 設計的核心。
當 Connector 將 socket 連接封裝成 request 和 response 對象后接下來的事情就交給 Container 來處理了。
Servlet 容器“Container”
Container 是容器的父接口,所有子容器都必須實現這個接口,Container 容器的設計用的是典型的責任鏈的設計模式,它有四個子容器組件構成,分別是:Engine、Host、Context、Wrapper,這四個組件不是平行的,而是父子關系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。通常一個 Servlet class 對應一個 Wrapper,如果有多個 Servlet 就可以定義多個 Wrapper,如果有多個 Wrapper 就要定義一個更高的 Container 了,如 Context,Context 通常就是對應下面這個配置:
<Context
path="/library"
docBase="D:\projects\library\deploy\target\library.war"
reloadable="true"
/>
容器的總體設計
Context 還可以定義在父容器 Host 中,Host 不是必須的,但是要運行 war 程序,就必須要 Host,因為 war 中必有 web.xml 文件,這個文件的解析就需要 Host 了,如果要有多個 Host 就要定義一個 top 容器 Engine 了。而 Engine 沒有父容器了,一個 Engine 代表一個完整的 Servlet 引擎。
當 Connector 接受到一個連接請求時,將請求交給 Container,Container 是如何處理這個請求的?這四個組件是怎么分工的,怎么把請求傳給特定的子容器的呢?又是如何將最終的請求交給 Servlet 處理。下面是這個過程的時序圖:

參考