Java IO全面

 

轉載請註明原文地址:http://www.javashuo.com/article/p-qivyngym-mv.htmlhtml

 

一:IO流梳理——字符流、字節流、輸入流、輸出流

  見另外一篇博文:http://www.javashuo.com/article/p-gmyidsnn-bu.html編程

  

 

二:同步&異步、阻塞&非阻塞的概念

  同步:同步,就是在發出一個功能調用時,在沒有獲得結果以前,該調用就不返回。也就是必須一件一件事作,等前一件作完了才能作下一件事。
windows

  異步:當一個異步過程調用發出後,調用者不能馬上獲得結果,能夠先作其餘事。當這個調用在完成後,經過狀態、通知和回調來通知調用者。設計模式

  同步與異步 是 調用與結果 的關係。

 

  阻塞:阻塞調用是指調用結果返回以前,當前線程會被掛起(掛起:線程進入非可執行狀態,在這個狀態下,cpu不會給線程分配時間片,即線程暫停運行)。緩存

     同步 不等於 阻塞:對於同步調用來講,不少時候當前線程仍是激活的,只是從邏輯上當前函數沒有返回,它仍是會搶佔cpu去執行其餘邏輯,也會主動檢測io是否準備好服務器

  非阻塞:調用函數時,在不能馬上獲得結果以前,該函數不會阻塞當前線程,而會馬上返回,在以後經過select通知調用者。網絡

  阻塞與非阻塞 是 調用與線程狀態 的關係。

 

三:Linux的5 種 IO 模型

  1)阻塞I/O(blocking I/O)多線程

  應用程序調用一個IO函數,致使應用程序阻塞,等待數據準備好。 若是數據沒有準備好,一直等待….直到數據準備好了,從內核拷貝到用戶空間,IO函數返回成功指示。

  2)非阻塞I/O (nonblocking I/O)架構

  非阻塞IO經過進程反覆調用IO函數(屢次系統調用,並立刻返回),而在IO過程當中,進程是阻塞的。框架

  咱們把一個SOCKET接口設置爲非阻塞的意思就是:告訴內核,當所請求的I/O操做沒法完成時,不要將進程睡眠,而是返回一個錯誤。這樣咱們的I/O操做函數將不斷的測試數據是否已經準備好,若是沒有準備好,繼續測試,直到數據準備好爲止。在這個不斷測試的過程當中,會大量的佔用CPU的時間。


  3) I/O複用(select 和poll) (I/O multiplexing)  

  能實現同時對多個IO端口進行監聽; I/O複用模型會用到select、poll、epoll函數,這幾個函數也會使進程阻塞,可是和阻塞I/O所不一樣的的,這兩個函數能夠同時阻塞多個I/O操做。並且能夠同時對多個讀操做,多個寫操做的I/O函數進行檢測,直到有數據可讀或可寫時,才真正調用I/O操做函數。


  4)信號驅動I/O (signal driven I/O (SIGIO))
  首先咱們容許進程進行信號驅動I/O,並定義一個信號處理函數;

  當數據準備好時,進程會收到一個SIGIO信號,能夠在信號處理函數中調用I/O操做函數處理數據。

 

  以上四種其實都是同步IO。


  5)異步I/O (asynchronous I/O (the POSIX aio_functions))

  當一個異步過程調用發出後,調用者不能馬上獲得結果,它轉身去作其餘事情;

  被調用者在執行完畢後,經過狀態、通知、回調等信息,通知調用者作完了,而後調用者再接着以前的工做往下進行。

 

 

三:BIO、NIO 和 AIO 的區別

  BIO:同步並阻塞IO,一個鏈接一個線程。

 

 NIO:輪詢,通道有請求就調用對應處理函數。

 「多路複用IO+同步非阻塞IO」,一個單線程Selector阻塞輪詢,找到有數據的channel進行非阻塞IO。

 NIO是基於事件驅動模式的IO,Reactor模式是事件驅動模式的實現,主要原理見:https://www.jianshu.com/p/eef7ebe28673

 Selector能夠輪詢多個Channel,由於JDK使用了epoll()代替傳統的select實現,沒有最大鏈接句柄限制,因此只須要一個線程負責Selector的輪詢,就能夠接入成千上萬的客戶端。

 應用程序向selector註冊一個channel,由selector不斷輪詢,若是發現有某個channel發生鏈接請求,就會通知相應的處理線程。

 

 AIO:訂閱,操做系統有IO就通知程序處理。

 AIO框架在windows下使用windows IOCP技術,在Linux下使用epoll多路複用IO技術模擬異步IO。

 應用程序向操做系統註冊IO監聽,而後繼續作本身的事情。操做系統發生IO事件,而且準備好數據後,在主動通知應用程序,觸發相應的函數。

 

四:Netty框架

  一、Java原生NIO存在的問題

  1)NIO 的類庫和 API 繁雜,使用麻煩:你須要熟練掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等。

  2)須要具有其餘的額外技能作鋪墊:例如熟悉 Java 多線程編程,由於 NIO 編程涉及到 Reactor 模式,你必須對多線程和網路編程很是熟悉,才能編寫出高質量的 NIO 程序。

  3)可靠性能力補齊,開發工做量和難度都很是大:例如客戶端面臨斷連重連、網絡閃斷、半包讀寫、失敗緩存、網絡擁塞和異常碼流的處理等等。NIO 編程的特色是功能開發相對容易,可是可靠性能力補齊工做量和難度都很是大。

  4)JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug,它會致使 Selector 空輪詢,最終致使 CPU 100%。官方聲稱在 JDK 1.6 版本的 update 18 修復了該問題,可是直到 JDK 1.7 版本該問題仍舊存在,只不過該 Bug 發生機率下降了一些而已,它並無被根本解決。

 

  二、Netty適用場景

  Netty 對 JDK 自帶的 NIO 的 API 進行了封裝,主要使用場景以下:

  1)互聯網行業:在分佈式系統中,各個節點之間須要遠程服務調用,高性能的 RPC 框架必不可少,Netty 做爲異步高性能的通訊框架,每每做爲基礎通訊組件被這些 RPC 框架使用。

    典型的應用有:阿里分佈式服務框架 Dubbo 的 RPC 框架使用 Dubbo 協議進行節點間通訊,Dubbo 協議默認使用 Netty 做爲基礎通訊組件,用於實現各進程節點之間的內部通訊。

  2)大數據領域:經典的 Hadoop 的高性能通訊和序列化組件 Avro 的 RPC 框架,默認採用 Netty 進行跨界點通訊,它的 Netty Service 基於 Netty 框架二次封裝實現。

 

  三、Netty的高性能設計

  Netty 是異步事件驅動的,高性能之處主要來自於其 I/O 模型和線程處理模型,前者決定如何收發數據,後者決定如何處理數據

  1)基於 I/O多路複用模式

  Netty 的非阻塞 I/O 的實現關鍵是基於 I/O 複用模型:

  

 

  2)面向Buffer的數據讀寫

  傳統的 I/O 是面向字節流或字符流的,以流式的方式順序地從一個 Stream 中讀取一個或多個字節, 所以也就不能隨意改變讀取指針的位置。

  在 NIO 中,拋棄了傳統的 I/O 流,而是引入了 Channel 和 Buffer 的概念L:在 NIO 中,只能從 Channel 中讀取數據到 Buffer 中或將數據從 Buffer 中寫入到 Channel,能夠隨意地讀取任意位置的數據。

 

  3)事件驅動的線程模型

  發生事件時,主線程把事件放入事件隊列。

  在另外的線程中不斷循環消費事件列表中的事件,調用事件對應的處理邏輯處理事件。

  事件驅動方式也被稱爲消息通知方式,實際上是設計模式中觀察者模式的實現

  包括 4 個基本組件:

  1)事件隊列(event queue):接收事件的入口,存儲待處理事件;

  2)分發器(event mediator):將不一樣的事件分發到不一樣通道;

  3)事件通道(event channel):分發器與業務處理器之間的聯繫渠道;

  4)事件處理器(event processor):實現業務邏輯,處理完成後會發出事件,觸發下一步操做。

  此模式的優勢:

  1)可擴展性好:基於分佈式的異步架構,事件與處理器之間高度解耦,能夠方便擴展事件處理邏輯;

  2)高性能:基於隊列暫存事件,能方便並行異步處理事件。

 

  NIO的線程模型——Reactor模型(反應堆模型)

  服務端程序處理傳入多路請求,並將它們同步分派給請求對應的處理線程.

  Reactor 模式也叫 Dispatcher 模式,即 I/O 多路複用統一監聽事件,收到事件後分發(Dispatch 給某進程),是編寫高性能網絡服務器的必備技術之一。

  Reactor 模型中有 2 個關鍵組成:

  1)Reactor:Reactor 在一個單獨的線程中運行,負責監聽和分發事件,分發給適當的處理程序來對 IO 事件作出反應。

  2)Handlers:處理程序執行 I/O 事件要完成的實際事件,Reactor 經過調度適當的處理程序來響應 I/O 事件,處理程序執行非阻塞操做。

 

  Netty的線程模型——基於NIO的主從 Reactors 多線程模型做進一步修改

  a)MainReactor 負責客戶端的鏈接請求,並將請求轉交給 SubReactor;

  b)SubReactor 負責相應通道的 IO 讀寫請求;

  c)非 IO 請求(具體邏輯處理)的任務則會直接寫入隊列,等待 worker threads 進行處理。

 

  4)基於異步的事件處理方式

  Netty 中的 I/O 操做是異步的,包括 Bind、Write、Connect 等操做會簡單的返回一個 ChannelFuture。

  調用者並不能馬上得到結果,而是經過 Future-Listener 機制,用戶能夠方便的主動獲取或者經過通知機制得到 IO 操做結果

  當 Future 對象剛剛建立時,處於非完成狀態,調用者能夠經過返回的 ChannelFuture 來獲取操做執行的狀態,註冊監聽函數來執行完成後的操做。

相關文章
相關標籤/搜索