Node.js入門:非同步IO

來源:互聯網
上載者:User

標籤:style   blog   class   code   c   java   

非同步IO

    在作業系統中,程式啟動並執行空間分為核心空間和使用者空間。我們常常提起的非同步I/O,其實質是使用者空間中的程式不用依賴核心空間中的I/O操作實際完成,即可進行後續任務。

同步IO的並行模式

  • 多線程單進程
    多線程的設計之處就是為了在共用的程式空間中,實現平行處理任務,從而達到充分利用CPU的效果。多線程的缺點在於執行時上下文交換的開銷較大,和狀態同步(鎖)的問題。同樣它也使得程式的編寫和調用複雜化。
  • 單線程多進程
    為了避免多線程造成的使用不便問題,有的語言選擇了單線程保持調用簡單化,採用啟動多進程的方式來達到充分利用CPU和提升總體的平行處理能力。它的缺點在於商務邏輯複雜時(涉及多個I/O調用),因為商務邏輯不能分布到多個進程之間,交易處理時間長度要遠遠大於多線程模式。

非同步IO的必要性

    採用同步方式的程式要完成這兩個任務的時間總花銷會是m + n。但是如果是採用非同步方式的程式,在兩種I/O可以並行的狀況下(比如網路I/O與檔案I/O),時間開銷將會減小為max(m, n)。而當並行任務更多的時候,m + n + …與max(m, n, …)之間的孰優孰劣更是一目瞭然。Node.js天然地支援這種非同步I/O,這是眾多雲端運算廠商對其青睞的根本原因。

作業系統對非同步I/O的支援

    非同步與非阻塞聽起來似乎是同一回事。從實際效果的角度說,非同步和非阻塞都達到了我們並行I/O的目的。但是從電腦核心I/O而言,非同步/同步和阻塞/非阻塞實際上時兩回事。
  • I/O的阻塞與非阻塞
    阻塞模式的I/O會造成應用程式等待,直到I/O完成。同時作業系統也支援將I/O操作設定為非阻塞模式,這時應用程式的調用將可能在沒有拿到真正資料時就立即返回了,為此應用程式需要多次調用才能確認I/O操作完全完成。
  • I/O的同步與非同步
    I/O的同步與非同步出現在應用程式中。如果做阻塞I/O調用,應用程式等待調用的完成的過程就是一種同步狀況。相反,I/O為非阻塞模式時,應用程式則是非同步。

非同步I/O與輪詢技術

    當進行非阻塞I/O調用時,要讀到完整的資料,應用程式需要進行多次輪詢,才能確保讀取資料完成,以進行下一步的操作。    輪詢技術的缺點在於應用程式要主動調用,會造成佔用較多CPU時間片,效能較為低下。現存的輪詢技術有以下這些:
  • read:通過重複調用來檢查I/O的狀態來完成完整資料讀取,效能也是最低的一種。
  • select:通過對檔案描述符上的事件狀態來進行判斷。
  • poll 
  • epoll 
  • pselect 
  • kqueue
    輪詢技術滿足了非同步I/O確保擷取完整資料的保證。但是對於應用程式而言,它仍然只能算時一種同步,因為應用程式仍然需要主動去判斷I/O的狀態,依舊花費了很多CPU時間來等待。

理想的非同步I/O模型

    理想的非同步I/O應該是應用程式發起非同步呼叫,而不需要進行輪詢,進而處理下一個任務,只需在I/O完成後通過訊號或是回調將資料傳遞給應用程式即可。  

不同作業系統的非同步IO方案

  • Linux
    在Linux下存在一種這種方式,它原生提供了一種非同步非阻塞I/O方式(AIO)即是通過訊號或回調來傳遞資料的。不幸的是,只有Linux下有這麼一種支援,而且還有缺陷(AIO僅支援核心I/O中的O_DIRECT方式讀取,導致無法利用系統緩衝.    另一種理想的非同步I/O是採用阻塞I/O,但加入多線程,將I/O操作分到多個線程上,利用線程之間的通訊來類比非同步.    Linux平台下沒有完美的非同步I/O支援。所幸的是,libev的作者Marc Alexander Lehmann重新實現了一個非同步I/O的庫:libeio。libeio實質依然是採用線程池與阻塞I/O類比出來的非同步I/O。
  • Windows
    Windows有一種專屬的核心非同步IO方案:IOCP。IOCP的思路是真正的非同步I/O方案,調用非同步方法呼叫,然後等待I/O完成通知。IOCP內部依舊是通過線程實現,不同在於這些線程由系統核心接手管理。IOCP的非同步模型與Node.js的非同步呼叫模型已經十分近似。

Node.js的非同步IO方案

    由於Windows平台和*nix平台的差異,Node.js提供了libuv來作為抽象封裝層,使得所有平台相容性的判斷都由這一層次來完成,保證上層的Node.js與下層的libeio/libev及IOCP之間各自獨立。Node.js在編譯期間會判斷平台條件,選擇性編譯unix目錄或是win目錄下的源檔案到目標程式中。

Node.js的非同步IO模型

    Node.js的回呼函數究竟是如何被調用的?在檔案I/O這一塊與普通的商務邏輯的回呼函數不同在於它不是由我們自己的代碼所觸發,而是系統調用結束後,由系統觸發的。    下面我們以最簡單的fs.open方法來作為例子,探索Node.js與底層之間是如何執行非同步I/O調用和回呼函數究竟是如何被調用執行的。
1 fs.open = function(path, flags, mode, callback) { 2     callback = arguments[arguments.length - 1]; 3     if (typeof(callback) !== ‘function‘) { 4         callback = noop;5     }6     mode = modeNum(mode, 438 /*=0666*/); 7     binding.open(pathModule._makeLong(path), stringToFlags(flags), mode, callback); };

    fs.open的作用是根據指定路徑和參數,去開啟一個檔案,從而得到一個檔案描述符,是後續所有I/O操作的初始操作。

     在JavaScript層面上調用的fs.open方法最終都透過node_file.cc調用到了libuv中的uv_fs_open方法,這裡libuv作為封裝層,分別寫了兩個平台下的代碼實現,編譯之後,只會存在一種實現被調用。
  • 請求對象
    在uv_fs_open的調用過程中,Node.js建立了一個FSReqWrap請求對象。從JavaScript傳入的參數和當前方法都被封裝在這個請求對象中,其中回呼函數則被設定在這個對象的oncomplete_sym屬性上。
req_wrap->object_->Set(oncomplete_sym, callback);
    對象封裝完畢後,調用QueueUserWorkItem方法將這個FSReqWrap對象推入線程池中等待執行。
QueueUserWorkItem(&uv_fs_thread_proc, req, WT_EXECUTELONGFUNCTION)
    QueueUserWorkItem接受三個參數,第一個是要執行的方法,第二個是方法的上下文,第三個是執行的標誌。當線程池中有可用線程的時候調用uv_fs_thread_proc方法執行。該方法會根據傳入的類型調用相應的底層函數,以uv_fs_open 為例,實際會調用到fs__open方法。調用完畢之後,會將擷取的結果設定在req->result上。然後調用PostQueuedCompletionStatus通知我們的IOCP對象操作已經完成。
PostQueuedCompletionStatus((loop)->iocp, 0, 0, &((req)->overlapped))
    PostQueuedCompletionStatus方法的作用是向建立的IOCP上相關的線程通訊,線程根據執行狀況和傳入的參數判定退出。至此,由JavaScript層面發起的非同步呼叫第一階段就此結束。
  • 事件迴圈
    在調用uv_fs_open方法的過程中實際上應用到了事件迴圈。以在Windows平台下的實現中,啟動Node.js時,便建立了一個基於IOCP的事件迴圈loop,並一直處於執行狀態。
uv_run(uv_default_loop());
    每次迴圈中,它會調用IOCP相關的GetQueuedCompletionStatus方法檢查是否線程池中有執行完的請求,如果存在,poll操作會將請求對象加入到loop的pending_reqs_tail屬性上。另一邊這個迴圈也會不斷檢查loop對象上的pending_reqs_tail引用,如果有可用的請求對象,就取出請求對象的result屬性作為結果傳遞給oncomplete_sym執行,以此達到調用JavaScript中傳入的回呼函數的目的。至此,整個非同步I/O的流程完成結束。    其流程如下:     事件迴圈和請求對象構成了Node.js的非同步I/O模型的兩個基本元素,這也是典型的消費者生產者情境。在Windows下通過IOCP的GetQueuedCompletionStatus、PostQueuedCompletionStatus、QueueUserWorkItem方法與事件迴圈實。對於*nix平台下,這個流程的不同之處在與實現這些功能的方法是由libeio和libev提供。  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.