Tomcat的概念及啟動原理淺析
Tomcat概念及原理
Servlet(server applet 服務端小程式)是一種國際組織的協議、約定.
Tomcat只是針對Servlet協議的規範做了封裝,其他這樣的軟體還有jetty等.
Tomcat總體結構
Server –> Service –>
Connector & Container( Engine –> Host –> Context( Wrapper( Servlet ) ) )
Tomcat 的心臟是兩個元件:Connector 和 Container,Connector 元件是可以被替換,這樣可以提供給伺服器設計者更多的選擇,因為這個元件是如此重要,不僅跟伺服器的設計的本身,而且和不同的應用場景也十分相關,所以一個 Container 可以選擇對應多個 Connector。
多個 Connector 和一個 Container 就形成了一個 Service,有了 Service 就可以對外提供服務了,但是 Service 還要一個生存的環境,必須要有人能夠給她生命、掌握其生死大權,那就非 Server 莫屬了。所以整個 Tomcat 的生命週期由 Server 控制。
Server
前面說一對情侶因為 Service 而成為一對夫妻,有了能夠組成一個家庭的基本條件,但是它們還要有個實體的家,這是它們在社會上生存之本,有了家它們就可以安心的為人民服務了,一起為社會創造財富。
Server 要完成的任務很簡單,就是要能夠提供一個介面讓其它程式能夠訪問到這個 Service 集合、同時要維護它所包含的所有 Service 的生命週期,包括如何初始化、如何結束服務、如何找到別人要訪問的 Service。還有其它的一些次要的任務,如您住在這個地方要向當地政府去登記啊、可能還有要配合當地公安機關日常的安全檢查什麼的。
Service
我們將 Tomcat 中 Connector、Container 作為一個整體比作一對情侶的話,Connector 主要負責對外交流,可以比作為 Boy,Container 主要處理 Connector 接受的請求,主要是處理內部事務,可以比作為 Girl。那麼這個 Service 就是連線這對男女的結婚證了。是
Service 將它們連線在一起,共同組成一個家庭。當然要組成一個家庭還要很多其它的元素
說白了,Service 只是在 Connector 和 Container 外面多包一層,把它們組裝在一起,向外面提供服務,一個 Service 可以設定多個 Connector,但是隻能有一個 Container 容器。這個 Service 介面的方法列表如下:
Container
Container是一個介面,定義了下屬的各種容器,尤其是Wrapper、Host、Engine、Context
Engine
負責處理來自相關聯的service的所有請求,處理後,將結果返回給service,而connector是作為service與engine的中間媒介出現的。
一個engine下可以配置一個預設主機,每個虛擬主機都有一個域名。當engine獲得一個請求時,它把該請求匹配到虛擬主機(host)上,然後把請求交給該主機來處理。
Engine有一個預設主機,當請求無法匹配到任何一個虛擬主機時,將交給預設host來處理。Engine以執行緒的方式啟動Host。
Host
代表一個虛擬主機,每個虛擬主機和某個網路域名(Domain Name)相匹配。
每個虛擬主機下都可以部署一個或多個web應用,每個web應用對應於一個context,有一個context path。
當Host獲得一個請求時,將把該請求匹配到某個Context上,然後把該請求交給該Context來處理匹配的方法是“最長匹配”,所以一個path==””的Context將成為該Host的預設Context所有無法和其它Context的路徑名匹配的請求都將最終和該預設Context匹配。
Context
一個Context對應於一個Web應用,一個Web應用由一個或者多個Servlet組成Context在建立的時候將根據配置檔案$ CATALINA_HOME/conf/web.xml和$ WEBAPP_HOME/WEB-INF/web.xml載入Servlet類。當Context獲得請求時,將在自己的對映表(mapping table)中尋找相匹配的Servlet類,如果找到,則執行該類,獲得請求的迴應,並返回。
Wrapper
Wrapper 代表一個 Servlet,它負責管理一個 Servlet,包括的 Servlet 的裝載、初始化、執行以及資源回收。Wrapper 是最底層的容器,它沒有子容器了,所以呼叫它的 addChild 將會報錯。
Wrapper 的實現類是 StandardWrapper,StandardWrapper 還實現了擁有一個 Servlet 初始化資訊的 ServletConfig,由此看出 StandardWrapper 將直接和 Servlet 的各種資訊打交道。
Connector
Connector將在某個指定的埠上來監聽客戶的請求,把從socket傳遞過來的資料,封裝成Request,傳遞給Engine來處理,並從Engine處獲得響應並返回給客戶。
Tomcat通常會用到兩種Connector:
- Http Connector 在埠8080處偵聽來自客戶browser的http請求。 AJP Connector
- 在埠8009處偵聽來自其它WebServer(Apache)的servlet/jsp代理請求。
Lifecycle
現實生活中大部分的事物都有生命週期,就像人的生老病死一樣。
在程式設計中也有很多物件是具有生命週期的,從初始化、執行、回收等 會經歷幾個不同的階段。 在tomcat中容器相關的好多組建都實現了Lifecycle介面,當tomcat啟動時,其依賴的下層元件會全部進行初始化。 並且可以對每個元件生命週期中的事件新增監聽器。
例如當伺服器啟動的時候,tomcat需要去呼叫servlet的init方法和初始化容器等一系列操作,而停止的時候,也需要呼叫servlet的destory方法。而這些都是通過org.apache.catalina.Lifecycle介面來實現的。由這個類來制定各個元件生命週期的規範。
下圖是生命週期的所有狀態
LifecycleListener
在Lifecycle的介紹中提到,Lifecycle會對每個元件生命週期中的事件新增監聽器,也就是addLifecycleListener(LifecycleListener listener)方法,而LifecycleListener就是上面提到的監聽器。
LifecycleEvent
顧名思義,就是當有監聽事件發生的時候,LifecycleEvent會儲存時間型別和資料
/**
* Construct a new LifecycleEvent with the specified parameters.
*
* @param lifecycle Component on which this event occurred
* @param type Event type (required)
* @param data Event data (if any)
*/
public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
super(lifecycle);
this.type = type;
this.data = data;
}
Tomcat啟動過程
以嵌入式的tomcat-embed-core:8.5.6為例,以程式設計的方式啟動tomcat,可以看到呼叫了server.start()
Tomcat的start()方法
/**
* Start the server.
*
* @throws LifecycleException Start error
*/
public void start() throws LifecycleException {
getServer();
getConnector();
server.start();
}
server的預設實現是StandardServer, start()方法在其父類LifecycleBase中
宣告:除了StandardServer, 這個類也是StandardService, StandardEngine, StandardHost, StandardContext的父類,所以當呼叫這些類的start()方法時其實都是呼叫此處的start()方法,而最重要的是在start()方法中會呼叫startInternal(),startInternal()在LifecycleBase中是抽象方法,具體實現由各實現類去做,在此後不再贅述。
啟動server
啟動server其實就是啟動service容器
啟動service
啟動service其實就是啟動engine容器和connector容器
啟動Engine
啟動engine其實就是啟動host容器(多執行緒)
啟動Host
啟動Host的方式和上圖一樣,都是以執行緒的方式啟動子Container,這裡Host的children為Context
啟動Context
啟動Wrapper
下面看一下非常重要的一個方法 loadServlet:
它基本上描述了對 Servlet 的操作,當裝載了 Servlet 後就會呼叫 Servlet 的 init 方法,同時會傳一個 StandardWrapperFacade 物件給 Servlet,這個物件包裝了 StandardWrapper,ServletConfig 與它們的關係圖如下:
啟動Connector
Tomcat的Connector是Coyote connector的一種實現,這是tomcat的官方解釋:The Coyote HTTP/1.1 Connector element represents a Connector component that supports the HTTP/1.1 protocol. It enables Catalina to function as a stand-alone web server, in addition to its ability to execute servlets and JSP pages.
Tomcat8之後預設使用nio作為接受請求策略,預設在Service啟動的時候進行初始化,當然也可以單獨啟動,在預設的建構函式中會初始化ProtocolHandler
tomcat中支援兩種協議的聯結器:HTTP/1.1與AJP/1.3
HTTP/1.1協議負責建立HTTP連線,web應用通過瀏覽器訪問tomcat伺服器用的就是這個聯結器,預設監聽的是8080埠;
AJP/1.3協議負責和其他HTTP伺服器建立連線,監聽的是8009埠,比如tomcat和apache或者iis整合時需要用到這個聯結器。
Connector的啟動其實就是ProtocolHandler的啟動,如下圖:
ProtocolHandler的類結構如下圖:
Connector的startInternal方法呼叫了ProtocolHandle的start方法,這個start方法就在AbstractProtocol中,如下圖:
經歷了這麼多,終於快到終點了,這時候又出來了一個EndPoint,不過不要急,這個EndPoint已經算是終點了,EndPoint是咱們Tomcat啟動的Socket管理者(注意:通過類圖可以看出AbstractEndpoint已經脫離了Lifecycle和LifecycleListener體系,所以它只是一個簡簡單單的Socket管理者),因為是由他直接啟動預設的Nio,在啟動的時候先看看類結構圖:
再看一下EndPoint能做什麼,看方法就知道了
createAcceptor、createExecutor等方法都是在初始化EndPoint很重要方法,因為在接收請求的時候,通過Acceptor的接收,經過重重模組,才能一路到達最後的Servlet,這個在後面會有講到。
那麼EndPoint最後的啟動,看下圖:
在這個地方會啟動很多的執行緒,第一次讀這段程式碼的同學可能會有點亂,因為有想過啟動執行緒,現在還有些不知道是為什麼啟動,這就是tomcat的有意思之處了,因為下一篇我會說到tomcat接收請求的流程,所以在下一篇文章我會詳細講解。
好了,大家,tomcat 的啟動流程大概就是這樣,當然這裡面還有一些更細節的知識,此文章適合第一次想要了解Tomcat的同學,當然以後我也會一直完善這篇文章,有看到有意思的部分我也會補充的。