萬字詳解 Tomcat 組成與工作原理



Server:指的就是整個(gè) Tomcat 服 務(wù)器,包含多組服務(wù),負(fù)責(zé)管理和 啟動(dòng)各個(gè) Service,同時(shí)監(jiān)聽 8005 端口發(fā)過來的 shutdown 命令,用于關(guān)閉整個(gè)容器;
Service:Tomcat 封裝的、對外提供完整的、基于組件的 Web 服務(wù), 包含 Connectors、Container 兩個(gè)核心組件,以及多個(gè)功能組件,各個(gè) Service 之間是獨(dú)立的,但是共享 同一 JVM 的資源;
Connector:Tomcat 與外部世界的連接器,監(jiān)聽固定端口接收外部請求,傳遞給 Container,并將 Container 處理的結(jié)果返回給外部;
Container:Catalina,Servlet 容器,內(nèi)部有多層容器組成,用于管理 Servlet 生命周期,調(diào)用 servlet 相關(guān)方法;
Loader:封裝了 Java ClassLoader,用于 Container 加載類文件;
Realm:Tomcat 中為 Web 應(yīng)用程序提供訪問認(rèn)證和角色管理的機(jī)制;
JMX:Java SE 中定義技術(shù)規(guī)范,是一個(gè)為應(yīng)用程序、設(shè)備、系統(tǒng)等植入管理功能的框架,通過 JMX 可以遠(yuǎn)程監(jiān)控 Tomcat 的運(yùn)行狀態(tài);
Jasper:Tomcat 的 JSP 解析引擎,用于將 JSP 轉(zhuǎn)換成 Java 文件,并編譯成 class 文件。
Session:負(fù)責(zé)管理和創(chuàng)建 Session,以及 Session 的持久化(可自定義),支持 Session 的集 群。
Pipeline:在容器中充當(dāng)管道的作用,管道中可以設(shè)置各種 valve(閥門),請求和響應(yīng)在經(jīng)由管道中各個(gè)閥門處理,提供了一種靈活可配置的處理請求和響應(yīng)的機(jī)制。
Naming:命名服務(wù),JNDI, Java 命名和目錄接口,是一組在 Java 應(yīng)用中訪問命名和目錄服務(wù)的 API。命名服務(wù)將名稱和對象聯(lián)系起來,使得我們可以用名稱訪問對象,目錄服務(wù)也是一種命名 服務(wù),對象不但有名稱,還有屬性。Tomcat 中可以使用 JNDI 定義數(shù)據(jù)源、配置信息,用于開發(fā)與部署的分離。

Engine:Servlet 的頂層容器,包含一 個(gè)或多個(gè) Host 子容器;
Host:虛擬主機(jī),負(fù)責(zé) Web 應(yīng)用的部 署和 Context 的創(chuàng)建;
Context:Web 應(yīng)用上下文,包含多個(gè) Wrapper,負(fù)責(zé) Web 配置的解析、管 理所有的 Web 資源;
Wrapper:最底層的容器,是對 Servlet 的封裝,負(fù)責(zé) Servlet 實(shí)例的創(chuàng) 建、執(zhí)行和銷毀。

EngineConfig:主要打印啟動(dòng)和停止日志
HostConfig:主要處理部署應(yīng)用,解析應(yīng)用 META-INF/context.xml 并創(chuàng)建應(yīng)用的 Context
ContextConfig:主要解析并合并 web.xml,掃描應(yīng)用的各類 Eeb 資源(filter、servlet、listener)


List
>?results?=?new?ArrayList >();
for?(int?i?=?0;?i?????results.add(startStopExecutor.submit(new?StartChild(children[i])));
}
boolean?fail?=?false;
for?(Futureresult :results)?{
????try?{
????????result.get();
????}?catch?(Exception?e)?{
????????log.error(sm.getString("containerBase.threadedStartFailed"),?e);
????????fail?=?true;
????}
}
catalina.home:安裝目錄
catalina.base:工作目錄
默認(rèn)值:user.dir
Server.xml 配置 Host 元素,指定 appBase 屬性,默認(rèn) $catalina.base/webapps/
Server.xml 配置 Context 元素,指定 docBase,元素,指定 Web 應(yīng)用的路徑
自定義配置在 $catalina.base/EngineName/HostName/XXX.xml 配置 Context 元素
掃描 appbase 路徑下的所有文件夾和 war 包,解析各個(gè)應(yīng)用的 META-INF/context.xml,并創(chuàng)建 StandardContext,并將 Context 加入到 Host 的子容器中。
解析 $catalina.base/EngineName/HostName/ 下的所有 Context 配置,找到相應(yīng) Web 應(yīng)用的位置,解析各個(gè)應(yīng)用的 META-INF/context.xml,并創(chuàng)建 StandardContext,并將 Context 加入到 Host 的子容器中。
HostConfig 并沒有實(shí)際解析 Context.xml,而是在 ContextConfig 中進(jìn)行的。
HostConfig 中會定期檢查 watched 資源文件(context.xml 配置文件)
先解析全局的配置 config/context.xml
然后解析 Host 的默認(rèn)配置 EngineName/HostName/context.xml.default
最后解析應(yīng)用的 META-INF/context.xml
先解析全局的配置 config/web.xml
然后解析 Host 的默認(rèn)配置 EngineName/HostName/web.xml.default 接著解析應(yīng)用的 MEB-INF/web.xml
掃描應(yīng)用 WEB-INF/lib/ 下的 jar 文件,解析其中的 META-INF/web-fragment.xml 最后合并 xml 封裝成 WebXml,并設(shè)置 Context
掃描 Web 應(yīng)用和 jar 中的注解(Filter、Listener、Servlet)就是上述步驟中進(jìn)行的。
容器的定期執(zhí)行:backgroundProcess,由 ContainerBase 來實(shí)現(xiàn)的,并且只有在頂層容器中才會開啟線程。(backgroundProcessorDelay=10 標(biāo)志位來控制)

請求到達(dá) server 端,server 根據(jù) url 映射到相應(yīng)的 Servlet
判斷 Servlet 實(shí)例是否存在,不存在則加載和實(shí)例化 Servlet 并調(diào)用 init 方法
Server 分別創(chuàng)建 Request 和 Response 對象,調(diào)用 Servlet 實(shí)例的 service 方法(service 方法內(nèi)部會根據(jù) http 請求方法類型調(diào)用相應(yīng)的 doXXX 方法)
doXXX 方法內(nèi)為業(yè)務(wù)邏輯實(shí)現(xiàn),從 Request 對象獲取請求參數(shù),處理完畢之后將結(jié)果通過 response 對象返回給調(diào)用方
當(dāng) Server 不再需要 Servlet 時(shí)(一般當(dāng) Server 關(guān)閉時(shí)),Server 調(diào)用 Servlet 的 destroy() 方法。

根據(jù) server.xml 配置的指定的 connector 以及端口監(jiān)聽 http、或者 ajp 請求
請求到來時(shí)建立連接,解析請求參數(shù),創(chuàng)建 Request 和 Response 對象,調(diào)用頂層容器 Pipeline 的 invoke 方法
容器之間層層調(diào)用,最終調(diào)用業(yè)務(wù) servlet 的 service 方法
Connector 將 response 流中的數(shù)據(jù)寫到 socket 中


編譯階段:servlet 容器編譯 servlet 源文件,生成 servlet 類
初始化階段:加載與 JSP 對應(yīng)的 servlet 類,創(chuàng)建其實(shí)例,并調(diào)用它的初始化方法
執(zhí)行階段:調(diào)用與 JSP 對應(yīng)的 servlet 實(shí)例的服務(wù)方法
銷毀階段:調(diào)用與 JSP 對應(yīng)的 servlet 實(shí)例的銷毀方法,然后銷毀 servlet 實(shí)例
代碼片段:<% 代碼片段 %>
JSP聲明:<%! declaration; [ declaration; ]+ ... %>
JSP表達(dá)式:<%= 表達(dá)式 %>
JSP注釋:<%-- 注釋 --%>
JSP指令:<%@ directive attribute=“value” %>
JSP行為:
HTML元素:html/head/body/div/p/……
JSP隱式對象:request、response、out、session、application、config、 pageContext、page、Exception
代碼片段:包含任意量的 Java 語句、變量、方法或表達(dá)式
JSP 聲明:一個(gè)聲明語句可以聲明一個(gè)或多個(gè)變量、方法,供后面的 Java 代碼使用
JSP 表達(dá)式:輸出 Java 表達(dá)式的值,String 形式;
JSP 注釋:為代碼作注釋以及將某段代碼注釋掉
JSP 指令:用來設(shè)置與整個(gè) JSP 頁面相關(guān)的屬性:
<%@ page ... %> 定義頁面的依賴屬性,比如 language、contentType、errorPage、 isErrorPage、import、isThreadSafe、session 等等
<%@ include ... %> 包含其他的 JSP 文件、HTML 文件或文本文件,是該 JSP 文件的一部分,會被同時(shí)編譯執(zhí)行
<%@ taglib ... %> 引入標(biāo)簽庫的定義,可以是自定義標(biāo)簽
JSP 行為:jsp:include、jsp:useBean、jsp:setProperty、jsp:getProperty、jsp:forward

代碼片段:在 _jspService() 方法內(nèi)直接輸出
JSP 聲明:在 servlet 類中進(jìn)行輸出
JSP 表達(dá)式:在 _jspService() 方法內(nèi)直接輸出
JSP 注釋:直接忽略,不輸出
JSP 指令:根據(jù)不同指令進(jìn)行區(qū)分,include:對引入的文件進(jìn)行解析;page 相關(guān)的屬性會做為 JSP 的屬性,影響的是解析和請求處理時(shí)的行為
JSP 行為:不同的行為有不同的處理方式,jsp:useBean 為例,會從 pageContext 根據(jù) scope 的 類別獲取 bean 對象,如果沒有會創(chuàng)建 bean,同時(shí)存到相應(yīng) scope 的 pageContext 中
HTML:在 _jspService() 方法內(nèi)直接輸出
JSP 隱式對象:在 _jspService() 方法會進(jìn)行聲明,只能在方法中使用

HTTP,HTTP 是超文本傳輸協(xié)議,是客戶端瀏覽器或其他程序與 Web 服務(wù)器之間的應(yīng)用層通信協(xié)議
AJP,Apache JServ 協(xié)議(AJP)是一種二進(jìn)制協(xié)議,專門代理從 Web 服務(wù)器到位于后端的應(yīng)用程序服務(wù)器的入站請求




JIO:用 java.io 編寫的 TCP 模塊,阻塞IO
NIO:用 java.nio 編寫的 TCP 模塊,非阻塞 IO,(IO 多路復(fù)用)
APR:全稱 Apache Portable Runtime,使用 JNI 的方式來進(jìn)行讀取文件以及進(jìn)行網(wǎng)絡(luò)傳輸
Support Polling:是否支持基于 IO 多路復(fù)用的 socket 事件輪詢
Polling Size:輪詢的最大連接數(shù)
Wait for next Request:在等待下一個(gè)請求時(shí),處理線程是否釋放,BIO 是沒有釋放的,所以在 keep-alive=true 的情況下處理的并發(fā)連接數(shù)有限
Read Request Headers:由于 request header 數(shù)據(jù)較少,可以由容器提前解析完畢,不需要阻塞
Read Request Body:讀取 request body 的數(shù)據(jù)是應(yīng)用業(yè)務(wù)邏輯的事情,同時(shí) Servlet 的限制,是需要阻塞讀取的
Write Response:跟讀取 request body 的邏輯類似,同樣需要阻塞寫



Begin:新的請求連接接入調(diào)用,可進(jìn)行與 Request 和 Response 相關(guān)的對象初始化操作,并保存 response 對象,用于后續(xù)寫入數(shù)據(jù)
Read:請求連接有數(shù)據(jù)可讀時(shí)調(diào)用
End:當(dāng)數(shù)據(jù)可用時(shí),如果讀取到文件結(jié)束或者 response 被關(guān)閉時(shí)則被調(diào)用
Error:在連接上發(fā)生異常時(shí)調(diào)用,數(shù)據(jù)讀取異常、連接斷開、處理異常、socket 超時(shí)
Read:在 post 請求有數(shù)據(jù),但在begin事件中沒有處理,則會調(diào)用read,如果read沒有讀取數(shù)據(jù),在會觸發(fā)Error回調(diào),關(guān)閉socket
End:當(dāng)socket超時(shí),并且response被關(guān)閉時(shí)也會調(diào)用;server被關(guān)閉時(shí)調(diào)用
Error:除了socket超時(shí)不會關(guān)閉socket,其他都會關(guān)閉socket
End和Error時(shí)間觸發(fā)時(shí)應(yīng)關(guān)閉當(dāng)前comet會話,即調(diào)用CometEvent的close方法 Note:在事件觸發(fā)時(shí)要做好線程安全的操作

首先,Servlet 接收到請求之后,request 數(shù)據(jù)解析;
接著,調(diào)用業(yè)務(wù)接口的某些方法,以完成業(yè)務(wù)處理;
最后,根據(jù)處理的結(jié)果提交響應(yīng),Servlet 線程結(jié)束。

客戶端發(fā)送一個(gè)請求
Servlet 容器分配一個(gè)線程來處理容器中的一個(gè) Servlet
Servlet 調(diào)用 request.startAsync(),保存 AsyncContext,然后返回
任何方式存在的容器線程都將退出,但是 response 仍然保持開放
業(yè)務(wù)線程使用保存的 AsyncContext 來完成響應(yīng)(線程池)
客戶端收到響應(yīng)
onStartAsync:Request 調(diào)用 startAsync 方法時(shí)觸發(fā)
onComplete:syncContext 調(diào)用 complete 方法時(shí)觸發(fā)
onError:處理請求的過程出現(xiàn)異常時(shí)觸發(fā)
onTimeout:socket 超時(shí)觸發(fā)
文章轉(zhuǎn)載:分布式實(shí)驗(yàn)室
(版權(quán)歸原作者所有,侵刪)
PS:如果覺得我的分享不錯(cuò),歡迎大家隨手點(diǎn)贊、在看。
(完) 加我"微信"?獲取一份 最新Java面試題資料 請備注:666,不然不通過~
最近好文
最近面試BAT,整理一份面試資料《Java面試BAT通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。 獲取方式:關(guān)注公眾號并回復(fù)?java?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。 明天見(??ω??)??
