Tomcat中得到更多-Tomcat的源碼分析

趙晨希

<zhaochenxi@vip.sina.com>

本文的作者通過對(duì)TomCat源代碼的研究,向讀者描述了在tomcat3.3和TomCat4.0中在設(shè)計(jì)方面使用的不同設(shè)計(jì)理念和模式,希望對(duì)廣大開發(fā)者在設(shè)計(jì)自己的系統(tǒng)時(shí)有所幫助。

關(guān)于Tomcat的基本情況

眾所周知Tomcat是一個(gè)免費(fèi)的開放源碼的Serlvet容器,它是Apache基金會(huì)的Jakarta項(xiàng)目中的一個(gè)核心項(xiàng)目,也是sun公司官方推薦的servlet和jsp容器,同時(shí)它還獲得過多種榮譽(yù)。servlet和jsp的最新規(guī)范都可以在tomcat的新版本中得到實(shí)現(xiàn)。Tomcat具有輕量級(jí)和靈活嵌入到應(yīng)用系統(tǒng)中的優(yōu)點(diǎn),所以得到了廣泛的應(yīng)用。在Tomcat的發(fā)展中,Sun在1999年六月宣布參與Jakarta項(xiàng)目的Tomcat servlet容器和Jsp引擎的開發(fā),使得Tomcat在3.x和4.x版之間系統(tǒng)設(shè)計(jì)上發(fā)生了比較大的變化。Tomcat的其他信息我就不多說了。有興趣的朋友可以訪問http://jakarta.apache.org/ 的官方網(wǎng)站得到更多的信息。

因?yàn)楣ぷ鞯脑颍腋膶懥?strong>Tomcat的一些代碼,所以我粗略的研究了一下Tomcat3.3和Tomcat4.0的源碼,深深地被這個(gè)開放軟件的設(shè)計(jì)和實(shí)現(xiàn)吸引,感覺到這個(gè)軟件中有許多值得我們學(xué)習(xí)和借鑒的地方。我把自己的理解介紹給大家算是拋磚引玉,不足和偏差還望大家批評(píng)指正。下面就來讓我們看看從 Tomcat那里我們可以得到什么。

Tomcat中學(xué)習(xí)設(shè)計(jì)模式

Tomcat的設(shè)計(jì)和實(shí)現(xiàn)處處體現(xiàn)著設(shè)計(jì)模式的思想,它的基本流程是首先通過解析xml格式的配置文件,獲得系統(tǒng)的配置和應(yīng)用信息,然后加載定制的組件模塊提供各種系統(tǒng)服務(wù)。系統(tǒng)的各部分功能都是通過可以配置的組件模塊來實(shí)現(xiàn)的。Tomcat實(shí)現(xiàn)中像Observer,F(xiàn)acade,Adapter, Singleton等多種設(shè)計(jì)模型在Tomcat的源碼中隨處可見,為我們提供了一個(gè)很好的學(xué)習(xí)設(shè)計(jì)模式的平臺(tái)。我主要介紹一下Tomcat中程序流程控制所采用的設(shè)計(jì)模式,這是一個(gè)程序運(yùn)行的框架。前面提到由于Sun公司的參與,Tomcat雖然基本的流程沒有變化,但是Tomcat3.3和 Tomcat4.0版本之間在概念上還是有很大地不同的。Tomcat3.3整體上是模塊化的設(shè)計(jì),而Tomcat4.0可以看作是采用面向組件技術(shù)進(jìn)行設(shè)計(jì)的。組件是比模塊更高級(jí)的一個(gè)層次。我們可以通過比較它們之間的不同來了解實(shí)現(xiàn)一個(gè)服務(wù)器軟件可以采用的設(shè)計(jì)模式和實(shí)現(xiàn)方式。

Tomcat3.3的基本結(jié)構(gòu)設(shè)計(jì)

Tomcat3.3采用的是一種模塊化的鏈狀的控制結(jié)構(gòu),它的主要設(shè)計(jì)模式有:

Chain of responsibility(責(zé)任鏈)

作為一個(gè)基于請(qǐng)求響應(yīng)模式的服務(wù)器,在Tomcat3.3中采用一種鏈狀處理的控制模式。請(qǐng)求在鏈上的各個(gè)環(huán)節(jié)上傳遞,在任一環(huán)節(jié)上可以存在若干個(gè)"監(jiān)聽器"處理它。這樣做的目的是避免請(qǐng)求的發(fā)送者和接受者之間的直接耦合,從而可以為其他的對(duì)象提供了參與處理請(qǐng)求的機(jī)會(huì)。采用這個(gè)方式不但可以通過"監(jiān)聽器 "實(shí)現(xiàn)系統(tǒng)功能,而且可以通過添加新的"監(jiān)聽器"對(duì)象實(shí)現(xiàn)系統(tǒng)功能的擴(kuò)展。

Interceptor(監(jiān)聽器)

"監(jiān)聽器"是一個(gè)過去使用的名稱,它可以看作 "模塊(module)"的同義詞。它是Tomcat功能模塊構(gòu)建和擴(kuò)展的方式。Tomcat3.3的大部分功能都是通過"監(jiān)聽器"實(shí)現(xiàn)的。在 Tomcat中提供了一種簡單的鉤子(Hook)機(jī)制,監(jiān)聽器對(duì)鉤子中感興趣的事件進(jìn)行注冊(cè),事件發(fā)生后通過鉤子喚醒已注冊(cè)的"監(jiān)聽器"對(duì)象,"監(jiān)聽器" 對(duì)象對(duì)Tomcat內(nèi)核事件進(jìn)行處理。這些模塊都是圍繞著"責(zé)任鏈"和"策略"的模式進(jìn)行設(shè)計(jì)。通過"監(jiān)聽器"你可以監(jiān)聽各種特殊事件,進(jìn)而控制處理請(qǐng)求的各個(gè)步驟---解析,認(rèn)證,授權(quán),會(huì)話,響應(yīng)提交,緩沖區(qū)提交等等。

Strategy(策略)

所謂策略是指"定義一組規(guī)則,按照規(guī)則進(jìn)行對(duì)象封裝,使得他們只在規(guī)則內(nèi)部進(jìn)行交互"。通過策略模式使得Tomcat作為一個(gè)開源項(xiàng)目在開放環(huán)境下的開發(fā)和演變變得更輕松。通過這種模式把復(fù)雜的算法分成模塊然后不同的開發(fā)組提供各自的實(shí)現(xiàn)。從而實(shí)現(xiàn)調(diào)用模塊的代碼和模塊的具體實(shí)現(xiàn)代碼的分別開發(fā)。這樣可以使我們專注于問題的重點(diǎn),并且減少問題之間的依賴性。在Tomcat中大量采用了策略的設(shè)計(jì)模式,通過這種方式每一種服務(wù)都提供了多種的實(shí)現(xiàn)(例如Tomcat中有2-3種認(rèn)證模塊),在代碼完成后可以從穩(wěn)定性和性能表現(xiàn)的考慮選擇更好的實(shí)現(xiàn)。策略模式對(duì)開放式環(huán)境下的軟件開發(fā)非常有用。

我們通過簡化的類圖(見圖一)和時(shí)序圖(見圖二),描述一下Tomcat3.3的程序流程控制如何通過監(jiān)聽器和責(zé)任鏈實(shí)現(xiàn)。

圖?1.?簡化的類圖

關(guān)于類圖的簡單說明:

BaseInterceptor

是所有監(jiān)聽器的基類,描述了基本的模塊功能和對(duì)各種事件的缺省處理。

ContextManage

系統(tǒng)的核心控制對(duì)象,進(jìn)行請(qǐng)求處理和系統(tǒng)配置。它維護(hù)了全局的屬性、web應(yīng)用的內(nèi)容和全局模塊等多種信息,責(zé)任鏈的鉤子實(shí)現(xiàn)也在其中。

PoolTcpConnector

一個(gè)處理TCP連接的連接器對(duì)象,從BaseIntercepor派生。它包括一個(gè)具體處理socket連接的PoolTcpEndPoint類對(duì)象。

PoolTcpEndPoint

處理實(shí)際的tcp連接。它有一個(gè)連接池對(duì)象ThreadPool和運(yùn)行在獨(dú)立線程中的應(yīng)用邏輯類TcpWorkThread。

TcpWorkThead

處理socket連接事務(wù),調(diào)用接口TcpConnectionHandler中的請(qǐng)求處理方法。

Http10Interceptor

從PoolTcpConnector派生,實(shí)現(xiàn)了TcpConnectionHandler接口,是一個(gè)真正的監(jiān)聽器對(duì)象。它按照Http1.0的協(xié)議標(biāo)準(zhǔn)對(duì)tcp連接進(jìn)行處理,調(diào)用核心對(duì)象ContextManage的服務(wù)方法。

圖?2.?簡化的時(shí)序圖

關(guān)于時(shí)序圖中需要說明的地方:

  1. 在contextManager初始化后會(huì)根據(jù)配置信息,加載基本的應(yīng)用模塊和各種監(jiān)聽器對(duì)象,創(chuàng)建鉤子(Hook)機(jī)制,注冊(cè)監(jiān)聽器對(duì)象,形成一個(gè)責(zé)任鏈。然后對(duì)各個(gè)監(jiān)聽器對(duì)象發(fā)出engineInit,engineStart消息。
  2. 一個(gè)請(qǐng)求在經(jīng)過http10interceptor基本處理后提交到contextManager處理。
  3. ContextManager的processRequest方法進(jìn)行請(qǐng)求的處理。按照處理的步驟會(huì)順序地發(fā)出H_postReadRequest, H_contextMap, H_requestMap等消息。然后從hook中取得對(duì)該消息注冊(cè)的監(jiān)聽器對(duì)象,調(diào)用他們的處理方法,從而實(shí)現(xiàn)責(zé)任鏈方式。以下的代碼片斷說明了這種方式:

4.           BaseInterceptor ri[];

5.           //取得注冊(cè)對(duì)象

6.           ri=defaultContainer.getInterceptors(Container.H_postReadRequest);

7.           //執(zhí)行注冊(cè)對(duì)象的對(duì)消息的處理方法

for( int i=0; i< ri.length; i++ ) { status=ri[i].postReadRequest( req );      ......}

  1. 系統(tǒng)退出時(shí)contextManager發(fā)出engineStop消息。

Tomcat3.3的基本程序結(jié)構(gòu)就是采用上面介紹的方式設(shè)計(jì)的。它給我們的設(shè)計(jì)和開發(fā)提供了一個(gè)很好的思路,通過這種模式可以輕松的實(shí)現(xiàn)一個(gè)事件驅(qū)動(dòng)的基于模塊化設(shè)計(jì)的應(yīng)用程序。各個(gè)功能通過模塊實(shí)現(xiàn),通過對(duì)責(zé)任鏈上的消息和處理步驟的改動(dòng)或者添加新的監(jiān)聽器對(duì)象可以非常方便的擴(kuò)展Tomcat的功能。所以這是一個(gè)非常好的設(shè)計(jì)。

Tomcat4.0的基本結(jié)構(gòu)設(shè)計(jì)

雖然Tomcat3.x已經(jīng)實(shí)現(xiàn)了一個(gè)非常好的設(shè)計(jì)體系,但是在Sun公司加入后, Tomcat4.0中還是引入了不同的實(shí)現(xiàn)方式。主要的區(qū)別是Tomcat4.0采用了面向組件的設(shè)計(jì)方式, Tomcat4.0中的功能是由組件提供的,控制流程通過組件之間的通訊完成。這不同于Tomcat3.3中的基于模塊的鏈?zhǔn)娇刂?strong>結(jié)構(gòu)

面 向組件的技術(shù)(CO)是比面向?qū)ο蟮募夹g(shù)(OOP)更高一層的抽象,它融合了面向?qū)ο蟮膬?yōu)點(diǎn),加入了安全性和可擴(kuò)展的模塊設(shè)計(jì),可以更好的映射問題域空 間。采用面向組件的設(shè)計(jì)會(huì)帶來很多好處,可以提高復(fù)用性、降低耦合度和通過組裝構(gòu)成系統(tǒng)等等。面向組件編程中有許多概念與原來面向?qū)ο蟮木幊淌遣煌模?如:

Message(消息):定義抽象操作; Method(方法):定義具體的操作; Interface(接口):一組消息的集合; Implementation(實(shí)現(xiàn)):一組方法的集合; Module(模塊):靜態(tài)的數(shù)據(jù)結(jié)構(gòu), Type(類型):動(dòng)態(tài)的數(shù)據(jù)結(jié)構(gòu)

軟件組件不同與功能模塊,它具有以下特性:

  • 組件是一個(gè)自包容的模塊,具有定義清楚的界線,對(duì)外提供它的能力、屬性和事件。
  • 組件自身不保留狀態(tài)。
  • 組件可以是一個(gè)類,大部分情況下是一組類。

Java 語言中對(duì)面向組件編程的支持是通過JavaBeans模型獲得的。JavaBean組件框架提供了對(duì)事件和屬性的支持。Tomcat4.0的組件的就是通過JavaBean技術(shù)實(shí)現(xiàn)的。這是它和Tomcat3.3中最大的不同。下面我們來看一下Tomcat4.0是如何通過面向組件編程來實(shí)現(xiàn)程序流程控制的。

面向組件編程時(shí)設(shè)計(jì)組件是關(guān)鍵,從Tomcat4.0中可以看出主要使用了以下的設(shè)計(jì)模式:

Separation of Concerns(SOC)

設(shè)計(jì)組件時(shí)應(yīng)該從不同的問題領(lǐng)域,站在不同的觀點(diǎn)上分析,把每一種屬性分別考慮。舉一個(gè)例子FileLogger組件,它用于把系統(tǒng)日志信息保存到文件系統(tǒng)中。按照這種模式分析,我們從不同的角度看待它:它如何啟動(dòng)服務(wù)、停止服務(wù)和進(jìn)行通訊?它的具體的功能有哪些?別的組件可以發(fā)給它哪些消息?基于這些考慮,F(xiàn)ileLogger組件應(yīng)該實(shí)現(xiàn)兩種接口:Lifecycle(生存期接口)和LoggerBase(功能接口)。

Inversion of Control(IOC)這個(gè)模式定義的是,組件總是通過外部進(jìn)行管理的。組件需要的信息總是來源于外部,實(shí)際上組件在生存期的各個(gè)階段都是被創(chuàng)建它的組件管理的。在Tomcat4.0中就是通過這種組件之間的相互控制和調(diào)用實(shí)現(xiàn)各個(gè)功能的。

按照這些模式分析得到的Tomcat4.0中的組件是既有共性又有特性。共性是Lifecycle接口,特性是不同的功能接口。其中生存期接口處理組件的整個(gè)生存期中的各個(gè)階段的事件,功能接口提供給其他的組件使用。

具體的功能如何實(shí)現(xiàn)我在這里不多介紹了,我主要介紹一下Tomcat4.0中組件的Lifecycle接口的設(shè)計(jì)。Lifecycle接口實(shí)現(xiàn)了組件的生存期管理,控制管理和通訊。創(chuàng)建一個(gè)軟件組件和創(chuàng)建一個(gè)JavaBean對(duì)象一樣,可以參考JavaBean進(jìn)行理解。我通過一個(gè)模擬的 Lifecycle接口組件的類圖來描述。(見圖三)

圖?3.?Lifecycle接口組件類圖

對(duì)模擬的Lifecycle接口組件的類圖的說明

  1. Lifecycle Interface(接口)定義一組組件通訊的Message(消息)。
  2. 組件實(shí)現(xiàn)Lifecycle接口,組件內(nèi)部定義一個(gè)LifecycleSupport對(duì)象。需要和該組件通訊的其他組件必須實(shí)現(xiàn)LifecycleListener接口,該組件通過add/removeLifecycleListener方法管理需要通訊的其他組件。
  3. 組件內(nèi)部狀態(tài)改變時(shí)如果需要和其他組件通訊時(shí),通過LifecycleSupport對(duì)象的fireLifecycleEvent方法通知其他組件。
  4. 其他組件通過lifecycleEvent方法獲得通知的消息。LifecycleEvent對(duì)象是從java.util.EventObject派生的。
  5. 當(dāng)然在組件設(shè)計(jì)和實(shí)現(xiàn)中我們也可以直接使用JavaBeans中已經(jīng)提供的的類如:java.beans.PropertyChangeListener;java.beans.PropertyChangeSupport這樣可以獲得更多的功能特性支持。

通過上面的分析我們可以看到組件成為Tomcat4.0中的核心概念,系統(tǒng)的功能都是通過組件實(shí)現(xiàn)的,組件之間的通訊構(gòu)成了系統(tǒng)的運(yùn)行控制機(jī)制。我們把 Tomcat3.3中模塊化的鏈狀控制機(jī)制和Tomcat4.0的面向組件的設(shè)計(jì)進(jìn)行比較,就會(huì)發(fā)現(xiàn)Tomcat4.0在設(shè)計(jì)思想上軟件組件的概念非常明確。Tomcat4.0和Tomcat3.3最主要的區(qū)別就在于此。至于面向?qū)ο蠛兔嫦蚪M件的關(guān)系和區(qū)別,我在這里就不介紹了,有興趣的朋友可以找到很多這方面的資源。

Tomcat源碼中得到高效的軟件組件

Tomcat不但為我們提供了設(shè)計(jì)和實(shí)現(xiàn)系統(tǒng)時(shí)的新思路,同時(shí)因?yàn)樗怯山M件或者模塊構(gòu)成的,所以它還為我們提供了大量可用的高效軟件組件。這些組件都可以在我們的程序開發(fā)中使用。我簡單列舉一些,需要時(shí)可以直接從源碼中取得。

  • 一些特殊集合類數(shù)據(jù)結(jié)構(gòu)如池、隊(duì)列、緩存等可用于服務(wù)端開發(fā)。 "src"share"org"apache"tomcat"util"collections
  • 一個(gè)簡單的鉤子(Hooks)機(jī)制的實(shí)現(xiàn)。 src"share"org"apache"tomcat"util"hooks
  • 一個(gè)簡單線程池(ThreadPool)的實(shí)現(xiàn)。 src"share"org"apache"tomcat"util"threads
  • 組件Lifecycle接口的設(shè)計(jì)和實(shí)現(xiàn)。 "src"catalina"src"share"org"apache"Catalina
  • 常用的日志信息的管理(Logger)的實(shí)現(xiàn)。 src"catalina"src"share"org"apache"catalina"logger
  • 對(duì)xml格式的配置信息進(jìn)行處理(XmlMapper)的實(shí)現(xiàn)。 src"catalina"src"share"org"apache"catalina"util"xml
  • 對(duì)socket通訊的高級(jí)管理和實(shí)現(xiàn)(net)。 "src"catalina"src"share"org"apache"catalina"net

通過以上對(duì)Tomcat的簡單的介紹,我們可以看出,作為一個(gè)開放源碼的項(xiàng)目,Tomcat不但為我們提供了一個(gè)應(yīng)用的平臺(tái),同時(shí)它還為我們提供了一個(gè)學(xué)習(xí)和研究設(shè)計(jì)模式、面向組件技術(shù)等理論的實(shí)踐平臺(tái)。