溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Tomcat架構(gòu)及啟動(dòng)過程是怎樣的

發(fā)布時(shí)間:2021-12-31 16:30:03 來源:億速云 閱讀:118 作者:iii 欄目:服務(wù)器

這篇文章主要講解了“Tomcat架構(gòu)及啟動(dòng)過程是怎樣的”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Tomcat架構(gòu)及啟動(dòng)過程是怎樣的”吧!

Tomcat-9.0.0.M22 是 Tomcat 目前最新的版本,但尚未發(fā)布,它實(shí)現(xiàn)了 Servlet4.0 及 JSP2.3 并提供了很多新特性,需要  1.8 及以上的 JDK 支持等等,詳情請(qǐng)查閱 Tomcat-9.0-doc

Tomcat-9.0-dochttps://tomcat.apache.org/tomcat-9.0-doc/index.html

Overview

Tomcat架構(gòu)及啟動(dòng)過程是怎樣的
  • Bootstrap 作為 Tomcat 對(duì)外界的啟動(dòng)類,在 $CATALINA_BASE/bin 目錄下,它通過反射創(chuàng)建 Catalina  的實(shí)例并對(duì)其進(jìn)行初始化及啟動(dòng)。

  • Catalina 解析 $CATALINA_BASE/conf/server.xml 文件并創(chuàng)建  StandardServer、StandardService、StandardEngine、StandardHost 等

  • StandardServer 代表的是整個(gè) Servlet 容器,他包含一個(gè)或多個(gè) StandardService

  • StandardService 包含一個(gè)或多個(gè) Connector,和一個(gè) Engine,Connector 和 Engine 都是在解析  conf/server.xml 文件時(shí)創(chuàng)建的,Engine 在 Tomcat 的標(biāo)準(zhǔn)實(shí)現(xiàn)是 StandardEngine

  • MapperListener 實(shí)現(xiàn)了 LifecycleListener 和 ContainerListener  接口用于監(jiān)聽容器事件和生命周期事件。該監(jiān)聽器實(shí)例監(jiān)聽所有的容器,包括  StandardEngine、StandardHost、StandardContext、StandardWrapper,當(dāng)容器有變動(dòng)時(shí),注冊(cè)容器到  Mapper。

  • Mapper 維護(hù)了 URL 到容器的映射關(guān)系。當(dāng)請(qǐng)求到來時(shí)會(huì)根據(jù) Mapper 中的映射信息決定將請(qǐng)求映射到哪一個(gè)  Host、Context、Wrapper。

  • Http11NioProtocol 用于處理 HTTP/1.1 的請(qǐng)求

  • NioEndpoint 是連接的端點(diǎn),在請(qǐng)求處理流程中該類是核心類,會(huì)重點(diǎn)介紹。

  • CoyoteAdapter 用于將請(qǐng)求從 Connctor 交給 Container 處理。使 Connctor 和 Container 解耦。

  • StandardEngine 代表的是 Servlet 引擎,用于處理 Connector 接受的 Request。包含一個(gè)或多個(gè) Host(虛擬主機(jī)),  Host 的標(biāo)準(zhǔn)實(shí)現(xiàn)是 StandardHost。

  • StandardHost 代表的是虛擬主機(jī),用于部署該虛擬主機(jī)上的應(yīng)用程序。通常包含多個(gè) Context (Context 在 Tomcat  中代表應(yīng)用程序)。Context 在 Tomcat 中的標(biāo)準(zhǔn)實(shí)現(xiàn)是 StandardContext。

  • StandardContext 代表一個(gè)獨(dú)立的應(yīng)用程序,通常包含多個(gè) Wrapper,一個(gè) Wrapper 容器封裝了一個(gè)  Servlet,Wrapper的標(biāo)準(zhǔn)實(shí)現(xiàn)是 StandardWrapper。

  • StandardPipeline 組件代表一個(gè)流水線,與 Valve(閥)結(jié)合,用于處理請(qǐng)求。 StandardPipeline 中含有多個(gè) Valve,  當(dāng)需要處理請(qǐng)求時(shí),會(huì)逐一調(diào)用 Valve 的 invoke 方法對(duì) Request 和 Response 進(jìn)行處理。特別的,其中有一個(gè)特殊的 Valve 叫  basicValve,每一個(gè)標(biāo)準(zhǔn)容器都有一個(gè)指定的 BasicValve,他們做的是最核心的工作。

  • StandardEngine 的是 StandardEngineValve,他用來將 Request 映射到指定的 Host;

  • StandardHost 的是 StandardHostValve, 他用來將 Request 映射到指定的 Context;

  • StandardContext 的是 StandardContextValve,它用來將 Request 映射到指定的 Wrapper;

  • StandardWrapper 的是 StandardWrapperValve,他用來加載 Rquest 所指定的 Servlet,并調(diào)用 Servlet  的 Service 方法。

Tomcat init

Tomcat架構(gòu)及啟動(dòng)過程是怎樣的
  • 當(dāng)通過 ./startup.sh 腳本或直接通過 java 命令來啟動(dòng) Bootstrap 時(shí),Tomcat 的啟動(dòng)過程就正式開始了,啟動(dòng)的入口點(diǎn)就是  Bootstrap 類的 main 方法。

  • 啟動(dòng)的過程分為兩步,分別是 init 和 start,本節(jié)主要介紹 init;

  • 初始化類加載器。[關(guān)于 Tomcat 類加載機(jī)制,可以參考我之前寫的一片文章:談?wù)凧ava類加載機(jī)制]

  1. 通過從 CatalinaProperties 類中獲取 common.loader 等屬性,獲得類加載器的掃描倉庫。CatalinaProperties  類在的靜態(tài)塊中調(diào)用了 loadProperties() 方法,從 conf/catalina.properties  文件中加載了屬性.(即在類創(chuàng)建的時(shí)候?qū)傩跃鸵呀?jīng)加載好了)。

  2. 通過 ClassLoaderFactory 創(chuàng)建 URLClassLoader 的實(shí)例

  • 通過反射創(chuàng)建 Catalina 的實(shí)例并設(shè)置 parentClassLoader

  • setAwait(true)。設(shè)置 Catalina 的 await 屬性為 true。在 Start 階段尾部,若該屬性為 true,Tomcat 會(huì)在  main 線程中監(jiān)聽 SHUTDOWN 命令,默認(rèn)端口是 8005.當(dāng)收到該命令后執(zhí)行 Catalina 的 stop() 方法關(guān)閉 Tomcat  服務(wù)器。

  • createStartDigester()。Catalina 的該方法用于創(chuàng)建一個(gè) Digester 實(shí)例,并添加解析 conf/server.xml 的  RuleSet。Digester 原本是 Apache 的一個(gè)開源項(xiàng)目,專門解析 XML 文件的,但我看 Tomcat-9.0.0.M22 中直接將這些類整合到  Tomcat 內(nèi)部了,而不是引入 jar 文件。Digester 工具的原理不在本文的介紹范圍,有興趣的話可以參考 The Digester Component  – Apache 或 《How Tomcat works》- Digester [推薦] 一章

  • parse() 方法就是 Digester 處理 conf/server.xml  創(chuàng)建各個(gè)組件的過程。值的一提的是這些組件都是使用反射的方式來創(chuàng)建的。特別的,在創(chuàng)建 Digester 的時(shí)候,添加了一些特別的 rule  Set,用于創(chuàng)建一些十分核心的組件,這些組件在 conf/server.xml 中沒有但是其作用都比較大,這里做下簡單介紹,當(dāng) Start  時(shí)用到了再詳細(xì)說明:

  1. EngineConfig。LifecycleListener 的實(shí)現(xiàn)類,觸發(fā) Engine  的生命周期事件后調(diào)用,這個(gè)監(jiān)聽器沒有特別大的作用,就是打印一下日志

  2. HostConfig。LifecycleListener 的實(shí)現(xiàn)類,觸發(fā) Host 的生命周期事件后調(diào)用。這個(gè)監(jiān)聽器的作用就是部署應(yīng)用程序,這包括  conf/

    // 目錄下所有的 Context xml 文件 和 webapps 目錄下的應(yīng)用程序,不管是 war  文件還是已解壓的目錄。 另外后臺(tái)進(jìn)程對(duì)應(yīng)用程序的熱部署也是由該監(jiān)聽器負(fù)責(zé)的。
  3. ContextConfig。LifecycleListener 的實(shí)現(xiàn)類,觸發(fā) Context  的生命周期事件時(shí)調(diào)用。這個(gè)監(jiān)聽器的作用是配置應(yīng)用程序,它會(huì)讀取并合并 conf/web.xml 和 應(yīng)用程序的 web.xml,分析  /WEB-INF/classes/ 和 /WEB-INF/lib/*.jar中的 Class 文件的注解,將其中所有的  Servlet、ServletMapping、Filter、FilterMapping、Listener 都配置到 StandardContext  中,以備后期使用。當(dāng)然了 web.xml 中還有一些其他的應(yīng)用程序參數(shù),最后都會(huì)一并配置到 StandardContext 中。

  • reconfigureStartStopExecutor() 用于重新配置啟動(dòng)和停止子容器的 Executor。默認(rèn)是 1 個(gè)線程。我們可以配置  conf/server.xml 中 Engine 的 startStopThreads,來指定用于啟動(dòng)和停止子容器的線程數(shù)量,如果配置 0 的話會(huì)使用  Runtime.getRuntime().availableProcessors() 作為線程數(shù),若配置為負(fù)數(shù)的話會(huì)使用  Runtime.getRuntime().availableProcessors() + 配置值,若和小與 1 的話,使用 1 作為線程數(shù)。當(dāng)線程數(shù)是 1  時(shí),使用 InlineExecutorService 它直接使用當(dāng)前線程來執(zhí)行啟動(dòng)停止操作,否則使用 ThreadPoolExecutor  來執(zhí)行,其最大線程數(shù)為我們配置的值。

  • 需要注意的是 Host 的 init 操作是在 Start 階段來做的, StardardHost 創(chuàng)建好后其 state 屬性的默認(rèn)值是  LifecycleState.NEW,所以在其調(diào)用 startInternal() 之前會(huì)進(jìn)行一次初始化。

Tomcat Start[Deployment]

Tomcat架構(gòu)及啟動(dòng)過程是怎樣的
  • 圖中從 StandardHost Start StandardContext 的這步其實(shí)在真正的執(zhí)行流程中會(huì)直接跳過,因?yàn)?conf/server.xml  文件中并沒有配置任何的 Context,所以在 findChildren() 查找子容器時(shí)會(huì)返回空數(shù)組,所以之后遍歷子容器來啟動(dòng)子容器的 for  循環(huán)就直接跳過了。

  • 觸發(fā) Host 的 BEFORE_START_EVENT 生命周期事件,HostConfig 調(diào)用其 beforeStart() 方法創(chuàng)建  $CATALINA_BASE/webapps& $CATALINA_BASE/conf/

    // 目錄。
  • 觸發(fā) Host 的 START_EVENT 生命周期事件,HostConfig 調(diào)用其 start() 方法開始部署已在  $CATALINA_BASE/webapps & $CATALINA_BASE/conf/

    // 目錄下的應(yīng)用程序。
  1. 解析 $CATALINA_BASE/conf/

    // 目錄下所有定義 Context 的 XML 文件,并添加到  StandardHost。這些 XML 文件稱為應(yīng)用程序描述符。正因?yàn)槿绱?,我們可以配置一個(gè)虛擬路徑來保存應(yīng)用程序中用到的圖片,詳細(xì)的配置過程請(qǐng)參考  開發(fā)環(huán)境配置指南 – 6.3. 配置圖片存放目錄
  2. 部署 $CATALINA_BASE/webapps 下所有的 WAR 文件,并添加到 StandardHost。

  3. 部署 $CATALINA_BASE/webapps 下所有已解壓的目錄,并添加到 StandardHost。

特別的,添加到 StandardHost 時(shí),會(huì)直接調(diào)用 StandardContext 的 start() 方法來啟動(dòng)應(yīng)用程序。啟動(dòng)應(yīng)用程序步驟請(qǐng)看  Context Start 一節(jié)。

  • 在 StandardEngine 和 StandardContext 啟動(dòng)時(shí)都會(huì)調(diào)用各自的 threadStart()  方法,該方法會(huì)創(chuàng)建一個(gè)新的后臺(tái)線程來處理該該容器和子容器及容器內(nèi)各組件的后臺(tái)事件。StandardEngine  會(huì)直接創(chuàng)建一個(gè)后臺(tái)線程,StandardContext 默認(rèn)是不創(chuàng)建的,和 StandardEngine 共用同一個(gè)。后臺(tái)線程處理機(jī)制是周期調(diào)用組件的  backgroundProcess() 方法。詳情請(qǐng)看 Background process 一節(jié)。

  • MapperListener

  1. addListeners(engine) 方法會(huì)將該監(jiān)聽器添加到 StandardEngine 和它的所有子容器中

  2. registerHost() 會(huì)注冊(cè)所有的 Host 和他們的子容器到 Mapper 中,方便后期請(qǐng)求處理時(shí)使用。

  3. 當(dāng)有新的應(yīng)用(StandardContext)添加進(jìn)來后,會(huì)觸發(fā) Host 的容器事件,然后通過 MapperListener 將新應(yīng)用的映射注冊(cè)到  Mapper 中。

  • Start 工作都做完以后 Catalina 會(huì)創(chuàng)建一個(gè) CatalinaShutdownHook 并注冊(cè)到  JVM。CatalinaShutdownHook 繼承了 Thread,是 Catalina 的內(nèi)部類。其 run 方法中直接調(diào)用了 Catalina 的  stop() 方法來關(guān)閉整個(gè)服務(wù)器。注冊(cè)該 Thread 到 JVM 的原因是防止用戶非正常終止  Tomcat,比如直接關(guān)閉命令窗口之類的。當(dāng)直接關(guān)閉命令窗口時(shí),操作系統(tǒng)會(huì)向 JVM 發(fā)送一個(gè)終止信號(hào),然后 JVM 在退出前會(huì)逐一啟動(dòng)已注冊(cè)的  ShutdownHook 來關(guān)閉相應(yīng)資源。

Context Start

Tomcat架構(gòu)及啟動(dòng)過程是怎樣的
  • StandRoot 類實(shí)現(xiàn)了 WebResourceRoot 接口,它容納了一個(gè)應(yīng)用程序的所有資源,通俗的來說就是部署到 webapps 目錄下對(duì)應(yīng)  Context 的目錄里的所有資源。因?yàn)槲覍?duì) Tomcat 的資源管理部分暫時(shí)不是很感興趣,所以資源管理相關(guān)類只是做了簡單了解,并沒有深入研究源代碼。

  • resourceStart() 方法會(huì)對(duì) StandardRoot 進(jìn)行初始配置

  • postWorkDirectory() 用于創(chuàng)建對(duì)應(yīng)的工作目錄  $CATALINA_BASE/work/

    //, 該目錄用于存放臨時(shí)文件。
  • StardardContext 只是一個(gè)容器,而 ApplicationContext  則是一個(gè)應(yīng)用程序真正的運(yùn)行環(huán)境,相關(guān)類及操作會(huì)在請(qǐng)求處理流程看完以后進(jìn)行補(bǔ)充。

  • StardardContext 觸發(fā) CONFIGURE_START_EVENT 生命周期事件,ContextConfig 開始調(diào)用  configureStart() 對(duì)應(yīng)用程序進(jìn)行配置。

  1. 這個(gè)過程會(huì)解析并合并 conf/web.xml & conf/

    //web.xml.default &  webapps//WEB-INF/web.xml 中的配置。
  2. 配置配置文件中的參數(shù)到 StandardContext, 其中主要的包括 Servlet、Filter、Listener。

  3. 因?yàn)閺?Servlet3.0 以后是直接支持注解的,所以服務(wù)器必須能夠處理加了注解的類。Tomcat 通過分析 WEB-INF/classes/ 中的  Class 文件和 WEB-INF/lib/ 下的 jar 包將掃描到的 Servlet、Filter、Listerner 注冊(cè)到  StandardContext。

  4. setConfigured(true),是非常關(guān)鍵的一個(gè)操作,它標(biāo)識(shí)了 Context 的成功配置,若未設(shè)置該值為 true 的話,Context  會(huì)啟動(dòng)失敗。

Background process

Tomcat架構(gòu)及啟動(dòng)過程是怎樣的
  • 后臺(tái)進(jìn)程的作用就是處理一下 Servlet 引擎中的周期性事件,處理周期默認(rèn)是 10s。

  • 特別的 StandardHost 的 backgroundProcess() 方法會(huì)觸發(fā) Host 的 PERIODIC_EVENT 生命周期事件。然后  HostConfig 會(huì)調(diào)用其 check() 方法對(duì)已加載并進(jìn)行過重新部署的應(yīng)用程序進(jìn)行 reload  或?qū)π虏渴鸬膽?yīng)用程序進(jìn)行熱部署。熱部署跟之前介紹的部署步驟一致, reload() 過程只是簡單的順序調(diào)用  setPause(true)、stop()、start()、setPause(false),其中 setPause(true)  的作用是暫時(shí)停止接受請(qǐng)求。

How to read excellent open source projects

真正的第一次閱讀開源項(xiàng)目源代碼,收獲還是很大的。讓我在架構(gòu)設(shè)計(jì)、面向?qū)ο笏枷?、設(shè)計(jì)模式、Clean  Code等等各個(gè)方面都有了進(jìn)步。閱讀優(yōu)秀的開源項(xiàng)目其實(shí)是一件很爽的事,因?yàn)闀r(shí)不時(shí)的會(huì)發(fā)現(xiàn)一個(gè)新的設(shè)計(jì)思路,然后不由自主的感嘆一聲居然還可以這樣!當(dāng)然了,讀的時(shí)候還是會(huì)有一些痛點(diǎn)的,比如說碰到一個(gè)變量,但是死活就是找不到初始化的位置,有時(shí)通過  Find Usage  工具可以找到,但有些找不到的只能從頭開始再過一邊源碼。有時(shí)碰到一個(gè)設(shè)計(jì)思路死活都想不明白為什么這樣設(shè)計(jì)等等,這種情況就只能通過分析更高一層的架構(gòu)來解決了等等。

下面我簡單分享一下我是如何閱讀開源項(xiàng)目源碼的。

  • 先找一些介紹該項(xiàng)目架構(gòu)的書籍來看,項(xiàng)目架構(gòu)是項(xiàng)目核心中的核心,讀架構(gòu)讀的是高層次的設(shè)計(jì)思路,讀源碼讀的是低層次的實(shí)現(xiàn)細(xì)節(jié)。有了高層次的設(shè)計(jì)思路做指導(dǎo),源碼讀起來才會(huì)得心應(yīng)手,因?yàn)樽x的時(shí)候心里很清楚現(xiàn)在在讀的源碼在整個(gè)項(xiàng)目架構(gòu)中處于什么位置。我在讀  Tomcat 源碼之前先把 《How Tomcat works》 一書過了一邊,然后又看了一下 《Tomcat 架構(gòu)解析》 的第二章,對(duì) Tomcat  的架構(gòu)有了初步了解。(PS:《How Tomcat works》一書是全英文的,但讀起來非常流暢,雖然它是基于 Tomcat 4 和 5 的,但 Tomcat  架構(gòu)沒有非常大的變化,新版的 Tomcat 只是增加了一些組件,如果你要學(xué)習(xí) Tomcat 的話,首推這本書!)

  • 如果實(shí)在找不到講架構(gòu)的書,那就自己動(dòng)手畫類圖吧!一般來說,開源項(xiàng)目都是為了提供服務(wù)的,我們把提供服務(wù)的流程作為主線來分析源代碼,這樣目的性會(huì)更強(qiáng)一些,將該流程中涉及到的類畫到類圖中,最后得到的類圖就是架構(gòu)!不過分析之前你要先找到流程的入口點(diǎn),否則分析就無從開始。以  Tomcat 為例,他的主線流程大致可以分為 3 個(gè):啟動(dòng)、部署、請(qǐng)求處理。他們的入口點(diǎn)就是 Bootstrap 類和 接受請(qǐng)求的 Acceptor  類!

  • 有了閱讀思路我們下面來說說工具吧。我使用的閱讀工具是 IntelliJ IDEA,一款十分強(qiáng)大的 IDE,可能比較重量級(jí),如果你有其他更加輕量級(jí)的  Linux 平臺(tái)源碼閱讀工具,可以推薦給我~

  1. Structure  欄目可以自定義列出類中的域、方法,然后還可以按照繼承結(jié)構(gòu)對(duì)域和方法進(jìn)行分組,這樣就可以直接看出來域和方法是在繼承結(jié)構(gòu)中哪個(gè)類里定義的。當(dāng)你點(diǎn)擊方法和域時(shí),還可以自動(dòng)滾動(dòng)到源代碼等等。

  2. 在源代碼中 點(diǎn)擊右鍵 -> Diagrams -> show Diagram  可以顯示類的繼承結(jié)構(gòu),圖中包含了該類所有的祖先和所有的接口。在該圖中選擇指定的父類和接口,點(diǎn)擊右鍵 -> show Implementations,  IDEA 會(huì)列出接口的實(shí)現(xiàn)類或該類的子類。

  3. FindUsage、Go To Declaration 等等就不再多說了。

感謝各位的閱讀,以上就是“Tomcat架構(gòu)及啟動(dòng)過程是怎樣的”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Tomcat架構(gòu)及啟動(dòng)過程是怎樣的這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI