Java NIO是否到處都適合?

      爲了實現高性能的MOM(消息中間件),學習了java的NIO,想利用NIO技術來改善網絡通訊的性能。學習NIO的時候,看了書上的,也找了網上相當多的資源,但大部分的資料都是比較粗淺的,入門級,深入的基本沒有。不過,對於瞭解和學習NIO來說已經足夠了。真正的領會還是需要在實踐中獲得的。

      NIO的技術在JDK1.4中出現。NIO最大的特點有兩個:一個是塊狀讀寫;另外一個是多路複用技術(multiplex)。第一個特點改變了老式IO中,每次讀寫只有一個字節(stream)和兩個字節(reader)的方式,提高了讀寫的速度;第二個特點改變了原來讀寫socket時候阻塞的同步狀況,使socket的讀寫具有的異步的功能。第二個特點比較重要,NIO的異步讀寫據說大大提高了網絡通訊的能力,儘管沒有C++的快,但至少不再象以前那樣,一說起java首先想到的是“性能低下”。這一點,在網上可以搜到一大把的資料。

        在JTangMQ消息中間件中,使用了NIO實現了通訊層。爲了方便起見,客戶端和服務器都採用了同一塊代碼,也就是說,客戶端和服務器端都是非阻塞的,編解碼和讀寫socket的方式都一樣。然而,在測試過程中發現,客戶端在向服務器發送消息的時候,cpu的佔用率在80%左右,帶寬只佔了5%(100M的帶寬);服務器端的配置比較高,cpu的佔用率在10%左右,帶寬佔有率一樣。通過java的profiler工具分析,發現客戶端的cpu時間主要是消耗在選擇器的select操作之上(NIO的多路複用),而select操作主要調用的是poll函數,這個函數是sun公司提供的關於選擇器的缺省類。根據網上的資料,這個操作是一個輪詢socket的同步操作,它的主要功能就是不斷地去查詢向該選擇器註冊的socket,看看有沒有可做的操作,如有,則返回,沒有,繼續不斷地輪詢。也就說,分配的cpu的時間基本上被消耗在poll上了。

       現在問題就出來了。既然poll佔用這麼多時間,是因爲沒有什麼東西可以寫入socket或者從socket中讀取;那麼,就應該給“提供讀寫數據的線程”更多的cpu時間,使得每一次的poll都有可作的操作,使時間耗費在讀寫socket之上,而不是poll之上。然而,從分析數據中看來,poll線程顯然比“提供讀寫數據的線程(wr線程)”佔用了更多的cpu時間,假設他們的優先級相同,也假設操作系統是公平的分配cpu時間,那麼它們得到的cpu時間應該是一樣的,此時,造成它們佔用cpu時間不同的原因就很可能是這樣:在相同的時間內,wr線程提供的數據量根本不夠poll線程處理,poll線程處理在相同的時間內處理這些數據綽綽有餘,那麼,poll線程更多的時間就耗費在輪詢上了。到這裏,我們假設的是這兩個線程分配到了相同的時間,所以,即使poll線程大部分時間是在做poll輪詢操作,那麼所佔用的時間最大值也是有限的,不會超過線程分配得到的時間。但是,如果此時儘管wr線程得到了cpu時間,但是它在處理過程中主動放棄cpu(比如執行wait操作),那麼,在cpu時間總量相等的情況下,由於poll線程沒有執行wait操作,因此,就會得到更多的cpu時間,這樣,就會出現上面的分析的情況,大部分的cpu時間都給poll的輪詢操作了。

        由此分析,如果使wr線程不斷的運行,而使poll線程在沒有數據的時候放棄cpu時間,就應該可以減少poll線程的輪詢操作,而使wr線程得到更多的cpu時間,提供更多的可供讀寫的數據,提高讀寫的效率。然而,由於poll輪詢操作是一個不間斷的過程,是sun公司提供的底層的api,是無法修改的。所以,就要放棄poll操作,也就是要放棄選擇器。換句話說,就是客戶端最好是不要採用非阻塞的方式。

        綜上所分析,如果客戶端採用非阻塞的方式,  應該是可以提高客戶端讀寫socket的速度的。而服務器端由於要同時連接大量的客戶端,還是繼續採用非阻塞方式爲好。

        這只是分析的結果,還沒有具體實驗過。不知道有沒有人遇到過類似的問題,請不吝賜教。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章