本文主要討論如下問題:
Node.js事件驅動模型分析Node.js是如何處理高並發請求的Node.js的缺點介紹
首先對Node.js做個簡單介紹,Node.js是一個基於事件驅動、非阻塞式的I/O模型來實現的服務端JavaScript運行環境,是基於Google的V8引擎來實現的單線程、高性能運行在服務端的JavaScript語言。
01Node.js事件驅動模型分析
理解了上面這張圖,你就理解了Node.js的事件驅動模型。從上圖可以看出如下幾部分:
Application應用層,即JavaScript 交互層,常見的就是 Node.js 的模塊,比如 http,fs等V8這一層是V8引擎層,這一層的主要作用是解析JavaScript,同時和應用層和NodeApi層交互NodeApi為上層模塊提供系統調用,和作業系統進行交互 。Libuv是跨平臺的底層封裝,實現了線程池、事件循環、文件操作等,是 Node.js 實現異步的核心 。在Libuv層維護了一個Event Queue的事件隊列,當有請求過來時,經過Node.js的應用層和NodeApi層將請求作為一個事件放到Event Queue事件隊列中,並設置回調事件函數,然後繼續接受新的請求。
在Libuv層的Event Loop事件循環不斷讀Event Queue中的事件,在讀取事件的過程中如果遇到非阻塞事件,會自已處理,並且在處理完後調用回調函數向上一層返回結果;對於阻塞事件,會委託給後臺線程池來處理,當這些阻塞操作完成後,執行結果與提供的回調函數一起再被放入事件隊列中。當Event Loop再次讀到這個事件時,會再次執行被放到隊列中的事件回調函數,最後將結果返回給上一層。具體流程可以參考下圖:
02Node.js是如何處理高並發請求的
如果你理解了上一個問題,這個問題就很好理解了。如果要總結一下那就是異步非阻塞的編程思想。當遇到比較耗時的操作時,採用異步和非阻塞的方式進入事件隊列,不影響後面請求的執行。事件循環會讀取到這個耗時請求,交給線程池來處理。當這些耗時的操作處理完後會再次進入事件隊列,通過事件循環和回調來將請求結果返回給上一層應用,最後返回給客戶端。通過以上方式減少了高並發時的等待,從而可以從容應對高並發。
03Node.js的缺點介紹
通過以上介紹我們知道了Node.js的事件驅動模型,下面我們將介紹一下Node.js的不足。
Node.js的最大不足就是單線程,同一時間只能服務一個請求。而現在伺服器大多數都是多核CPU,這就造成了CPU的利用率非常低,造成資源的浪費。
Node.js的主線程Event Loop在處理事件隊列中的事件時都是按照事件隊列的順序執行的,在其中一個任務沒有完成之前,其他的回調、監聽器等函數都得不到運行的機會,因為被阻塞的Event Loop沒有機會處理它們。如果出現這種情況,程序執行就會變慢。