轉載請註明原文地址:http://www.javashuo.com/article/p-qivyngym-mv.htmlhtml
見另外一篇博文:http://www.javashuo.com/article/p-gmyidsnn-bu.html編程
同步:同步,就是在發出一個功能調用時,在沒有獲得結果以前,該調用就不返回。也就是必須一件一件事作,等前一件作完了才能作下一件事。
windows
異步:當一個異步過程調用發出後,調用者不能馬上獲得結果,能夠先作其餘事。當這個調用在完成後,經過狀態、通知和回調來通知調用者。設計模式
阻塞:阻塞調用是指調用結果返回以前,當前線程會被掛起(掛起:線程進入非可執行狀態,在這個狀態下,cpu不會給線程分配時間片,即線程暫停運行)。緩存
同步 不等於 阻塞:對於同步調用來講,不少時候當前線程仍是激活的,只是從邏輯上當前函數沒有返回,它仍是會搶佔cpu去執行其餘邏輯,也會主動檢測io是否準備好。服務器
非阻塞:調用函數時,在不能馬上獲得結果以前,該函數不會阻塞當前線程,而會馬上返回,在以後經過select通知調用者。網絡
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:同步並阻塞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事件,而且準備好數據後,在主動通知應用程序,觸發相應的函數。
一、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 來獲取操做執行的狀態,註冊監聽函數來執行完成後的操做。