天天看點

深入Jetty源碼之Connector

深入Jetty源碼之Connector

首先connector實作了lifecycle接口,在啟動jetty伺服器時,會調用其的start方法,用于初始化connector内部狀态,并打開connector以接受用戶端的請求(調用open方法);而在停止jetty伺服器時會調用其stop方法,以關閉connector以及内部元件(如connection等)以及做一些清理工作。因而open、close方法是connector中用于處理生命周期的方法;對每個connector都有name字段用于标記該connector,預設值為:hostname:port;connector中還有server的引用,可以從中擷取threadpool,并作為handler的容器被使用(在建立httpconnection時,server執行個體作為構造函數參數傳入,并在handlerequest()方法中将解析出來的request、response傳遞給server注冊的handler);connector還定義了一些用于配置目前connector的方法,如buffer size、max idle time、low resource max idle time,以及一些統計資訊,如目前connector總共處理過的請求數、總共處理過的連接配接數、目前打開的連接配接數等資訊。

connector的接口定義如下:

public interface connector extends lifecycle { 

    // connector名字,預設值hostname:port

    string getname();

    // 打開目前connector

    void open() throws ioexception;

    // 關閉目前connector

    void close() throws ioexception;

    // 對server的引用

    void setserver(server server);

    server getserver();

    // 在處理請求消息頭時使用的buffer大小

    int getrequestheadersize();

    void setrequestheadersize(int size);

    // 在處理響應消息頭時使用的buffer大小

    int getresponseheadersize();

    void setresponseheadersize(int size);

    // 在處理請求消息内容時使用的buffer大小

    int getrequestbuffersize();

    void setrequestbuffersize(int requestbuffersize);

    // 在處理響應消息内容使用的buffer大小

    int getresponsebuffersize();

    void setresponsebuffersize(int responsebuffersize);

    // 在處理請求消息時使用的buffer工廠

    buffers getrequestbuffers();

    // 在處理響應消息時使用的buffer工廠

    buffers getresponsebuffers();

    // user data constraint的配置可以是none、integral、confidential,對這三種值的解釋:

    // none:a value of none means that the application does not require any transport guarantees.

    // integral:a value of integral means that the application requires the data sent between the client and server to be sent in such a way that it can't be changed in transit.

    // confidential:a value of confidential means that the application requires the data to be transmitted in a fashion that prevents other entities from observing the contents of the transmission on.

    // 如果配置了user-data-constraint為integral或confidential表示所有相應請求都會重定向到使用integral/confidential schema/port建構的新的url中。

    int getintegralport();

    string getintegralscheme();

    boolean isintegral(request request);

    int getconfidentialport();

    string getconfidentialscheme();

    boolean isconfidential(request request);

    // 在将httpconnection交給server中的handlers處理前,根據目前connector,自定義一些endpoint和request的配置,如設定endpoint的maxidletime,request的timestamp,

    // 清除selectchannelendpoint中的idletimestamp,檢查forward頭等。

    void customize(endpoint endpoint, request request) throws ioexception;

    // 主要用于selectchannelconnector中,重置selectchannelendpoint中的idletimestamp,即重新計時idle的時間。

   // 它在每個request處理結束,endpoint還未關閉,并且目前連接配接屬于keep-alive類型的時候被調用。

    void persist(endpoint endpoint) throws ioexception;

    // 底層的連結執行個體,如serversocket、serversocketchannel等。

    object getconnection();

    //是否對"x-forwarded-for"頭進行dns名字解析

    boolean getresolvenames();

    // 目前connector綁定的主機名、端口号等。貌似在connector的實作中沒有一個預設的主機名。

    // 由于端口号可以設定為0,表示由作業系統随機的配置設定一個還沒有被使用的端口,因而這裡由localport用于存儲connector實際上綁定的端口号;

    // 其中-1表示這個connector還未開啟,-2表示connector已經關閉。

    string gethost();

    void sethost(string hostname);

    void setport(int port);

    int getport();

    int getlocalport();

    // socket的最大空閑時間,以及在資源比較少(如線程池中的任務數比最大可用線程數要多)的情況下的最大空閑時間,當空閑時間超過這個時間後關閉目前連接配接(socket)。

    int getmaxidletime();

    void setmaxidletime(int ms);

    int getlowresourcemaxidletime();

    void setlowresourcemaxidletime(int ms);

    // 是否目前connector處于lowresources狀态,即線程池中的任務數比最大可用線程數要多

    public boolean islowresources();

    /* ------------------------以下是一些擷取和目前connector相關的統計資訊------------------------------------ */

    // 打開或關閉統計功能

    public void setstatson(boolean on);

    // 統計功能的開閉狀态

    public boolean getstatson();

    // 重置統計資料,以及統計開始時間

    public void statsreset();

    // 統計資訊的開啟時間戳

    public long getstatsonms();

    // 目前connector處理的請求數

    public int getrequests();

    // 目前connector接收到過的連接配接數

    public int getconnections() ;

    // 目前connector所有目前還處于打開狀态的連接配接數

    public int getconnectionsopen() ;

    // 目前connector曆史上同時處于打開狀态的最大連接配接數

    public int getconnectionsopenmax() ;

    // 目前connector所有連接配接的持續時間總和

    public long getconnectionsdurationtotal();

    // 目前connector的最長連接配接持續時間

    public long getconnectionsdurationmax();

    // 目前connector平均連接配接持續時間

    public double getconnectionsdurationmean() ;

    // 目前connector所有連接配接持續時間的标準偏差

    public double getconnectionsdurationstddev() ;

    // 目前connector的所有連接配接的平均請求數

    public double getconnectionsrequestsmean() ;

    // 目前connector的所有連接配接的請求數标準偏差

    public double getconnectionsrequestsstddev() ;

    // 目前connector的所有連接配接的最大請求數

    public int getconnectionsrequestsmax();

}

jetty中所有的connector都繼承自abstractconnector,而它自身繼承自httpbuffers,httpbuffers包含了request、response的buffer工廠的建立以及相應size的配置。abstractconnector還包含了name、server、maxidletime以及一些統計資訊的引用,用于實作connector接口中的方法,隻是一些get、set方法,不詳述。除了connector接口相關的配置,abstractconnector還為定義了兩個字段:_acceptqueuesize用于表示serversocket 或serversocketchannel中最大可等待的請求數,_acceptors用于表示用于調用serversocket或serversocketchannel中accept方法的線程數,建議這個數字小于或等于可用處理器的2倍。

在abstractconnector中還定義了acceptor内部類,它實作了runnable接口,在其run方法實作中,它将自己的thread執行個體指派給abstractconnector中的_acceptorthread數組中acceptor号對應的bucket,并更新線程名為:<name> acceptor<index> <connector.tostring>。然後根據配置的_acceptorpriorityoffset設定目前線程的priority。隻要目前connector處于running狀态,并且底層連結執行個體不為null,不斷的調用accept方法()。在退出的finally語句快中清理_acceptorthread數組中相應的bucket值為null,并将線程原來的name、priority設定回來。

abstractconnector實作了dostart()方法,它首先保證server執行個體的存在;然後打開目前connector(調用其open()方法),并調用父類的dostart方法(這裡是httpbuffers,用于初始化對應的buffers);如果沒有自定義的threadpool,則從server中擷取threadpool;最後根據acceptors的值建立_acceptorthread數組,将acceptors個acceptor執行個體dispatch給threadpool。在dostop方法實作中,它首先調用close方法,然後對非server中的threadpool調用其stop方法,再調用父類的dostop方法清理buffers的引用,最後周遊_acceptorthread資料,調用每個thread的interrupt方法。

在各個子類的accept方法實作中,他們在擷取用戶端過來的socket連接配接後,都會對該socket做一些配置,即調用abstractconnector的configure方法,它首先設定socket的tcp_nodelay為true,即禁用nagle算法(關于禁用的理由可以參考:http://jerrypeng.me/2013/08/mythical-40ms-delay-and-tcp-nodelay/#sec-4-2,簡單的,如果該值為false,則tcp的資料包要麼達到tcp segment size,要麼收到一個ack,才會發送出去,即有delay);然後如果設定了_solingertime,則開啟socket中so_linger選項,否則,關閉該選項(so_linger選項用于控制關閉一個socket的行為,如果開啟了該選項,則在關閉socket時會等待_solingertime時間,此時如果有資料還未發送完,則會發送這些資料;如果關閉了該選項,則socket的關閉會立即傳回,此時也有可能繼續發送未發送完成的資料,具體參考:http://blog.csdn.net/factor2000/article/details/3929816)。

在serversocket和serversocketchannel中還有一個so_reuseaddr的配置,一般來說當一個端口被釋放後會等待兩分鐘再被使用,此時如果重新開機伺服器,可能會導緻啟動時的綁定錯誤,設定該值可以讓端口釋放後可以立即被使用(具體參考:http://www.cnblogs.com/mydomain/archive/2011/08/23/2150567.html)。在abstractconnector中可以使用setreuseaddress方法來配置,預設該值設定為true。

abstractconnector中還實作了customize方法,它在forwarded設定為true的情況下設定相應的attribute:javax.servlet.request.cipher_suite, javax.servlet.request.ssl_session_id,以及request中對應host、server等頭資訊。這個邏輯具體含義目前還不是很了解。。。。

最後關于統計資料的更新方法,abstractconnector定義了如下方法:

protected void connectionopened(connection connection)

protected void connectionupgraded(connection oldconnection, connection newconnection)

protected void connectionclosed(connection connection)

有了abstractconnector的實作,socketconnector的實作就變的非常簡單了,它儲存了一個endpoint的set,表示所有在這個connector下正在使用的endpoint,然後是serversocket,在open方法中建立,并在getconnection()方法中傳回,還有一個localport字段,當serversocket被建立時從serversocket執行個體中擷取,并在getlocalport()方法中傳回。在close方法中關閉serversocket,并設定localport為-2;在accept方法中,調用serversocket的accept方法,傳回一個socket,調用configure方法對新建立的socket做一些基本的配置,然後使用該socket建立connectorendpoint,并調用其dispatch方法;在customize方法中,在調用abstractconnector的customize方法的同時還設定connectorendpoint的maxidletime,即設定socket的so_timeout選項,用于配置該socket的空閑可等待時間;在dostart中會先清理connectorendpoint的集合,而在dostop中會關閉所有還處于打開狀态的connectorendpoint。

selectchannelconnector内部使用serversocketchannel,在open方法中建立serversocketchannel,配置其為非blocking模式,并設定localport值;在accept方法中調用serversocket的accept方法獲得一個socketchannel,配置該channel為非blocking模式,調用abstractchannel的configure方法做相應socket配置,最後将該socketchannel注冊給connectorselectmanager;在dostart方法中,它會初始化connectorselectmanager的selectsets值為acceptors值、maxidletime、lowresourceconnections、lowresourcesmaxidletime等值,并啟動該manager,并dispatch acceptors個線程,不斷的調用manager的doselect方法;在close方法中會先stop connectorselectmanager,然後關閉serversocketchannel,設定localport為-2;在customize方法中會清除selectchannelendpoint的idletimestamp,重置其maxidletime以及重置request中的timestamp的值;在persist方法中會重置selectchannelendpoint中idletimestamp的值。

blockingchannelconnector實作類似socketconnector,不同的是它使用serversocketchannel,并且其endpoint為blockingchannelendpoint。所不同的是它需要在dostart方法中啟動一個線程不斷的檢查所有還在connections集合中的blockingchannelendpoint是否已經逾時,每400ms檢查一次,如果逾時則關閉該endpoint。

其他的connector的實作都比較類似,而ssl相關的connector需要也隻是加入了ssl相關的邏輯,這裡不再贅述。

繼續閱讀