1. 程式人生 > >Jetty9原始碼剖析 - Connection元件 - HttpInput

Jetty9原始碼剖析 - Connection元件 - HttpInput

轉載自ph0ly:http://www.ph0ly.com

一、概念

HttpInput是Jetty實現ServletInputStream的類,主要用於應用層對Request的輸入流操作,主要是請求體的操作

二、繼承體系

繼承體系

繼承自ServletInputStream,實現了Runnable,很簡單

三、原始碼剖析

1. 建立

建立

前面在HttpChannel章節講到過,HttpInput是HttpChannel建立Request物件的時候建立,會同時附帶一個HttpChannelState狀態物件,這個主要是控制非同步流程

2. 流讀取

其實對於HttpInput來說,最核心的就是流的讀取,我們重點就看下這個方法的實現

read

對於一個流來說,這個read方法就是比較核心的了,我們使用三方工具例如guava或者其他流讀取類,通常都會調到這個方法,我們可以看到這裡第一步對_inputQ加鎖,其實這個_inputQ就是一個緩衝佇列,Jetty這裡用了生產者消費者模式,如果這個時候_inputQ沒資料,就讓HttpConnection.fillAndParseForContent,要求EndPoint從Channel讀取資料,讀取完了同時會要求解析,這個時候的HttpParser其實的狀態其實是CONTENT(如果讀者不理解,可以回到HttpParser章節重溫一下),因此HttpParser呼叫HttpChannelOverHttp的content介面,如下圖

HttpChannel.content

這是HttpChannelOverHttp的content方法,會讓HttpConnection生成一個新的HttpConnection下面的Content,並呼叫onContent將這個Content放到HttpInput,如下圖

HttpConnection.onContent

這裡就直接從HttpChannelOverHttp拿到Request物件,再拿HttpInput,直接調addContent方法,接下來再來看HttpInput.addContent如何實現

addContent

可以看到其實就是往_inputQ放這個Content,而這個Content其實就包裝了這些資料的ByteBuffer,這裡利用佇列,保證HttpParser能不阻塞解析。當整個流讀完了,HttpParser會觸發HttpChannelOverHttp的onRequestComplete方法,該方法會呼叫HttpInput.eof方法,如下圖

eof

HttpInput的eof方法會呼叫addContent新增一個EOF_CONTENT,表示沒有資料可以讀了

再回到上面HttpInput.read方法,它呼叫了nextContent,要求從_inputQ讀取資料,如下圖

nextContent

pollContent

如果沒拿到,就呼叫produceContent

produceContent

其實就是調到了HttpConnection.fillAndParseForContent,如下圖

HttpConnection.fillAndParseForContent

上面也簡單提了下,這裡會要求EndPoint去讀資料,每讀取一塊緩衝後解析一次,直到整個HttpParser已經完成Content的解析,HttpParser會把解析後的Content放到_inputQ,同時最後放一個EOF_CONTENT,表示沒資料了

之後一直去調pollContent,拿到每一個Content

read-2

拿到Content後,會直接調get方法,把當前Content的資料拷貝到外面傳入的緩衝b裡面,然後consumeNonContent,如下圖,如果拿到的item是null,這裡會呼叫HttpInput的blockForContent,阻塞拿,後面拿到資料會通知_inputQ的執行緒繼續執行

consumeNoContent

這個方法其實就是從佇列移除掉剛才處理的Content,因為前面我們已經將緩衝拷貝出來了。最後再嘗試獲取新的Content,如果發現是 EofContent,這裡就會結束讀取

3. 回收

recycle

前面提到Request、Response、HttpInput、HttpOutput都是同一連線複用的,複用就需要回收,重置狀態,recycle就是HttpInput的重置方法,這裡會把_inputQ的Content全部通知失敗,然後其他的值恢復最開始的狀態

四、總結

HttpInput作為ServletInputStream的實現,主要完成了對請求體的處理,Jetty藉助於_inputQ佇列來隔離框架解析和應用層業務,是比較合理的做法,可以看出HttpInput最核心就是這個_inputQ的設計了。另外HttpInput還有一些其他的邏輯,一些非同步的處理,例如Servlet3.1提供了ReadListener這樣的監聽器,這篇文章並沒有提到,其實屬於Servlet3非同步的處理,後續有專題文章來分析,這裡為了便於大家理解,只講了同步,畢竟非同步了就會出現狀態機這樣複雜的操作,很容易搞暈的。這篇文章就到這裡,希望讀者對Jetty對輸入流的實現有一定的理解,接下來的文章我會分析HttpOutput以及HttpGenerator的實現,歡迎大家持續關注~