Tomcat 組成與工作原理

Tomcat 組成與工作原理

Tomcat是什麼

開源的 Java Web 應用伺服器,實現了 Java EE(Java Platform Enterprise Edition)的部 分技術規範,比如 Java Servlet、Java Server Page、JSTL、Java WebSocket。Java EE 是 Sun 公 司為企業級應用推出的標準平臺,定義了一系列用於企業級開發的技術規範,除了上述的之外,還有 EJB、Java Mail、JPA、JTA、JMS 等,而這些都依賴具體容器的實現。

Tomcat 組成與工作原理

img

上圖對比了 Java EE 容器的實現情況,Tomcat 和 Jetty 都只提供了 Java Web 容器必需的 Servlet 和 JSP 規範,開發者要想實現其他的功能,需要自己依賴其他開源實現。

Glassfish 是由 sun 公司推出,Java EE 最新規範出來之後,首先會在 Glassfish 上進行實 現,所以是研究 Java EE 最新技術的首選。

最常見的情況是使用 Tomcat 作為 Java Web 伺服器,使用 Spring 提供的開箱即用的強大 的功能,並依賴其他開源庫來完成負責的業務功能實現。

Servlet容器

Tomcat 組成如下圖

:主要有 Container 和 Connector 以及相關元件構成。

Tomcat 組成與工作原理

img

Server

:指的就是整個 Tomcat 服 務器,包含多組服務,負責管理和 啟動各個 Service,同時監聽 8005 埠發過來的 shutdown 命令,用 於關閉整個容器 ;

Service

:Tomcat 封裝的、對外提 供完整的、基於元件的 web 服務, 包含 Connectors、Container 兩個 核心元件,以及多個功能元件,各 個 Service 之間是獨立的,但是共享 同一 JVM 的資源 ;

Connector

:Tomcat 與外部世界的聯結器,監聽固定埠接收外部請求,傳遞給 Container,並 將 Container 處理的結果返回給外部;

Container

:Catalina,Servlet 容器,內部有多層容器組成,用於管理 Servlet 生命週期,呼叫 servlet 相關方法。

Loader

:封裝了 Java ClassLoader,用於 Container 載入類檔案;Realm:Tomcat 中為 web 應用程式提供訪問認證和角色管理的機制;

JMX

:Java SE 中定義技術規範,是一個為應用程式、裝置、系統等植入管理功能的框架,透過 JMX 可以遠端監控 Tomcat 的執行狀態;

Jasper

:Tomcat 的 Jsp 解析引擎,用於將 Jsp 轉換成 Java 檔案,並編譯成 class 檔案。Session:負責管理和建立 session,以及 Session 的持久化(可自定義),支援 session 的集 群。

Pipeline

:在容器中充當管道的作用,管道中可以設定各種 valve(閥門),請求和響應在經由管 道中各個閥門處理,提供了一種靈活可配置的處理請求和響應的機制。

Naming

:命名服務,JNDI, Java 命名和目錄介面,是一組在 Java 應用中訪問命名和目錄服務的 API。命名服務將名稱和物件聯絡起來,使得我們可以用名稱訪問物件,目錄服務也是一種命名 服務,物件不但有名稱,還有屬性。Tomcat 中可以使用 JNDI 定義資料來源、配置資訊,用於開發 與部署的分離。

Container組成

Tomcat 組成與工作原理

img

Engine:Servlet 的頂層容器,包含一 個或多個 Host 子容器;Host:虛擬主機,負責 web 應用的部 署和 Context 的建立;Context:Web 應用上下文,包含多個 Wrapper,負責 web 配置的解析、管 理所有的 Web 資源;Wrapper:最底層的容器,是對 Servlet 的封裝,負責 Servlet 例項的創 建、執行和銷燬。

生命週期管理

Tomcat 為了方便管理元件和容器的生命週期,定義了從建立、啟動、到停止、銷燬共 12 中狀態,tomcat 生命週期管理了內部狀態變化的規則控制,元件和容器只需實現相應的生命週期 方法即可完成各生命週期內的操作(initInternal、startInternal、stopInternal、 destroyInternal);

比如執行初始化操作時,會判斷當前狀態是否 New,如果不是則丟擲生命週期異常;是的 話則設定當前狀態為 Initializing,並執行 initInternal 方法,由子類實現,方法執行成功則設定當 前狀態為 Initialized,執行失敗則設定為 Failed 狀態;

Tomcat 組成與工作原理

img

Tomcat 的生命週期管理引入了事件機制,在元件或容器的生命週期狀態發生變化時會通 知事件監聽器,監聽器透過判斷事件的型別來進行相應的操作。事件監聽器的新增可以在 server。xml 檔案中進行配置;

Tomcat 各類容器的配置過程就是透過新增 listener 的方式來進行的,從而達到配置邏輯與 容器的解耦。如 EngineConfig、HostConfig、ContextConfig。EngineConfig:主要列印啟動和停止日誌 HostConfig:主要處理部署應用,解析應用 META-INF/context。xml 並建立應用的 Context ContextConfig:主要解析併合並 web。xml,掃描應用的各類 web 資源 (filter、servlet、listener)

Tomcat 組成與工作原理

img

Tomcat 的啟動過程

Tomcat 組成與工作原理

img

啟動從 Tomcat 提供的 start。sh 指令碼開始,shell 指令碼會呼叫 Bootstrap 的 main 方法,實際 呼叫了 Catalina 相應的 load、start 方法。

load 方法會透過 Digester 進行 config/server。xml 的解析,在解析的過程中會根據 xml 中的關係 和配置資訊來建立容器,並設定相關的屬性。接著 Catalina 會呼叫 StandardServer 的 init 和 start 方法進行容器的初始化和啟動。

按照 xml 的配置關係,server 的子元素是 service,service 的子元素是頂層容器 Engine,每層容器有持有自己的子容器,而這些元素都實現了生命週期管理 的各個方法,因此就很容易的完成整個容器的啟動、關閉等生命週期的管理。

StandardServer 完成 init 和 start 方法呼叫後,會一直監聽來自 8005 埠(可配置),如果接收 到 shutdown 命令,則會退出迴圈監聽,執行後續的 stop 和 destroy 方法,完成 Tomcat 容器的 關閉。同時也會呼叫 JVM 的 Runtime。getRuntime()﴿。addShutdownHook 方法,在虛擬機器意外退 出的時候來關閉容器。

所有容器都是繼承自 ContainerBase,基類中封裝了容器中的重複工作,負責啟動容器相關的組 件 Loader、Logger、Manager、Cluster、Pipeline,啟動子容器(執行緒池併發啟動子容器,透過 執行緒池 submit 多個執行緒,呼叫後返回 Future 物件,執行緒內部啟動子容器,接著呼叫 Future 物件 的 get 方法來等待執行結果)。

Web 應用的部署方式

注:catalina。home:安裝目錄;catalina。base:工作目錄;預設值 user。dir

Server。xml 配置 Host 元素,指定 appBase 屬性,預設$catalina。base/webapps/

Server。xml 配置 Context 元素,指定 docBase,元素,指定 web 應用的路徑

自定義配置:在$catalina。base/EngineName/HostName/XXX。xml 配置 Context 元素

HostConfig 監聽了 StandardHost 容器的事件,在 start 方法中解析上述配置檔案:

掃描 appbase 路徑下的所有資料夾和 war 包,解析各個應用的 META-INF/context。xml,並 建立 StandardContext,並將 Context 加入到 Host 的子容器中。

解析$catalina。base/EngineName/HostName/下的所有 Context 配置,找到相應 web 應 用的位置,解析各個應用的 META-INF/context。xml,並建立 StandardContext,並將 Context 加入到 Host 的子容器中。

注:

HostConfig 並沒有實際解析 Context。xml,而是在 ContextConfig 中進行的。

HostConfig 中會定期檢查 watched 資原始檔(context。xml 配置檔案)

ContextConfig 解析 context。xml 順序:

先解析全域性的配置 config/context。xml

然後解析 Host 的預設配置 EngineName/HostName/context。xml。default

最後解析應用的 META-INF/context。xml

ContextConfig 解析 web。xml 順序:

先解析全域性的配置 config/web。xml

然後解析 Host 的預設配置 EngineName/HostName/web。xml。default 接著解析應用的 MEB-INF/web。xml

掃描應用 WEB-INF/lib/下的 jar 檔案,解析其中的 META-INF/web-fragment。xml 最後合併 xml 封裝成 WebXml,並設定 Context

注:

掃描 web 應用和 jar 中的註解(Filter、Listener、Servlet)就是上述步驟中進行的。

容器的定期執行:backgroundProcess,由 ContainerBase 來實現的,並且只有在頂層容器 中才會開啟執行緒。(backgroundProcessorDelay=10 標誌位來控制)

**Servlet 生命週期 **

Tomcat 組成與工作原理

img

Servlet 是用 Java 編寫的伺服器端程式。其主要功能在於互動式地瀏覽和修改資料,生成動態 Web 內容。

請求到達 server 端,server 根據 url 對映到相應的 Servlet

判斷 Servlet 例項是否存在,不存在則載入和例項化 Servlet 並呼叫 init 方法

Server 分別建立 Request 和 Response 物件,呼叫 Servlet 例項的 service 方法(service 方法 內部會根據 http 請求方法型別呼叫相應的 doXXX 方法)

doXXX 方法內為業務邏輯實現,從 Request 物件獲取請求引數,處理完畢之後將結果透過 response 物件返回給呼叫方

當 Server 不再需要 Servlet 時(一般當 Server 關閉時),Server 呼叫 Servlet 的 destroy() 方 法。

load on startup

當值為 0 或者大於 0 時,表示容器在應用啟動時就載入這個 servlet; 當是一個負數時或者沒有指定時,則指示容器在該 servlet 被選擇時才載入; 正數的值越小,啟動該 servlet 的優先順序越高;

single thread model

每次訪問 servlet,新建 servlet 實體物件,但並不能保證執行緒安全,同時 tomcat 會限制 servlet 的例項數目 最佳實踐:不要使用該模型,servlet 中不要有全域性變數

**請求處理過程  **

Tomcat 組成與工作原理

img

根據 server。xml 配置的指定的 connector 以及埠監聽 http、或者 ajp 請求

請求到來時建立連線,解析請求引數,建立 Request 和 Response 物件,呼叫頂層容器 pipeline 的 invoke 方法

容器之間層層呼叫,最終呼叫業務 servlet 的 service 方法

Connector 將 response 流中的資料寫到 socket 中

**Pipeline 與 Valve  **

Tomcat 組成與工作原理

img

Pipeline 可以理解為現實中的管道,Valve 為管道中的閥門,Request 和 Response 物件在管道中 經過各個閥門的處理和控制。

每個容器的管道中都有一個必不可少的 basic valve,其他的都是可選的,basic valve 在管道中最 後呼叫,同時負責呼叫子容器的第一個 valve。

Valve 中主要的三個方法:setNext、getNext、invoke;valve 之間的關係是單向鏈式結構,本身 invoke 方法中會呼叫下一個 valve 的 invoke 方法。

各層容器對應的 basic valve 分別是 StandardEngineValve、StandardHostValve、 StandardContextValve、StandardWrapperValve。

JSP引擎

img

JSP 生命週期

編譯階段:servlet 容器編譯 servlet 源文 件,生成 servlet 類

初始化階段:載入與 JSP 對應的 servlet 類, 建立其例項,並呼叫它的初始化方法

執行階段:呼叫與 JSP 對應的 servlet 例項的 服務方法

銷燬階段:呼叫與 JSP 對應的 servlet 例項的 銷燬方法,然後銷燬 servlet 例項

JSP元素

程式碼片段: JSP宣告: JSP表示式: JSP註釋: JSP指令:    JSP行為:    HTML元素:html/head/body/div/p/… JSP隱式物件:request、response、out、session、application、config、 pageContext、page、Exception

**JSP 元素說明 ** 程式碼片段:包含任意量的 Java 語句、變數、方法或表示式; JSP 宣告:一個宣告語句可以宣告一個或多個變數、方法,供後面的 Java 程式碼使用; JSP 表示式:輸出 Java 表示式的值,String 形式; JSP 註釋:為程式碼作註釋以及將某段程式碼註釋掉 JSP 指令:用來設定與整個 JSP 頁面相關的屬性, 定義頁面的依賴屬性,比如 language、contentType、errorPage、 isErrorPage、import、isThreadSafe、session 等等 包含其他的 JSP 檔案、HTML 檔案或文字檔案,是該 JSP 檔案的一部分,會 被同時編譯執行 引入標籤庫的定義,可以是自定義標籤 JSP 行為:jsp:include、jsp:useBean、jsp:setProperty、jsp:getProperty、jsp:forward

**Jsp 解析過程  **

Tomcat 組成與工作原理

img

程式碼片段:在_jspService()方法內直接輸出

JSP 宣告: 在 servlet 類中進行輸出

JSP 表示式:在_jspService()方法內直接輸出

JSP 註釋:直接忽略,不輸出

JSP 指令:根據不同指令進行區分,include:對引入的檔案進行解析;page 相關的屬性會做為 JSP 的屬性,影響的是解析和請求處理時的行為

JSP 行為:不同的行為有不同的處理方式,jsp:useBean 為例,會從 pageContext 根據 scope 的 類別獲取 bean 物件,如果沒有會建立 bean,同時存到相應 scope 的 pageContext 中

HTML:在_jspService()方法內直接輸出

JSP 隱式物件:在_jspService()方法會進行宣告,只能在方法中使用;

Connector

img

Http:HTTP 是超文字傳輸協議,是客戶端瀏覽器或其他程式與 Web 伺服器之間的應用層通訊協 議 AJP:Apache JServ 協議(AJP)是一種二進位制協議,專門代理從 Web 伺服器到位於後端的應用 程式伺服器的入站請求

阻塞 IO

Tomcat 組成與工作原理

img

非阻塞 IO

Tomcat 組成與工作原理

img

** IO多路複用**

Tomcat 組成與工作原理

img

阻塞與非阻塞的區別在於進行讀操作和寫操作的系統呼叫時,如果此時核心態沒有資料可讀或者沒有緩衝空間可寫時,是否阻塞。

IO多路複用的好處在於可同時監聽多個socket的可讀和可寫事件,這樣就能使得應用可以同時監聽多個socket,釋放了應用執行緒資源。

Tomcat各類Connector對比

Tomcat 組成與工作原理

img

Connector的實現模式有三種,分別是BIO、NIO、APR,可以在server。xml中指定。

JIO:用java。io編寫的TCP模組,阻塞IO

NIO:用java。nio編寫的TCP模組,非阻塞IO,(IO多路複用)

APR:全稱Apache Portable Runtime,使用JNI的方式來進行讀取檔案以及進行網路傳輸

Apache Portable Runtime是一個高度可移植的庫,它是Apache HTTP Server 2。x的核心。APR具有許多用途,包括訪問高階IO功能(如sendfile,epoll和OpenSSL),作業系統級功能(隨機數生成,系統狀態等)和本地程序處理(共享記憶體,NT管道和Unix套接字)。

表格中欄位含義說明:

Support Polling:是否支援基於IO多路複用的socket事件輪詢

Polling Size:輪詢的最大連線數

Wait for next Request:在等待下一個請求時,處理執行緒是否釋放,BIO是沒有釋放的,所以在keep-alive=true的情況下處理的併發連線數有限

Read Request Headers:由於request header資料較少,可以由容器提前解析完畢,不需要阻塞

Read Request Body:讀取request body的資料是應用業務邏輯的事情,同時Servlet的限制,是需要阻塞讀取的

Write Response:跟讀取request body的邏輯類似,同樣需要阻塞寫

NIO處理相關類

Tomcat 組成與工作原理

img

Acceptor執行緒負責接收連線,呼叫accept方法阻塞接收建立的連線,並對socket進行封裝成PollerEvent,指定註冊的事件為op_read,並放入到EventQueue佇列中,PollerEvent的run方法邏輯的是將Selector註冊到socket的指定事件;

Poller執行緒從EventQueue獲取PollerEvent,並執行PollerEvent的run方法,呼叫Selector的select方法,如果有可讀的Socket則建立Http11NioProcessor,放入到執行緒池中執行;

CoyoteAdapter是Connector到Container的介面卡,Http11NioProcessor呼叫其提供的service方法,內部建立Request和Response物件,並呼叫最頂層容器的Pipeline中的第一個Valve的invoke方法

Mapper主要處理http url 到servlet的對映規則的解析,對外提供map方法

NIO Connector主要引數

Tomcat 組成與工作原理

img

Comet

Comet是一種用於web的推送技術,能使伺服器實時地將更新的資訊傳送到客戶端,而無須客戶端發出請求 在WebSocket出來之前,如果不使用comet,只能透過瀏覽器端輪詢Server來模擬實現伺服器端推送。Comet支援servlet非同步處理IO,當連線上資料可讀時觸發事件,並非同步寫資料(阻塞)

Tomcat 組成與工作原理

img

Tomcat要實現Comet,只需繼承HttpServlet同時,實現CometProcessor介面

Begin:新的請求連線接入呼叫,可進行與Request和Response相關的物件初始化操作,並儲存response物件,用於後續寫入資料

Read:請求連線有資料可讀時呼叫

End:當資料可用時,如果讀取到檔案結束或者response被關閉時則被呼叫

Error:在連線上發生異常時呼叫,資料讀取異常、連線斷開、處理異常、socket超時

Note:

Read:在post請求有資料,但在begin事件中沒有處理,則會呼叫read,如果read沒有讀取資料,在會觸發Error回撥,關閉socket

End:當socket超時,並且response被關閉時也會呼叫;server被關閉時呼叫

Error:除了socket超時不會關閉socket,其他都會關閉socket

End和Error時間觸發時應關閉當前comet會話,即呼叫CometEvent的close方法 Note:在事件觸發時要做好執行緒安全的操作

非同步Servlet

img

傳統流程:

首先,Servlet 接收到請求之後,request資料解析;

接著,呼叫業務介面的某些方法,以完成業務處理;

最後,根據處理的結果提交響應,Servlet 執行緒結束

Tomcat 組成與工作原理

img

非同步處理流程:

客戶端傳送一個請求

Servlet容器分配一個執行緒來處理容器中的一個servlet

servlet呼叫request。startAsync(),儲存AsyncContext, 然後返回

任何方式存在的容器執行緒都將退出,但是response仍然保持開放

業務執行緒使用儲存的AsyncContext來完成響應(執行緒池)

客戶端收到響應

Servlet 執行緒將請求轉交給一個非同步執行緒來執行業務處理,執行緒本身返回至容器,此時 Servlet 還沒有生成響應資料,非同步執行緒處理完業務以後,可以直接生成響應資料(非同步執行緒擁有 ServletRequest 和 ServletResponse 物件的引用)

為什麼web應用中支援非同步?

推出非同步,主要是針對那些比較耗時的請求:比如一次緩慢的資料庫查詢,一次外部REST API呼叫, 或者是其他一些I/O密集型操作。這種耗時的請求會很快的耗光Servlet容器的執行緒池,繼而影響可擴充套件性。

Note:從客戶端的角度來看,request仍然像任何其他的HTTP的request-response互動一樣,只是耗費了更長的時間而已

非同步事件監聽

onStartAsync:Request呼叫startAsync方法時觸發

onComplete:syncContext呼叫complete方法時觸發

onError:處理請求的過程出現異常時觸發

onTimeout:socket超時觸發

Note : onError/ onTimeout觸發後,會緊接著回撥onComplete onComplete 執行後,就不可再操作request和response

最近面試BAT,整理一份面試資料《

Java面試BATJ通關手冊

》,覆蓋了Java核心技術、JVM、Java併發、SSM、微服務、資料庫、資料結構等等。

文章有幫助的話,在看,轉發吧。

謝謝支援喲 (*^__^*)

TAG: 容器呼叫JSPJavaxml