上善若水
          In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
          posts - 146,comments - 147,trackbacks - 0

          概述

          當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接口定義

          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();
          }

          Connection類圖



          HttpConnection實現

          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關閉。在該方法的實現中:
          1. 它首先設置_handling字段,表示當前正在處理,并且將當前HttpConnection實例設置到__currentConnection的ThreadLocal變量中。
          2. 循環處理請求消息直到EndPoint關閉或者在more_in_buffer為true(初始值為true)。
          3. 如果當前Request處于Async狀態,并且還Async狀態還沒有結束,直接調用handleRequest()方法,如果Async狀態結束了,但是HttpParser還沒有結束,則繼續使用HttpParser解析,在解析過程中,可能會在headerComplete()、content()、messageComplete()回調函數中調用handleRequest()方法(具體參見HttpParser的實現);而后在HttpGenerator已經Commit(所有的響應頭已經準備好,并已經寫入到EndPoint中),但是還沒有完成的情況下,將HttpGenerator中的數據Flush到EndPoint中;此時如果EndPoint還存在輸出緩存,則將其Flush到底層鏈路中。
          4. 如果當前Request沒有處于Async狀態,如果HttpPaser還沒有結束,使用HttpParser解析,在解析過程中,可能會在headerComplete()、content()、messageComplete()回調函數中調用handleRequest()方法(具體參見HttpParser的實現);而后在HttpGenerator已經Commit(所有的響應頭已經準備好,并已經寫入到EndPoint中),但是還沒有完成的情況下,持續的將HttpGenerator中的數據Flush到EndPoint中,如果EndPoint還存在輸出緩存,則將其Flush到底層鏈路中;如果HttpGenerator已經處于完成狀態,但是EndPoint中還有輸出緩存數據,此時將這些數據Flush到底層鏈路,如果寫完緩存中的數據,將progress設置為true,表示handle方法需要繼續處理。
          5. 在這些過成中如果出現任何HttpException,則使用HttpGenerator發送錯誤響應碼給客戶端(使用sendError()方法,并關閉EndPoint)。
          6. 如果HttpParser中還有數據未處理或者EndPoint中還有輸入數據未處理,則循環繼續。
          7. 如果此時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。
          8. 如果HttpParser處于idle狀態,并且EndPoint的InputStream已經關閉,則關閉當前EndPoint,并設置more_in_buffer為false。
          9. 如果Request的Async處于啟動狀態,則設置more_in_buffer為false。
          10. 如果EndPoint是AsyncEndPoint,Generator已經Commit,但是還未Complete,則該EndPoint schedule一個write操作。
          11. 最后,清理_handle字段和__currentConnection的ThreadLocal字段。

          handleRequest()是HttpConnection在對請求消息頭解析完成后執行的真正處理邏輯方法:
          1. 對任何Request還沒有處理完成,并且Server不為null且處于Running狀態,循環處理。
          2. 設置Request的handled為false,以及PathInfo字段(如果pathInfo為null,又不是Connect請求,則為400 Error)。
          3. 如果_out字段不為null,reopen it。
          4. 如果Request處于initial狀態,設置Request的DispatcherType為REQUEST,使用當前的EndPoint和Request實例配置Connector,并調用使用當前HttpConnection作為參數調用Server的handle()方法;否則,設置Request的DispatcherType為ASYNC,調用Server的handleAsync()方法(傳入當前HttpConnection做為參數)。
          5. 對任何非ContinuationThrowable異常,設置Request的handled為true,error為true,對HttpException使用Response發送響應狀態碼給客戶端,而對Throwable,使用HttpGenerator發送400或500狀態碼給客戶端。
          6. 如果此時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的執行流程:
          1. 在HttpGenerator還沒有Commit之前(即響應狀態行和響應消息頭還沒寫入到EndPoint中)時,先調用HttpGenerator的setResponse()方法設置狀態行。
          2. 然后調用HttpGenerator的completeHeader()方法將響應消息頭寫入到EndPoint中。
          3. 最后調用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中。
          posted on 2014-03-29 14:42 DLevin 閱讀(3030) 評論(5)  編輯  收藏 所屬分類: Jetty

          FeedBack:
          # 戀字坊
          2014-03-30 23:08 | 戀字坊
          學習了,先收藏一下,具體用的時候再看看  回復  更多評論
            
          # re: 深入Jetty源碼之Connection
          2014-03-31 09:00 | 萬利鎖業
          支持博主更新 啊  回復  更多評論
            
          # re: 深入Jetty源碼之Connection
          2014-04-08 16:52 | 真相帝
          雖然看不懂,但是感覺很厲害的樣子!!  回復  更多評論
            
          # re: 深入Jetty源碼之Connection
          2014-04-11 10:34 | 無添加護膚品
          // 返回Connection創建的時間戳。
          long getTimeStamp();  回復  更多評論
            
          # re: 深入Jetty源碼之Connection
          2014-05-06 10:43 | 同款網
          我也先收藏一下,寫得非常好,將來可以運用的同款網上使用,先收獲了很多  回復  更多評論
            
          主站蜘蛛池模板: 龙井市| 正定县| 宁武县| 泰安市| 剑河县| 海宁市| 云龙县| 桐柏县| 从江县| 隆林| 平昌县| 买车| 津市市| 通州区| 都安| 元江| 凤台县| 吉安市| 得荣县| 全州县| 旅游| 海晏县| 大埔区| 图们市| 大连市| 大同县| 丰宁| 碌曲县| 县级市| 牟定县| 上思县| 修水县| 内丘县| 岑溪市| 和林格尔县| 南漳县| 松桃| 安吉县| 梁平县| 游戏| 土默特左旗|