天天看點

深入Jetty源碼之Connection

當jetty中的connector收到一個用戶端的連接配接時(serversocket或serversocketchannel的accept()方法傳回),connector會首先建立一個connectedendpoint用于和連接配接的底層(socket、channel)打交道(讀寫資料),在建立的connectedendpoint時會同時使用該endpoint建立相應類型的connection,然後會建立一個task仍給線程池,最終線程池會啟動一個線程啟動這個task,而在這個task中調用connection中的handle()方法,以處理目前的連接配接請求,在這個task中,它會持續的調用connection中的handle()方法直到連接配接關閉或connector停止。在connectorendpoint中,它自己就實作了runnable接口,因而可以将它自己丢給線程池,而在selectchannelendpoint中則交給selectormanager來管理用戶端連接配接過來的channel,并調用connection的handle方法。 

connection的定義如下: 

public interface connection {

    // connection中的核心邏輯,在httpconnection中,它使用httpparser解析請求資料,httpparser采用事件相應機制,可以通過注冊httpparser.eventhandler(requesthandler)填充httpconnection中的需要從請求消息中擷取的資訊,最後在messagecomplete()事件相應方法中調用handlerequest()方法将解析後的request請求交由server執行個體的handle()方法處理。在jetty中,server是handlerwrapper子類,它存儲了所有注冊的handler,進而将最終的處理流程傳導給所有注冊的handler。在所有注冊的handler處理完成後,connection中的handle()方法會繼續執行,使用httpgenerator、nestedgenerator将緩存的資料重新整理到endpoint中。如果目前請求的相應狀态是101(switching protocols),則handle方法傳回的connection執行個體是從注冊的以"org.eclipse.jetty.io.connection"為key的執行個體,也正是因為這個相應狀态碼的存在,這個handle方法的傳回值是一個connection。

    connection handle() throws ioexception;

    // 除了handle方法,connection中還有一些提供了一些包含connection狀态的方法:

    // 傳回connection建立的時間戳。

    long gettimestamp();

    // 目前connection是否處于idle狀态,如httpparser、httpgenerator都處于idle狀态。

    boolean isidle();

    // 目前connection是否處于suspended狀态,用于continuation機制。

    boolean issuspended();

    // 當connection關閉時會調用這個方法。

    void closed();

    // 當連接配接的idle時間逾時後調用該方法,在httpconnection中,該方法會關閉endpoiont。

    void idleexpired();

}

深入Jetty源碼之Connection

httpconnection是jetty中對connection的主要實作,它表示http用戶端和伺服器的一次連接配接,用于将request、response、endpoint聯系在一起。同時httpconnection也是在避免使用pooling的方式下重用request、response、httpparser、httpgenerator、httpfields(requestfields、responsefields)、buffer、httpuri等(因為jetty保證了每一次連接配接隻建立一個httpconnection執行個體,這是一個可以學習的點,不用pooling方式的重用,以進一步提升性能)。另外,httpconnection還有對connector和server執行個體的引用,并且用request字段記錄了該connection總共處理的請求數(在headercomplete回調函數中自增)。

如果請求包含expect頭,并且其值是100-continue,表示用戶端希望在請求被正真處理前發送一個響應以表示是否能處理該請求,因而在第一次調用getinputstream時表示伺服器已經準備好開始處理請求消息體了,此時在傳回servletinputstream之前,伺服器要發送100 continue響應消息給用戶端(通過調用httpgenerator中的send1xx()方法)。在jetty中,httpinput類繼承自servletinputstream,它從httpparser中讀取請求消息體資料。

如果請求頭包含expect頭,并且它的值是102-processing,此時伺服器可能會發送102狀态碼的響應,表示請求正在被處理,之後會發送最終的響應。在jetty中,可以通過response中的senderror()方法,傳入102的狀态碼以發送102狀态碼的響應(使用httpgenerator中的send1xx()方法)。

handle()方法是httpconnection中的核心方法,在每一個連接配接到來時,connector會建立一個runnable執行個體,将該runnable執行個體扔到線程池中,在該runnable的run()方法實作中不斷的調用connection的handle()方法直到目前連接配接或connector關閉。在該方法的實作中:

它首先設定_handling字段,表示目前正在處理,并且将目前httpconnection執行個體設定到__currentconnection的threadlocal變量中。

循環處理請求消息直到endpoint關閉或者在more_in_buffer為true(初始值為true)。

在這些過成中如果出現任何httpexception,則使用httpgenerator發送錯誤響應碼給用戶端(使用senderror()方法,并關閉endpoint)。

如果httpparser中還有資料未處理或者endpoint中還有輸入資料未處理,則循環繼續。

如果此時httpparser已經處理完成,httpgenerator已經處理完成,并且endpoint中的輸出緩存中已經沒有任何資料:1. 如果響應狀态碼時101 switching protocols,且在request存在org.eclipse.jetty.io.connection的connection執行個體,則新的connection從request的該attribute中擷取,并重置httpparser和httpgenerator;2. 如果request不存在該attribute的connection,httpgenerator非persisent狀态或endpoint的inputstream已經關閉,則重置httpparser,關閉endpoint,設定more_in_buffer為false,重置目前httpconnection。

如果httpparser處于idle狀态,并且endpoint的inputstream已經關閉,則關閉目前endpoint,并設定more_in_buffer為false。

如果request的async處于啟動狀态,則設定more_in_buffer為false。

如果endpoint是asyncendpoint,generator已經commit,但是還未complete,則該endpoint schedule一個write操作。

最後,清理_handle字段和__currentconnection的threadlocal字段。

handlerequest()是httpconnection在對請求消息頭解析完成後執行的真正處理邏輯方法:

對任何request還沒有處理完成,并且server不為null且處于running狀态,循環處理。

設定request的handled為false,以及pathinfo字段(如果pathinfo為null,又不是connect請求,則為400 error)。

如果_out字段不為null,reopen it。

如果request處于initial狀态,設定request的dispatchertype為request,使用目前的endpoint和request執行個體配置connector,并調用使用目前httpconnection作為參數調用server的handle()方法;否則,設定request的dispatchertype為async,調用server的handleasync()方法(傳入目前httpconnection做為參數)。

對任何非continuationthrowable異常,設定request的handled為true,error為true,對httpexception使用response發送響應狀态碼給用戶端,而對throwable,使用httpgenerator發送400或500狀态碼給用戶端。

如果此時request處于為完成狀态,調用asynccontinuation.docomplete()方法;如果100 continue響應沒有發送給用戶端,則清除該狀态,但是如果此時response還沒有commit,則設定httpgenerator的persistent為false,表示用戶端并沒有發送資料過來,我們可以關閉該連接配接了;如果endpoint關閉了,則調用response的complete方法;如果endpoint沒有關閉并且有error,直接關閉endpoint;如果endpoint沒有關閉,也沒有error,但是httpgenerator沒有commit,request也沒有被handle,則使用resonse發送404 resource not found響應消息,之後調用response的complete方法;最後設定request的handled為true。

commitresponse()方法,用于控制httpgenerator的執行流程:

在httpgenerator還沒有commit之前(即響應狀态行和響應消息頭還沒寫入到endpoint中)時,先調用httpgenerator的setresponse()方法設定狀态行。

然後調用httpgenerator的completeheader()方法将響應消息頭寫入到endpoint中。

最後調用httpgenerator的complete方法,不斷的将httpgenerator中的緩存寫入到endpoint中。

flushresponse()方法隻是調用了commitresponse方法。 

httpoutput時jetty中繼承自servletoutputstream的類,它使用abstractorgenerator向底層endpoint中寫入資料。

output時httpconnection中的内部類,它繼承自httpoutput,它在調用close/flush時會先調用commitresponse/flushreponse方法,保證響應消息先寫狀态行,然後是響應消息頭,最後才是響應消息體。該類還實作了sendcontent方法,其參數可以是httpcontent類型或resource類型,該方法是一個util方法,它會自動設定content-type、content-length、last-modified等頭,并将httpcontent或resource對應的資料寫入到endpoint中。