1. 程式人生 > >Java IO編程全解(六)——4種I/O的對比與選型

Java IO編程全解(六)——4種I/O的對比與選型

log jdk 狀態 來源 回調 現在 概念 core avr

  轉載請註明出處:http://www.cnblogs.com/Joanna-Yan/p/7804185.html

  前面講到:Java IO編程全解(五)——AIO編程

  為了防止由於對一些技術概念和術語的理解或者叫法不一致而引起歧義,這裏對涉及到的專業術語或者技術用語做下聲明:如果它們與其他一些地方的稱呼不一致,請以本解釋為準。

異步非阻塞I/O

  很多人喜歡將JDK1.4提供的NIO框架成為異步非阻塞I/O,但是,如果嚴格按照UNIX網絡編程模型和JDK的實現進行區分,實際上它只能被稱為非阻塞I/O,不能叫異步非阻塞I/O。在早期的JDK1.4和1.5 update10版本之前,JDK的Selector基於select/poll模型實現,它是基於I/O復用技術的非阻塞I/O,不是異步I/O。在JDK1.5 update10和Linux core2.6以上版本,Sun優化了Selector實現,它在底層使用epoll替換了select/poll,上層的API並沒有變化,可以認為是JDK NIO的一次性能優化,但是它仍舊沒有改變I/O的模型。

  由JDK1.7提供的NIO 2.0,新增了異步的套接字通道,它是真正的異步I/O,在異步I/O操作的時候可以傳遞信號變量,當操作完成之後會回調相關的方法,異步I/O也被稱為AIO。

  NIO類庫支持非阻塞讀和寫操作,相比於之前的同步阻塞讀和寫,它是異步的,NIO類庫支持非阻塞讀和寫操作,相比於之前的同步阻塞讀和寫,它是一部的,因此很多人習慣稱NIO為異步非阻塞I/O,包括很多介紹NIO編程的書籍也沿用了這個說法。為了符合大家的習慣,這裏也會將NIO稱為異步非阻塞I/O或者非阻塞I/O。請大家理解,不要過分糾結在一些技術術語的咬文嚼字上。

多路復用器Selector 

  幾乎所有的中文技術書籍都將Selector翻譯為選擇器,但是實際上我認為這樣的翻譯並不恰當,選擇器僅僅是字面上的意思,體現不出Selector的功能和特點。

  前面介紹過Java NIO的實現關鍵是多路復用I/O技術,多路復用的核心就是通過Selector來輪詢註冊在其上的Channel,當發現某個或者多個Channel處於就緒狀態後,從阻塞狀態返回就緒的Channel的選擇鍵集合,進行I/O操作。由於多路復用器是NIO實現非阻塞I/O的關鍵,它又是主要通過Selector實現的,所以這裏將Selector翻譯為多路復用器,與其他技術書籍所說的選擇器是同一個東西,請大家了解。

偽異步I/O

  偽異步的概念完全來源於實現。在JDK NIO編程沒有流行之前,為了解決Tomcat通信線程同步I/O導致業務線程被掛住的問題,大家想到了一個辦法:在通信線程和業務線程之前做個緩沖區,這個緩沖區用於隔離I/O線程和業務線程間的直接訪問,這樣業務線程就不會被I/O線程阻塞。而對於後端的業務側來說,將消息或者Task放到線程池後就返回了,它不再直接訪問I/O線程或者進行I/O讀寫,這樣就不會被同步阻塞。類似的設計還包括前端啟動一組線程,將接收到的客戶端封裝成Task,放到後端的線程池執行,用於解決一連接一線程問題。像這樣通過線程池做緩沖區的做法,這裏習慣於稱它為偽異步I/O,而官方並沒有偽異步I/O這種說法,請大家註意。

1. 不同I/O模型對比

  不同的I/O模型由於線程模型、API等差別很大,所以用法的差異也非常大。

技術分享

  實際開發中,具體選擇什麽樣的I/O模型或者NIO框架,完全基於業務的實際應用場景和性能訴求,如果客戶端並發連接數不多,周邊對接的網元不多,服務器的負載也不重,那就完全沒必要選擇NIO做服務器;如果是相反情況,那就要考慮選擇合適的NIO框架進行開發。

2.選擇Netty的理由

  開發出高質量的NIO程序並不是一件簡單的事情,除去NIO固有的復雜性和BUG不談,作為一個NIO服務器,需要能夠處理網絡的閃斷、客戶端的重復接入、客戶端的安全認證、消息的編解碼、半包讀寫等情況,如果你沒有足夠的NIO編程經驗積累,一個NIO框架的穩定往往需要半年甚至更長的時間。更為糟糕的是,一旦在生產環境中發生問題,往往會導致跨節點的服務調用中斷,嚴重的可能會導致整個集群環境都不可用,需要重啟服務器,這種非正常停機會帶來巨大的損失。

  從可維護性角度看,由於NIO采用了異步非阻塞編程模型,而且是一個I/O線程處理多條鏈路,它的調試和跟蹤非常麻煩,特別是生產環境中的問題,我們無法進行有效的調試和跟蹤,往往只能靠一些日誌來輔助分析,定位難度很大。

2.1不選擇Java原生NIO編程的原因

  現在我們總結一下我們不建議開發者直接使用JDK的NIO類庫進行開發,具體原因如下。

  1. NIO的類庫和API繁雜,使用麻煩,你需要熟練掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
  2. 需要具備其他的額外技能做鋪墊,例如熟悉Java多線程編程。這是因為NIO編程設計到Reactor模式,你必須對多線程和網絡編程非常熟悉,才能編寫出高質量的NIO程序。
  3. 可靠性能力補齊,工作量和難度都非常大。例如客戶端面臨斷連重連、網絡閃斷、半包讀寫、失敗緩存、網絡擁塞和異常碼流的處理等問題,NIO編程的特點是功能開發相對容易,但是可靠性能力補齊的工作量和難度都非常大。
  4. JDK NIO的BUG,例如臭名昭著的epoll bug,它會導致Selector空輪詢,最終導致CPU100%,官方聲稱在JDK1.6版本的update18修復了該問題,但是直到JDK1.7版本該問題仍舊存在,只不過該BUG發生頻率降低了一些而已,它並沒有被根本解決。

  由於上述原因,在大多數場景下,不建議大家直接使用JDK的NIO類庫,除非你精通NIO編程或者有特殊的需求。在絕大多數的業務場景中,我們可以使用NIO框架Netty來進行NIO編程,它既可以作為客戶端也可以作為服務端,同時支持UDP和異步文件傳輸,功能非常強大。

2.2為什麽選擇Netty

  Netty是業界最流行的NIO框架之一,它的健壯性、功能、性能、可定制性和可擴展性在同類框架中都是首屈一指的,它已經得到成百上千的商業項目驗證,例如Hadoop的RPC框架avro使用Netty作為底層通信框架;很多其他業界主流的RPC框架,也使用Netty來構建高性能的異步通信能力。

  Netty的優點總結如下:

  • API使用簡單,開發門檻低;
  • 功能強大,預置了多種編解碼功能,支持多種主流協議;
  • 定制能力強,可以通過ChannelHandler對通信框架進行靈活地擴展;
  • 性能高,通過與其他業界主流的NIO框架對比,Netty的綜合性能最優;
  • 成熟、穩定,Netty修復了已經發現的所有JDK NIO BUG,業務開發人員不需要再為NIO的BUG而煩惱;
  • 社區活躍,版本叠代周期短,發現的BUG可以被及時修復,同時,更多的新功能會加入;
  • 經歷了大規模的商業應用考驗,質量得到驗證。在互聯網、大數據、網絡遊戲、企業應用、電信軟件等眾多行業得到成功商用,證明了它已經完全能夠滿足不同行業的商業應用了。

  正是因為這些優點,Netty逐漸成為Java NIO編程的首選框架。

Netty(一)——Netty入門程序

如果此文對您有幫助,微信打賞我一下吧~

技術分享

Java IO編程全解(六)——4種I/O的對比與選型