解讀tomcat之二:tomcat如何啓動

      對於engine, host, context來說,它們都屬於容器,當接收到客戶端請求的時候,請求會被傳遞到容器中,在一個容器中處理完畢之後,會被傳遞給下一個容器處理。因此,我們可以這樣理解tomcat,總的來說,tomcat就是一種自上而下,一個容器裏面又嵌套包含了另一個子容器的結構。所以,在tomcat啓動的時候,我們也可以想象,它必定要先啓動父容器,然後再啓動子容器,在啓動每一層容器的時候,還會啓動容器中的一些相關組件,當所有的容器與組件都安裝啓動完畢,那麼tomcat就啓動完畢了。

因此,很容易理解,tomcat 啓動的第一步就是進行容器的裝配,就是把父容器和子容器拼裝起來,並且安裝上相關的組件,這很像一個車間裝配的過程。

當一切裝配齊全,機器已經在各個工人的手中完全組裝好了,那麼接下來的一步,我們只需要按下開關,機器就可以工作啦。多麼方便哪!

1、一切事情的起點都源於org.apache.catalina.startup.Bootstrap的“引導”。Bootstrap負責對catalina的配置文件路徑進行了一番指導,指定了三種類型的classLoader,接下來catalina就可以用這三種類型的classLoader來負責裝配容器了。然後Bootstrap用反射機制調用了org.apache.catalina.startup.Catalina的process方法,引導catalina進行啓動。

2、Catalina的工作首先是用digester來裝配各個容器與組件(degester是Jakarta子項目Commons下的一個模塊,支持基於規則的對任意XML文檔的處理,提供了一種將XML與Java對象進行映射的方便方法),這個裝配就像我們上面說的那樣,就是把server下的service進行安裝,然後依次把service下的engine,host,context這些容器以及容器中的各種組件按照父子關係一一拼裝。這些配置文件的來源都是Bootstrap之間就已經告知了的。在這裏它只負責組裝。
接着,catalina會對server進行初始化工作,主要就是把service中配置的connector進行初始化(HTTP與AJP)。
然後調用server的start方法,啓動tomcat server。
最後,爲server註冊一個hook程序,檢測當server shutdown的時候,關閉tomcat的各個容器。

3、 進入server的start方法。
啓動server的容器的三個lifecycle事件:BEFORE_START_EVENT,START_EVENT,AFTER_START_EVENT。
啓動server的子容器service。

4、 進入service的start方法。啓動engine與connector。

5、 下面就開始進入engine了。
之前說過,engine, host與context都是容器,它們都繼承自Container類。它們既然都是一種container,那麼在處理手法上一定又很多類似的地方,因此,tomcat使用了ContainerBase這個類,把它作爲engine, host與context的父類,讓這些容器都可以通過super.start()方法來達到大部分主要邏輯的複用。

那麼,我們首先就來看一下這個ContainerBase中都做了些什麼,也就可以知道容器大致都怎麼處理請求的。
a) 觸發啓動前事件(BEFORE_START_EVENT)。
b) 設置標籤,表示該容器已啓動。
c) 啓動容器中的各個組件,如loader, logger, manager, cluster, realm, resources等。
d) 啓動當前容器的子容器。
e) 啓動當前容器的管道pipeline*。
f) 觸發啓動中事件(START_EVENT)。
g) 觸發啓動後事件(AFTER_START_EVENT)。
*:pipeline:當一個容器要把從上一級傳遞過來的需求轉交給子容器的時候,它就會把這個需求放進容器的管道(pipeline)裏面去,這個管道里面呢有多個閥門機關(value),而需求在管道里面流動的時候,就會被管道里面的各個閥門攔截下來,只有滿足了過關的要求,閥門纔會放行。比如管道里面放了兩個閥門,第一個閥門叫做“access_allow_vavle”,也就是說需求流過來的時候,它會看這個需求是哪個IP過來的,如果這個IP已經在黑名單裏面了,OK,立馬攔截,這個需求最遠就只能走到這裏了,不可能再往下走了!第二個閥門叫做“defaul_access_valve”,它會做例行的檢查,如果通過的話,OK,把需求傳遞給當前容器的子容器。 就是通過這種方式, 需求就在各個容器裏面傳遞,流動, 最後抵達目的地的了。

以上就是ContainerBase中進行的一些處理。儘管大部分內容都是共用的,但每個容器還是有一些自己特別的處理的,這些各個容器特有的任務都會放在調用ContainerBase之前進行處理。在engine中的特別處理包括engine自己的log以及mbean的處理等等。

6、 Host是engine的子容器,所以host也會調用ContainerBase的start()方法。
而host的特殊處理主要就是往pipeline裏面安裝了一個errorReportValue的閥門。這個errorReportValue的作用主要就是用來檢查response的。需求在被Engine傳遞給Host後, 會繼續傳遞給Context做具體的處理。 這裏需求其實就是作爲參數傳遞的Request, Response。所以在context把需求處理完後,通常會改動response。而這個org.apache.catalina.valves.ErrorReportValve的作用就是檢察response是否包含錯誤, 如果有就做相應的處理。

7、 終於到了Context了。Context的啓動是從 StandardContext的start()開始的。下面我們一步一步來看StandardContext的start()中都做了些什麼。

a) 觸發啓動前事件(BEFORE_START_EVENT)。
b) 設置web app的具體目錄webappResources。
c) 爲context指定loader,Loader就是用來指定這個context會用到哪些類啊,哪些jar包啊這些什麼的。
d)GetCharsetMapper(),得到字符編碼格式,tomcat自己有一個默認的配置文件用來設置默認情況下的字符編碼格式,如果用戶沒有自定義的話,就採用默認的配置,一般爲爲/org/apache/catalina/util/CharsetMapperDefault.properties。
e) postWorkDirectory (),創建臨時文件目錄。Tomcat下面有一個work目錄,用來存放臨時文件。這個步驟就是在那裏創建一個目錄,一般說來會在%CATALINA_HOME%/work/Standalone\localhost\這個地方生成一個目錄。
f) Binding thread(),負責綁定當前線程與context。首先要轉換class loader,因爲之前需要的是tomcat下的所有class和lib,接下來需要的就是當前context,也就是web app的class和lib了,所以要重新設置當前的的contextClassLoader,同時要記錄下舊的class loader。然後就要進行線程的綁定了。

Java代碼  收藏代碼
  1. threadBindings.put(Thread.currentThread(), context);  
  2. threadNameBindings.put(Thread.currentThread(), name);  


threadBindings和threadNameBindings都是HashTable,這兩步操作把當前線程與當前的這個context綁定起來。接下來這個線程就作爲這個web app的主線程了。
g) 啓動當前context的loader。
h) 重置logger並啓動它。
i) 若存在子容器,啓動子容器,並啓動其管道pipeline。
j) 觸發START_EVENT事件監聽,
Java代碼  收藏代碼
  1. lifecycle.fireLifecycleEvent(START_EVENT, null);  

在這個事件監聽裏面會啓動ContextConfig的start()事件,ContextConfig是用來配置web.xml的。比如這個Context有多少Servlet,又有多少Filter,就是在這裏給Context裝上去的。ContextConfig主要做了這些工作:

Java代碼  收藏代碼
  1. defaultWebConfig();    //每個context要配置一個默認的web.xml,就是omcat/conf/web.xml,這樣container servlet才能被加載。  
  2. applicationWebConfig();    //配置web app自己的web.xml  
  3. validateSecurityRoles();   //驗證訪問角色的安全性。就是web app的部署權限,通常我們會通過訪問/admin 或者/manager來進入應用的部署界面,一般用戶是admin或者manager才能訪問。訪問的用戶以及可以訪問的資源都是可以限制的,這些都可以通過權限驗證來實現。  
  4. authenticatorConfig();   //配置自動認證  


k) 爲context創建welcome files,通常是這三個啓動文件:index.html、index.htm、index.jsp,它們就被默認地綁在了這個context上。
l) 觸發AFTER_START_EVENT事件。
m) 配置listener。
n) 啓動manager。Manager是用來管理session的。對於服務器來說,每個請求傳遞過來的時候,會在request裏面加上一個叫做sessionId的屬性,服務器就通過這個sessionId來知道這個請求到底是屬於哪一個session的。
o) 啓動context的後臺主線程。
p) 配置filter。
q) 啓動帶有<load-on-startup>的Servlet。如<load-on-startup>1</load-on-startup>,啓動的順序從1開始按照數字從小到大,1, 2, 3 ……,最後纔是0。
默認情況下,至少會啓動如下3個的Servlet:
org.apache.catalina.servlets.DefaultServlet 負責處理靜態資源的Servlet,例如圖片、html、css、js等等。
org.apache.catalina.servlets.InvokerServlet負責處理沒有做Servlet Mapping的那些Servlet。
org.apache.jasper.servlet.JspServlet負責處理JSP文件。
r) 標識context已經啓動完畢,如果在啓動的時候發生錯誤,則stop server。
s) 註冊JMX。registerJMX();
t) 關閉所有JAR,以免在啓動的時候打開的文件數量總是很高。

如果文字看不下來的話,可以看看下面的流程圖,如果你堅持看完了上面一大段話的話,呃,也可以再看看下面的圖。



到這裏tomcat就算啓動完畢了,我們可以看到它的啓動過程是一環套一環的過程,先是父容器,然後是子容器,一層層往下遞進。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章