天天看點

深入了解Tomcat系列之三:Connector

前言

connector是tomcat的連接配接器,其主要任務是負責處理浏覽器發送過來的請求,并建立一個request和response的對象用于和浏覽器交換資料,然後産生一個線程用于處理請求,connector會把request和response對象傳遞給該線程,該線程的具體的處理過程是container容器的事了。執行過程分為以下幾個步驟

執行個體化connector,構造一個connector對象

調用connector的initintenal方法,初始化connetor

調用protocolhanlder的init方法,完成protocolhanlder的初始化。這個過程包括了建立線程池并建立一個線程處理浏覽器請求

調用connector的startintenal方法,啟動connector

調用protocolhandler的start方法,啟動protocolhanlder

調用mapperlistener的start方法,啟動監聽器程式

為了對connector的執行過程有一個大概的印象,可以參考下面的序列圖:

深入了解Tomcat系列之三:Connector

注意:由于tomcat還支援ajp協定,但為了簡化,我畫的這個序列圖是基于http協定的,這也是我們在web開發中接觸最多的協定了。

在深入connector之前我們先看看connector類的結構:

深入了解Tomcat系列之三:Connector
深入了解Tomcat系列之三:Connector

既然是處理浏覽器請求,那麼需要支援http協定,在tomcat中有兩種協定處理器:http/1.1與ajp/1.3協定處理器。在server.xml中已經指明tomcat所支援的兩種協定:

代碼清單3-1:

在tomcat中是怎麼樣分别處理這兩種協定的呢,我們可以protocolhanlder類中找到答案:

深入了解Tomcat系列之三:Connector

圖中被選中的就是tomcat預設使用協定處理器,其實作過程與java标準socket程式設計是一樣的,在tomcat中可以使用connetor類的setprotocol方法,看看源碼就知道了:

代碼清單3-2:

從第2個if子句的最後一個else可以知道tomcat預設使用的是http1.1協定。

我們再看看connector的初始化過程:

代碼清單3-3:

從這段代碼中可以看到:首先調用父類org.apache.catalina.util.lifecyclembeanbase的初始化方法,然後建立一個adapter,然後設定protocolhanlder(協定處理器)的adapter,同時判斷傳過來的請求的請求方法(比如get或者post),如果沒有指明請求方法,預設使用post處理,然後調用protocolhanlder的初始化方法,最後調用mapperlistener的初始化方法,而mapperlistener的初始化方法調用的是org.apache.catalina.util.lifecyclebase的init方法,我們重點關注protocolhanlder的初始化方法,具體是實作在abstractprotocol抽象類中,其直接子類有abstractajpprotocol和abstracthttp11protocol,分别對應的是兩種不同的處理協定,是以協定處理器的初始化方法是在其子抽象類(實作protocolhanlder接口的抽象類)來實作的,這裡看看abstracthttp11protocol的初始化方法:

代碼清單3-4:

打斷點調試可以知道oname的值是<code>tomcat:type=protocolhandler,port=auto-1,address="127.0.0.1"</code>,tponame是<code>tomcat:type=protocolhandler,port=auto-1,address="127.0.0.1"</code>,roname是<code>tomcat:type=globalrequestprocessor,name="http-bio-127.0.0.1-auto-1"</code>,我們重點關注endpoint的init方法,主要完成以下幾個過程:

設定線程接收數和最大連接配接數

建立線程池,啟動監聽的線程監聽使用者請求

啟動一個線程處理請求

初始化完成connector就可以啟動了,啟動階段調用startinternal方法:

代碼清單3-5:

可以看出connector調用<code>protocolhandler.start()</code>方法,繼續看看這個方法的源碼:

代碼清單3-6:

這個方法又調用了<code>endpoint.start()</code>方法:

代碼清單3-7:

然後又調用了org.apache.tomcat.util.net.abstractendpoint.startinternal()方法:

代碼清單3-8:

啟動一個線程處理異步請求

這裡啟動了一個異步線程處理請求,這個異步線程是如何執行的呢?

代碼清單3-9:

org.apache.tomcat.util.net.jioendpoint.socketprocessor的職責是把具體的請求處理過程委派給org.apache.tomcat.util.net.jioendpoint.handler,然後根據handler傳回的不同socketstate,來決定是否關閉連接配接或者進行下一輪處理。

代碼清單3-10:

其中的process方法主要完成對request的解析,包括請求頭、請求行和請求體

代碼清單3-11:

首先在http11processor的process方法裡,會先從socket裡讀取http請求資料,并解析請求頭,構造request對象和response對象,然後調用adapter.service()方法。adapter.service()完成請求行以及請求體的解析,并把解析出來的資訊封裝到request和response對象中,adapter(确切說是org.apache.catalina.connector.coyoteadapter)是connector和container的橋梁,經過這一步,請求就從connector傳遞到container裡了,adapter.service()方法之後便将封裝了request以及response對象的socket傳給container容器了。

要注意的是:最先處理請求的request是org.apache.coyote.request類型,這是一個tomcat中一個輕量級對象,完成基本的請求處理後很容易被jvm回收,那為什麼不直接交給connector.request對象處理呢?由于後者是servlet容器真正傳遞的對象其完成的職責比前者複雜,這裡使用org.apache.coyote.request主要減輕後者的任務負擔,出于性能考慮才這麼設計。

具體service方法清單如下:

代碼清單3-12:

從<code>connector.getservice().getcontainer().getpipeline().getfirst().invoke(request, response);</code>這句代碼中可以知道下一步的處理需要交給container容器了。

經過上面一系列複雜的操作流程,tomcat的connector已經完成了protocol.start()方法,傳回connector的startintenal方法,還有一個步驟要完成就是<code>mapperlistener.start()</code>的方法了,整個執行過程比較簡單,有兩步:

執行connector的startintenal方法

執行mapperlistener的startintenal方法

代碼清單3-14:

首先注冊已初始化的元件,然後為這些元件添加監聽器,最後添加容器之間的映射關系。這樣經過上面兩個大步驟以及n個小步驟,我們的connector才算啟動完畢,可謂是路途艱辛啊!