轉載請註明出處:http://www.cnblogs.com/Joanna-Yan/p/7804185.html 前面講到:Java IO編程全解(五)——AIO編程 為了防止由於對一些技術概念和術語的理解或者叫法不一致而引起歧義,這裡對涉及到的專業術語或者技術用語做下聲明:如果它們與其他一些地方的 ...
轉載請註明出處:http://www.cnblogs.com/Joanna-Yan/p/7804185.html
為了防止由於對一些技術概念和術語的理解或者叫法不一致而引起歧義,這裡對涉及到的專業術語或者技術用語做下聲明:如果它們與其他一些地方的稱呼不一致,請以本解釋為準。
非同步非阻塞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類庫進行開發,具體原因如下。
- NIO的類庫和API繁雜,使用麻煩,你需要熟練掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
- 需要具備其他的額外技能做鋪墊,例如熟悉Java多線程編程。這是因為NIO編程設計到Reactor模式,你必須對多線程和網路編程非常熟悉,才能編寫出高質量的NIO程式。
- 可靠性能力補齊,工作量和難度都非常大。例如客戶端面臨斷連重連、網路閃斷、半包讀寫、失敗緩存、網路擁塞和異常碼流的處理等問題,NIO編程的特點是功能開發相對容易,但是可靠性能力補齊的工作量和難度都非常大。
- 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編程的首選框架。
如果此文對您有幫助,微信打賞我一下吧~