TOMCAT源碼分析(啟動框架)
前言:
???本文是我閱讀了TOMCAT源碼后的一些心得。?主要是講解TOMCAT的系統框架,?以及啟動流程。若有錯漏之處,敬請批評指教!
建議:
???畢竟TOMCAT的框架還是比較復雜的,?單是從文字上理解,?是不那么容易掌握TOMCAT的框架的。?所以得實踐、實踐、再實踐。?建議下載一份TOMCAT的源碼,?調試通過,?然后單步跟蹤其啟動過程。?如果有不明白的地方,?再來查閱本文,?看是否能得到幫助。?我相信這樣效果以及學習速度都會好很多!
???
1.?Tomcat的整體框架結構
???Tomcat的基本框架,?分為4個層次。
???Top?Level?Elements:
????Server
????Service???
???Connector
????HTTP
????AJP
???Container
???Engine
?????Host
???Context
???Component??
????manager
???logger
???loader
???pipeline
???valve
?????????...
???站在框架的頂層的是Server和Service
???Server:??其實就是BackGroud程序,?在Tomcat里面的Server的用處是啟動和監聽服務端事件(諸如重啟、關閉等命令。? 在tomcat的標準配置文件:server.xml里面,?我們可以看到“<Server?port="8005"?shutdown= "SHUTDOWN"?debug="0">”這里的"SHUTDOWN"就是server在監聽服務端事件的時候所使用的命令字)
???Service:?在tomcat里面,?service是指一類問題的解決方案。??通常我們會默認使用tomcat提供的:Tomcat- Standalone?模式的service。?在這種方式下的service既給我們提供解析jsp和servlet的服務,?同時也提供給我們解析靜態文本的服務。
???
???Connector:?Tomcat都是在容器里面處理問題的,?而容器又到哪里去取得輸入信息呢?
Connector就是專干這個的。?他會把從socket傳遞過來的數據,?封裝成Request,?傳遞給容器來處理。
???通常我們會用到兩種Connector,一種叫http?connectoer,?用來傳遞http需求的。?另一種叫AJP,?在我們整合 apache與tomcat工作的時候,?apache與tomcat之間就是通過這個協議來互動的。?(說到apache與tomcat的整合工作,? 通常我們的目的是為了讓apache?獲取靜態資源,?而讓tomcat來解析動態的jsp或者servlet。)
???Container:?當http?connector把需求傳遞給頂級的container:?Engin的時候,?我們的視線就應該移動到Container這個層面來了。
???在Container這個層,?我們包含了3種容器:?Engin,?Host,?Context.
???Engin:?收到service傳遞過來的需求,?處理后,?將結果返回給service(?service?是通過?connector?這個媒介來和Engin互動的?).
???Host:?Engin收到service傳遞過來的需求后,不會自己處理,?而是交給合適的Host來處理。
Host在這里就是虛擬主機的意思,?通常我們都只會使用一個主機,既“localhost”本地機來處理。?
???Context:?Host接到了從Host傳過來的需求后,?也不會自己處理,?而是交給合適的Context來處理。?
???比如:?<http://127.0.0.1:8080/foo/index.jsp>
?????????<http://127.0.1:8080/bar/index.jsp>
???前者交給foo這個Context來處理,?后者交給bar這個Context來處理。
???很明顯吧!?context的意思其實就是一個web?app的意思。
???我們通常都會在server.xml里面做這樣的配置
???<Context?path="/foo"?docBase="D:/project/foo/web"?/>
???這個context容器,就是用來干我們該干的事兒的地方的。
???
???Compenent:?接下來,?我們繼續講講component是干什么用的。
???我們得先理解一下容器和組件的關系。
???需求被傳遞到了容器里面,?在合適的時候,?會傳遞給下一個容器處理。
???而容器里面又盛裝著各種各樣的組件,?我們可以理解為提供各種各樣的增值服務。
???manager:?當一個容器里面裝了manager組件后,這個容器就支持session管理了,?事實上在tomcat里面的session管理,?就是靠的在context里面裝的manager?component.
???logger:?當一個容器里面裝了logger組件后,?這個容器里所發生的事情,?就被該組件記錄下來啦!?我們通常會在logs/?這個目錄下看見?catalina_log.time.txt?以及?localhost.time.txt?和 localhost_examples_log.time.txt。?這就是因為我們分別為:engin,?host以及context (examples)這三個容器安裝了logger組件,?這也是默認安裝,?又叫做標配?:)
???loader:?loader這個組件通常只會給我們的context容器使用,?loader是用來啟動context以及管理這個context的classloader用的。
????pipline:?pipeline是這樣一個東西,?當一個容器決定了要把從上級傳遞過來的需求交給子容器的時候,?他就把這個需求放進容器的管道(pipeline)里面去。?而需求傻呼呼得在管道里面流動的時候,?就會被管道里面的各個閥門攔截下來。?比如管道里面放了兩個閥門。?第一個閥門叫做“access_allow_vavle”,?也就是說需求流過來的時候,它會看這個需求是哪個IP過來的,?如果這個IP已經在黑名單里面了, ?sure,?殺!?第二個閥門叫做“defaul_access_valve”它會做例行的檢查,?如果通過的話,OK,?把需求傳遞給當前容器的子容器。?就是通過這種方式,?需求就在各個容器里面傳遞,流動,?最后抵達目的地的了。
????valve:?就是上面所說的閥門啦。
???Tomcat里面大概就是這么些東西,?我們可以簡單地這么理解tomcat的框架,它是一種自上而下,?容器里又包含子容器的這樣一種結構。
2.?Tomcat的啟動流程
???這篇文章是講tomcat怎么啟動的,既然我們大體上了解了TOMCAT的框架結構了,?那么我們可以望文生意地就猜到tomcat的啟動,?會先啟動父容器,然后逐個啟動里面的子容器。?啟動每一個容器的時候,?都會啟動安插在他身上的組件。?當所有的組件啟動完畢,?所有的容器啟動完畢的時候, ?tomcat本身也就啟動完畢了。
???順理成章地,?我們同樣可以猜到,?tomcat的啟動會分成兩大部分,?第一步是裝配工作。?第二步是啟動工作。?
???裝配工作就是為父容器裝上子容器,?為各個容器安插進組件的工作。?這個地方我們會用到digester模式,?至于digester模式什么,?有什么用,?怎么工作的.?請參考?<http://software.ccidnet.com/pub/article/c322_a31671_p2.html>
???啟動工作是在裝配工作之后,?一旦裝配成功了,?我們就只需要點燃最上面的一根導線,?整個tomcat就會被激活起來。?這就好比我們要開一輛已經裝配好了的汽車的時候一樣,我們只要把鑰匙插進鑰匙孔,一擰,汽車的引擎就會發動起來,空調就會開起來,?安全裝置就會生效,?如此一來,汽車整個就發動起來了。(這個過程確實和TOMCAT的啟動過程不謀而和,?讓我們不得不懷疑?TOMCAT的設計者是在GE做JAVA開發的)。
2.1?一些有意思的名稱:
???Catalina
???Tomcat
???Bootstrap
???Engin
???Host
???Context
???他們的意思很有意思:
???Catalina:?遠程轟炸機
???Tomcat:?熊貓轟炸機?--?轟炸機的一種(這讓我想起了讓國人引以為豪的熊貓手機,是不是英文可以叫做tomcat????,?又讓我想起了另一則廣告:?波導-手機中的戰斗機、波音-客機中的戰斗機?)
???Bootstap:?引導
???Engin:?發動機
???Host:?主機,領土
???Context:?內容,?目標,?上下文
???
???...?在許多許多年后,?現代人類已經滅絕。?后現代生物發現了這些單詞零落零落在一塊。?一個自以為聰明的家伙把這些東西翻譯出來了:?
???在地勤人員的引導(bootstrap)下,?一架轟炸架(catalina)騰空躍起,?遠看是熊貓轟炸機(tomcat),?近看還是熊貓轟炸機!?憑借著優秀的發動機技術(engin),?這架熊貓轟炸機飛臨了敵國的領土上空(host),?對準目標(context)投下了毀天滅地的核彈頭,波~?現代生物就這么隔屁了~
?
???綜上所述,?這又不得不讓人聯想到GE是不是也參與了軍事設備的生產呢?
???反對美帝國主義!?反對美霸權主義!?和平萬歲!?自由萬歲!
???
2.2??歷史就是那么驚人的相似!?tomcat的啟動就是從org.apache.catalina.startup.Bootstrap這個類悍然啟動的!
???在Bootstrap里做了兩件事:
???1.?指定了3種類型classloader:
??????commonLoader:?common/classes、common/lib、common/endorsed
??????catalinaLoader:?server/classes、server/lib、commonLoader
??????sharedLoader:??shared/classes、shared/lib、commonLoader
???2.?引導Catalina的啟動。
??????用Reflection技術調用org.apache.catalina.startup.Catalina的process方法,?并傳遞參數過去。
???
2.3?Catalina.java
???Catalina完成了幾個重要的任務:
???1.?使用Digester技術裝配tomcat各個容器與組件。
??????1.1?裝配工作的主要內容是安裝各個大件。?比如server下有什么樣的servcie。?Host會容納多少個context。?Context都會使用到哪些組件等等。?
??????1.2?同時呢,?在裝配工作這一步,?還完成了mbeans的配置工作。?在這里,我簡單地但不十分精確地描述一下mbean是什么,干什么用的。
??????????我們自己生成的對象,?自己管理,?天經地義!?但是如果我們創建了對象了,?想讓別人來管,?怎么辦呢??我想至少得告訴別人我們都有什么,?以及通過什么方法可以找到??吧!?JMX技術給我們提供了一種手段。?JMX里面主要有3種東西。Mbean,?agent, ?connector.
???????Mbean:?用來映射我們的對象。也許mbean就是我們創建的對象,?也許不是,?但有了它,?就可以引用到我們的對象了。
???????Agent:??通過它,?就可以找到mbean了。
???????Connector:?連接Agent的方式。?可以是http的,?也可以是rmi的,還可以直接通過socket。
??????發生在tomcat?裝配過程中的事情:??GlobalResourcesLifecycleListener?類的初始化會被觸發:
?????????protected?static?Registry?registry?=?MBeanUtils.createRegistry();??會運行
?????????MBeanUtils.createRegistry()??會依據 /org/apache/catalina/mbeans/mbeans-descriptors.xml這個配置文件創建?mbeans.?Ok,?外界就有了條途徑訪問tomcat中的各個組件了。(有點像后門兒)
???2.?為top?level?的server?做初始化工作。?實際上就是做通常會配置給service的兩條connector.(http,?ajp)
???3.?從server這個容器開始啟動,?點燃整個tomcat.
???4.?為server做一個hook程序,?檢測當server?shutdown的時候,?關閉tomcat的各個容器用。
???5.?監聽8005端口,?如果發送"SHUTDOWN"(默認培植下字符串)過來,?關閉8005serverSocket。
2.4?啟動各個容器
???1.?Server
??????觸發Server容器啟動前(before_start),?啟動中(start),?啟動后(after_start)3個事件,?并運行相應的事件處理器。
??????啟動Server的子容器:Servcie.?
???2.?Service
??????啟動Service的子容器:Engin
??????啟動Connector
???3.?Engin
??????到了Engin這個層次,以及以下級別的容器,?Tomcat就使用了比較一致的啟動方式了。
??????首先,??運行各個容器自己特有一些任務
??????隨后,??觸發啟動前事件
??????立即,??設置標簽,就表示該容器已經啟動
??????接著,??啟動容器中的各個組件:?loader,?logger,?manager等等
??????再接著,啟動mapping組件。(注1)
??????緊跟著,啟動子容器。
??????接下來,啟動該容器的管道(pipline)
??????然后,??觸發啟動中事件
??????最后,??觸發啟動后事件。
?
??????Engin大致會這么做,?Host大致也會這么做,?Context大致還是會這么做。?那么很顯然地,?我們需要在這里使用到代碼復用的技術。?tomcat在處理這個問題的時候,?漂亮地使用了抽象類來處理。?ContainerBase.?最后使得這部分完成復雜功能的代碼顯得干凈利落,?干練爽快,?實在是令人覺得嘆為觀止,?細細品來,?直覺如享佳珍,?另人齒頰留香,?留戀往返啊!
??????
??????Engin的觸發啟動前事件里,?會激活綁定在Engin上的唯一一個Listener:EnginConfig。
??????這個EnginConfig類基本上沒有做什么事情,?就是把EnginConfig的調試級別設置為和Engin相當。?另外就是輸出幾行文本,?表示Engin已經配置完畢,?并沒有做什么實質性的工作。
??????注1:?mapping組件的用處是,?當一個需求將要從父容器傳遞到子容器的時候,?而父容器又有多個子容器的話,?那么應該選擇哪個子容器來處理需求呢??這個由mapping?組件來定奪。
????
???4.?Host
???????同Engin一樣,?也是調用ContainerBase里面的start()方法,?不過之前做了些自個兒的任務,就是往Host這個容器的通道(pipline)里面,?安裝了一個叫做
?“org.apache.catalina.valves.ErrorReportValve”的閥門。
???????這個閥門的用處是這樣的:??需求在被Engin傳遞給Host后,?會繼續傳遞給Context做具體的處理。?這里需求其實就是作為參數傳遞的Request,?Response。?所以在context把需求處理完后,?通常會改動response。?而這個 org.apache.catalina.valves.ErrorReportValve的作用就是檢察response是否包含錯誤,?如果有就做相應的處理。
???5.?Context
???????到了這里,?就終于輪到了tomcat啟動中真正的重頭戲,啟動Context了。
?StandardContext.start()?這個啟動Context容器的方法被StandardHost調用.
?5.1?webappResources?該context所指向的具體目錄
?5.2?安裝defaultContex,?DefaultContext?就是默認Context。?如果我們在一個Host下面安裝了 DefaultContext,而且defaultContext里面又安裝了一個數據庫連接池資源的話。?那么其他所有的在該Host下的 Context,?都可以直接使用這個數據庫連接池,?而不用格外做配置了。
??5.3?指定Loader.?通常用默認的org.apache.catalina.loader.WebappLoader這個類。???Loader就是用來指定這個context會用到哪些類啊,?哪些jar包啊這些什么的。
?5.4?指定?Manager.?通常使用默認的org.apache.catalina.session.?StandardManager?。?Manager是用來管理session的。
?????其實session的管理也很好實現。?以一種簡單的session管理為例。?當需求傳遞過來的時候,?在Request對象里面有一個 sessionId?屬性。?OK,?得到這個sessionId后,?我們就可以把它作為map的key,而value我們可以放置一個 HashMap.?HashMap里邊兒,?再放我們想放的東西。
?5.5?postWorkDirectory?().?Tomcat下面有一個work目錄。?我們把臨時文件都扔在那兒去。?這個步驟就是在那里創建一個目錄。?一般說來會在%CATALINA_HOME%/work/Standalone\localhost\?這個地方生成一個目錄。
5.6??Binding?thread。到了這里,?就應該發生?class?Loader?互換了。?之前是看得見tomcat下面所有的class 和lib.?接下來需要看得見當前context下的class。?所以要設置contextClassLoader,?同時還要把舊的 ClassLoader記錄下來,因為以后還要用的。
5.7??啟動?Loader.?指定這個Context具體要使用哪些classes,?用到哪些jar文件。?如果reloadable設置成了true,?就會啟動一個線程來監視classes的變化,?如果有變化就重新啟動Context。
5.8??啟動logger
5.9??觸發安裝在它身上的一個監聽器。
?lifecycle.fireLifecycleEvent(START_EVENT,?null);?
?作為監聽器之一,ContextConfig會被啟動.?ContextConfig就是用來配置web.xml的。?比如這個Context有多少Servlet,?又有多少Filter,?就是在這里給Context裝上去的。
?5.9.1?defaultConfig.?每個context都得配置?tomcat/conf/web.xml?這個文件。
?5.9.2?applicationConfig?配置自己的?WEB-INF/web.xml?文件
5.9.3?validateSecurityRoles?權限驗證。?通常我們在訪問/admin?或者/manager的時候,需要用戶要么是 admin的要么是manager的,?才能訪問。?而且我們還可以限制那些資源可以訪問,?而哪些不能。?都是在這里實現的。
5.9.4?tldScan:?掃描一下,?需要用到哪些標簽(tag?lab)
5.10?啟動?manager
5.11?postWelcomeFiles()?我們通常會用到的3個啟動文件的名稱:
index.html、index.htm、index.jsp?就被默認地綁在了這個context上
?5.12?listenerStart?配置listener
?5.13?filterStart?配置?filter
?5.14?啟動帶有<load-on-startup>1</load-on-startup>的Servlet.
??順序是從小到大:?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文件的.
???????5.15??標識context已經啟動完畢。
?走了多少個步驟啊,?Context總算是啟動完畢嘍。
????OK!?走到了這里,?每個容器以及組件都啟動完畢。?Tomcat終于不辭辛勞地為人民服務了!
3.?參考文獻:
????<http://jakarta.apache.org/tomcat/>
????<http://www.onjava.com/pub/a/onjava/2003/05/14/java_webserver.html>
????
4.?后記
????這篇文章是講解tomcat啟動框架的,還有篇文章是講解TOMCAT里面的消息處理流程的細節的。?文章內容已經寫好了,?現在正在整理階段。?相信很快就可以做出來,?大家共同研究共同進步。
????這篇文章是獨自分析TOMCAT源碼所寫的,?所以一定有地方是帶有個人主觀色彩,?難免會有片面之處。若有不當之處敬請批評指教,這樣不僅可以使剛開始研究TOMCAT的兄弟們少走彎路,?我也可以學到東西。
????email:?sojan_java@yahoo.com.cn
5.?tomcat源碼分析(消息處理)