Apache Tomcat 8 中的 NIO 2

類別: IT

Apache Tomcat 8 有一個新的基於 NIO 2 的聯結器正接近正常的使用狀態,而現在被標記成測試版. NIO 2 不僅向 Servlet 3.1的非同步IO看齊,它還不止這一個好處.

速度

首先,是一個快速的速度測試. 原生的速度使用一個Servlet寫1KB的資料來衡量, 使用 ab -k -c 100 (啟用超過100個併發連線並保持存活) 以讓其只去測量做一次快的寫入和在兩個請求之前保持連線. 明顯這是一個可怕的環境標準,但是這個主意只是要看看 NIO 2 是不是足夠快, 因為在你注意到它的高階API的時候,它看起來是有點慢的. 這可能會消除NIO 2作為一個很有用的解決方案在你心目中的存在感,因為Tomcat中已經存在一個穩定的NIO聯結器了, 不過在可選範圍的另外一端,APR是接近原生速度的. 我很高興的像大家報告 NIO 2 顯著地比 NIO 在這種純塊/輪詢的壓力測試上要快, 要快上大約50%, 並且相當於APR做這種任務的速度.

在這個關鍵問題有了結果之後,我們就有了一個比目前的聯結器更優雅的選擇, 因為NIO和APR的輪詢管理,NIO的阻塞IO和APR的原生程式碼已經被證明存在看似無休止的複雜、思索、奔潰、平臺特定等諸多問題.

不過,儘管一些初步的薄弱環節已經確定可以使用JSSE和靜態檔案服務(見下文)來解決,其在現實世界的好處和資源消耗怎麼樣現在還是個未知數. 隨著執行緒和輪詢管理被完全的抽象出來,JVM最終將會把一切掌控起來,已提供優化的行為.

一個簡單的API

那麼它是一個簡單的API嗎 ? 實際上,只有阻塞IO使用NIO 2來做才非常簡單. 像使用NIO一樣,一次讀或者寫會立即返回結果, 但是不同於 NIO 這種操作沒必要是完整的,它還可以繼續非同步存在與程式中. 為了能有所顯示,最基本的讀/寫API都使用到了一個可以在一旁被輪詢(這是一個糟糕的點子)或者阻塞的Future物件.因此,簡單的帶有操作時效的阻塞,看起來不錯.

"非阻塞"由於在Servlet 3.1中被引入,需要使用更加複雜的使用任務完成處理程式來通知操作現在已經完成的API. 那同樣也是聽起來很簡單,但是有特殊的問題需要處理,因為NIO 2 API不會提供讓處理那種問題更簡單的所有東西. 一次呼叫可以完全是(很明顯也可以不是)完全內聯的, 同步是不直觀的 (當一個操作被掛起是,沒有程式碼塊會同步上, 不過看樣子一些像buffer這樣的重要的物件的狀態沒有被定義;死鎖的風險也還存在), 不完整的操作是可能的,等等.

API 確實對一些更加重要的IO進行了優化,使用分散和集中. 我嘗試去利用Tomcat中後者的優勢,未來可以在其上做更多的工作.

為什麼NIO 2 會更好

NIO 2看起來簡單,快速且直觀, 但它內部的一些東西仍有待改進.

傳送檔案的支援

NIO 的transferTo API並不被NIO 2 非同步通道所支援, 並且我不認為這樣是明智的. 因此, 儘管NIO 2聯結器的原始速度不慢, 並且在大多數情況下它也足夠快速, 但它仍不是最高效的檔案伺服器. 雖然無關緊要,但因為實現起來也不太費事, 所以這是個不幸.

JSSE整合

與使用SSL引擎的API類似, 通過NIO可以提供良好控制和非阻塞能力. 但所有人都要寫相似的非同步通道封裝程式碼. 而JSSE通道程式碼已經被NIO 2包括了.

JSSE (無) 速度

與OpenSSL相比, JSSE仍跟以前一樣慢. 雖然到目前為止你已經對這傷心事有了免疫力, JSSE 現在仍看起來是在浪費伺服器資源. 然而,這個 JVM元件是可撥插的, 所以我們看以後是否會有所改善.

更好的狀態控制

當使用completion handler時沒法做像查詢操作狀態這樣的基本的事, 除非使用Future. 待定標識可以在其他地方獲得, 並且實際上這個標識是一個與future(能夠等候待定操作完成)共享的int型別的訊號燈. 最後, 雖然這看起來挺直觀也沒有什麼困難的, 但它會導致比需要的更為複雜.

所以NIO 2仍有改進的空間. 下面, 期待它的發生!

Apache Tomcat 8 中的 NIO 2原文請看這裡