現實比理想要骨感一些,但是要達成異步I/O的目標,并非難事。前面我們將場景限定在了單線程的狀況下,多線程的方式會是另一番風景。通過讓部分線程進行阻塞I/O或者非阻塞I/O加輪詢技術來完成數據獲取,讓一個線程進行計算處理,通過線程之間的通信將I/O得到的數據進行傳遞,這就輕松實現了異步I/O(盡管這是模擬的),示意圖如下:

glibc的AIO便是典型的線程池模擬異步I/O。然而遺憾的是,它存在一些難以忍受的缺陷和bug,不推薦采用。libev的作者 Marc Alexander Lehmann 重新實現了一個異步I/O的庫:libeio。libeio實質上依然是采用線程池與阻塞I/O模擬異步I/O。最初,Node在 `*nix`平臺上采用了libeio配合libev實現I/O部分,實現了異步I/O。在Node v0.9.3中,自行實現了線程池來完成異步I/O。
另一種我遲遲沒有透露的異步I/O方案則是Windows下的IOCP,它在某種程度上提供了理想的異步I/O:調用異步方法,等待I/O完成之后的通知,執行回調,用戶無須考慮輪詢。但是它的內部其實仍然是線程池原理,不同之處在于這些線程池由系統內核接手管理。
IOCP的異步I/O模型與Node的異步調用模型十分近似。在Windows平臺下采用了IOCP實現異步I/O。
由于Windows平臺和`*nix`平臺的差異,Node提供了libuv作為抽象封裝層,使得所有平臺兼容性的判斷都由這一層來完成,并保證上層的Node與下層的自定義線程池及IOCP之間各自獨立。Node在編譯期間會判斷平臺條件,選擇性編譯unix目錄或是win目錄下的源文件到目標程序中,架構如下圖:

需要強調的一點是,這里的I/O不僅僅只限于磁盤文件的讀寫。`*nix`將計算機抽象了一番,磁盤文件、硬件、套接字等幾乎所有計算機資源都被抽象為了文件,因此這里描述的阻塞和非阻塞的情況同樣能適合于套接字等。
另一個需要強調的地方在于,我們時常提到的Node是單線程的,這里的單線程僅僅只是JavaScript執行在單線程中罷了。在Node中,無論是 `*nix`還是Windows平臺,內部完成I/O任務的另有線程池。
- 目錄
- 第1章 Node 簡介
- 1.1 Node 的誕生歷程
- 1.2 Node 的命名與起源
- 1.2.1 為什么是 JavaScript
- 1.2.2 為什么叫 Node
- 1.3 Node給JavaScript帶來的意義
- 1.4 Node 的特點
- 1.4.1 異步 I/O
- 1.4.2 事件與回調函數
- 1.4.3 單線程
- 1.4.4 跨平臺
- 1.5 Node 的應用場景
- 1.5.1 I/O 密集型
- 1.5.2 是否不擅長CPU密集型業務
- 1.5.3 與遺留系統和平共處
- 1.5.4 分布式應用
- 1.6 Node 的使用者
- 1.7 參考資源
- 第2章 模塊機制
- 2.1 CommonJS 規范
- 2.1.1 CommonJS 的出發點
- 2.1.2 CommonJS 的模塊規范
- 2.2 Node 的模塊實現
- 2.2.1 優先從緩存加載
- 2.2.2 路徑分析和文件定位
- 2.2.3 模塊編譯
- 2.3 核心模塊
- 2.3.1 JavaScript核心模塊的編譯過程
- 2.3.2 C/C++核心模塊的編譯過程
- 2.3.3 核心模塊的引入流程
- 2.3.4 編寫核心模塊
- 2.4 C/C++擴展模塊
- 2.4.1 前提條件
- 2.4.2 C/C++擴展模塊的編寫
- 2.4.3 C/C++擴展模塊的編譯
- 2.4.2 C/C++擴展模塊的加載
- 2.5 模塊調用棧
- 2.6 包與NPM
- 2.6.1 包結構
- 2.6.2 包描述文件與NPM
- 2.6.3 NPM常用功能
- 2.6.4 局域NPM
- 2.6.5 NPM潛在問題
- 2.7 前后端共用模塊
- 2.7.1 模塊的側重點
- 2.7.2 AMD規范
- 2.7.3 CMD規范
- 2.7.4 兼容多種模塊規范
- 2.8 總結
- 2.9 參考資源
- 第3章 異步I/O
- 3.1 為什么要異步I/O
- 3.1.1 用戶體驗
- 3.1.2 資源分配
- 3.2 異步I/O實現現狀
- 3.2.1 異步I/O與非阻塞I/O
- 3.2.2 理想的非阻塞異步I/O
- 3.2.3 現實的異步I/O
- 3.3 Node的異步I/O
- 3.3.1 事件循環
- 3.3.2 觀察者
- 3.3.3 請求對象
- 3.3.4 執行回調
- 3.3.5 小結
- 3.4 非I/O的異步API
- 3.4.1 定時器
- 3.5 事件驅動與高性能服務器