這篇主要整理深刻淺出Node.js第三章 異步I/Ojavascript
一) 異步I/O的緣由 java
二)異步I/O實現現狀node
2.1 異步I/O與非阻塞I/O瀏覽器
2.2 輪詢服務器
2.3 理想的非阻塞異步I/O多線程
2.4 現實的異步I/O併發
三)Node的異步I/O異步
3.1 事件循環ide
3.2 理解異步回調函數的執行過程函數
3.2.1 基礎
3.2.2 過程
四)事件驅動與高性能服務器
一) 異步I/O的緣由 異步I/O主要有如下兩點的需求
- 用戶體驗 同步的模式下,在瀏覽器中等待服務端數據的過程當中,瀏覽器頁面會鎖死 同時請求A B 同步的狀況下時間爲 time(A) + time(B) 異步的狀況下是 時間爲 Max(time(A),time(B)) 而且當請求不斷增加的時候,帶來的差別更大
- 異步I/O能更好的分配資源 異步I/O不會阻塞後續的運算,將原有的等待I/O的時間分配給其他的任務去執行
二) 異步I/O實現現狀
2.1 異步I/O與非阻塞I/O 對操做系統的內核來講只存在阻塞I/O和非阻塞I/O (我對書中這部分的理解是這樣的 好比異步I/O是node爲咱們提供的一層API接口,讓咱們能經過非阻塞的形式去I/O)
操做系統對輸入輸出設備抽象成文件,內核在進行文件的I/O操做的時候,經過文件描述符進行管理,也就是應用程序須要I/O的時候,須要先打開文件描述符,在根據文件描述符去實現文件的讀寫 非阻塞I/O和阻塞I/O的區別主要在於阻塞I/O直接完成整個數據獲取的流程,非阻塞I/O返回的是文件描述符,當須要獲取數據的時候,在經過文件描述符去讀取數據
2.2 輪詢 輪詢技術主要爲了解決非阻塞I/O什麼時候完成完整的I/O而出現的 下面是輪詢技術的演變過程
- read 它經過重複檢查I/O的狀態來完成完整數據的讀取 在獲得數據前 CPU一直處於等待的狀態
![](http://static.javashuo.com/static/loading.gif)
- select 它在read的基礎上進行了一些改進,經過對文件描述符的事件狀態進行判斷來完成數據的I/O 它採用一個1024長度的數據來存儲狀態,因此它只能同時檢測1024個文件描述符
![](http://static.javashuo.com/static/loading.gif)
- poll 它是在select基礎上進行的改進,它採用鏈表的方式存儲文件描述符,避免了數據的限制,可是在文件描述符不少的時候,性能有所降低
![](http://static.javashuo.com/static/loading.gif)
- epoll 該方案是Linux下最高的事件通知機制 當沒有I/O事件的時候,它會休眠(觀察者),充分的利用了事件通知,執行回調來代替遍歷查詢 不會浪費CPU 因此執行的效率更高
![](http://static.javashuo.com/static/loading.gif)
2.3 理想的非阻塞異步I/O 理想的應該是由應用程序發起異步方法,不須要遍歷或者事件喚醒等方式輪詢,直接進入下一個任務,在I/O完成後經過信號或者回調的方式將數據傳送給應用程序
![](http://static.javashuo.com/static/loading.gif)
2.4 現實的異步I/O
![](http://static.javashuo.com/static/loading.gif)
三) Node中的異步I/O
3.1 事件循環 在進程啓動的時候,Node會建立一個相似於while(true)的循環,每執行一次循環體稱爲Tick,每一次Tick的時候會查看是否有事件等待處理,若是有就取出事件而且執行相應的回調函數,沒有的話就退出進程 事件循環是典型的生產者消費者模型 在Windows下這個循環基於IOCP,而在*nix基於多線程建立
![](http://static.javashuo.com/static/loading.gif)
3.2 理解異步回調的執行過程
3.2.1 基礎
- 請求對象 從javascript發起調用到內核執行完I/O操做的過渡過程當中的中間產物 全部的狀態都保存在這個對象,包括送入線程池等待執行以及I/O操做完畢後的回調處理
- Node中的經典調用模式 javascript調用Node的核心模塊,核心模塊調用C++內建模塊 內建模塊經過libuv(跨平臺)進行系統調用
3.2.2 過程
- 從javascript到內建模塊的調用過程當中會建立一個請求對象,將javascript層傳入的參數和方法都封裝到這個請求對象上,回調函數會被設置到這個請求對象的oncomplete_sym上 ,供後續的調用,而後將這個請求對象推入線程池中等待執行,此時javascript的調用當即返回(底層的I/O仍然多是阻塞或者是非阻塞的)
- 線程池中的I/O執行完畢後,會將結果存儲在請求對象的result上,此時它會通知自己的執行狀態已經完畢,而且將線程歸還給線程池
- 在每次的Tick中,檢查是否有執行完的請求,若是有將請求對象加入I/O觀察者的隊列,而且當作事件進行處理
- I/O觀察者會取出事件回調函數而且將result當作參數傳遞給回調函數執行,這樣就達到了執行回調函數的目的
![](http://static.javashuo.com/static/loading.gif)
四)事件驅動與高性能服務器
幾種經典的服務器模型
- 同步式 一次只能處理一個請求,而且其他的請求都處於等待的狀態
- 每進程/每請求 爲每一個請求啓動一個進程,可是不具有擴展性,由於系統的資源只有那麼多
- 每線程/每請求 爲每一個請求啓動一個線程來處理,可是線程佔用必定的內存,大併發到來的時候,會形成系統運行緩慢
Node的高性能正是由於它的事件驅動模式.
![](http://static.javashuo.com/static/loading.gif)