1. 程式人生 > >極簡 Node.js 入門 - Node.js 是什麼、效能有優勢?

極簡 Node.js 入門 - Node.js 是什麼、效能有優勢?

> 極簡 Node.js 入門系列教程:[https://www.yuque.com/sunluyong/node](https://www.yuque.com/sunluyong/node) > 本文更佳閱讀體驗:[https://www.yuque.com/sunluyong/node/what-is-node](https://www.yuque.com/sunluyong/node/what-is-node) ## 定義 > Node.js® is a JavaScript runtime built on [Chrome's V8 JavaScript engine](https://v8.dev/). 現在 [Node.js 官網](https://nodejs.org/en/)的定義就這麼簡單,但也可以看出幾個最重要的特徵 1. Node.js 不是一門語言,是一個執行時,和瀏覽器更像,只不過執行在服務端 1. 這個執行時的方言是 JavaScript(不包含 BOM、DOM API,增加了 Stream、網路等 API) 1. Node.js 是靠 Chrome V8 引擎執行 JavaScript 對應到 Java 我們可以理解 Node.js 是 JDK,裝上就能在服務端跑 JavaScript 程式碼了。 Chrome 和 Node.js 同樣是 JavaScript 執行時,都使用了 V8 引擎,主要區別在於 V8 只實現了 ECMAScript 的資料型別、物件和方法,Chrome 執行時提供了 Window、DOM、BOM,而 Node.js 執行時提供了global、 Buffer、net 等模組 > 下面內容需要一些計算機基礎知識,但看不懂並不影響 Node.js 的學習 ## 事件驅動 & 非阻塞 I/O 是什麼 在 Node.js 才誕生的時候大家總是充滿了好奇,早期官網上的介紹要更多一些,主要說了 Node 最核心的兩個特性:事件驅動、非阻塞 I/O  > Node.js® is a JavaScript runtime built on [Chrome's V8 JavaScript engine](https://link.jianshu.com?t=https://developers.google.com/v8/). Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem, [npm](https://link.jianshu.com?t=https://www.npmjs.com/), is the largest ecosystem of open source libraries in the world. 舉個例子理解 Node.js 和之前大部分 web 應用程式設計區別,當讀取資料庫的時候會寫出這樣的程式碼 ```javascript var result = db.query('select * from...'); ``` I/O 是一個相對耗時較長的工作(這是後面討論的前提),I/O 任務主要由 CPU 分發給 DMA 執行,等待資料庫查詢結果的時候程序在做什麼?大部分時候就是單純在等著而已 不同硬體裝置 I/O 操作所花費 CPU cycles ``` Action Cost (CPU cycles) L1 Cache* 3 L2 Cache* 14 RAM* 250 Disk 41,000,000 Network 240,000,000 ``` 這明顯是在浪費 CPU,所以有了多執行緒的效能優化手段,但學習作業系統的時候我們就知道 1. 作業系統建立執行緒和切換多和執行緒上下文需要一定的開銷 1. 因為多執行緒帶來的執行堆疊是要佔用記憶體的 1. 多執行緒變成面對的死鎖、狀態同步等問題會增加使用的複雜性 上面的程式碼要麼阻塞整個程序,要麼使用了多執行緒,如果程序不等待 I/O 結果,直接處理後續任務就是非阻塞 I/O,這樣可以不用浪費 CPU  ```javascript db.query('select * from...', function (result) { // 消費 result }); ``` > 在 Promise、async|await 沒有的年代,回撥是非同步的通用處理方式 程序如何獲知非同步 I/O 呼叫完成,觸發回撥函式呢?這就要靠 Event Loop 實現,也就是上面提到的事件驅動 ![image.png](https://cdn.nlark.com/yuque/0/2020/png/87727/1586147113446-f71b1488-9d2b-4bea-809e-54ef2cfd2029.png#align=left&display=inline&height=341&margin=%5Bobject%20Object%5D&name=image.png&originHeight=468&originWidth=1024&size=306043&status=done&style=none&width=746) 這個圖看起來非常複雜,有幾個要點可以幫助理解 1. 在 Node.js 中所有操作稱之為事件,客戶端的請求也是事件,所有事件維護在圖中最左側的事件佇列中 1. Node.js 主執行緒也就是圖中間的迴圈就是 Event Loop,主要作用是輪訓事件佇列中是否存在事件 1. 有非阻塞事件,按照先進先出原則依次呼叫處理 1. 有阻塞事件,交給圖中最右側的 C++ 執行緒池處理,執行緒池處理完成後把結果通過 Event Loop 返回給事件佇列 1. 進行下一次迴圈 3. 一個請求所有事件都被處理,把響應結果發給客戶端,完成一次請求 這樣一個請求 - 響應模型就完成了,如果在 Event Loop 中包含同步的 CPU 密集操作,就會阻塞主執行緒 ## Node.js 效能真的高嗎? 要回答這個問題首先需要了解幾個基本常識 1. CPU 運算遠遠快於 I/O 操作 1. Web 是典型的 I/O 密集場景 1. JavaScript 是單執行緒,但 JavaScript 的 runtime Node.js 並不是,負責 Event Loop 的 libuv 用 C 和 C++ 編寫 很多語言是依賴的多執行緒解決高併發,一個執行緒處理一條使用者請求,處理完成了釋放執行緒,在阻塞 I/O 模型下, I/O 期間該使用者執行緒所佔用的 CPU 資源(雖然十分微量,大部分交給了 DMA)什麼都不做,等待 I/O,然後響應使用者,而且開啟多個程序/執行緒 CPU 切換 Context 的時間也十分可觀 就像飯店的服務員只負責點菜,如果給每個廚師都配一個服務員,服務員把客人選單給大廚後就玩手機等著一樣,你是老闆你也生氣,況且不同於飯店大廚工資高於服務員,在計算機世界,CPU 資源比 I/O 寶貴的多 說 Node.js 在高併發、I/O 密集場景效能高,也就是 Web 場景效能高主要也是解決這個問題,沒必要一個廚師配一個服務員,整個飯店說不定一個服務員就夠了,剩下的錢可以隨便做其它事情 使用者請求來了, CPU 的部分做完不用等待 I/O,交給底層完成,然後可以接著處理下一個請求了,快就快在 1. 非阻塞 I/O 1. Web 場景 I/O 密集 1. 沒多執行緒 Context 切換開銷,多出來的開銷是維護 EventLoop 其它場景 NodeJS 效能確實不高,甚至非常低下,感興趣可以看一下 Apache(多程序) 和 Nginx(事件驅動) 對比,現在大型 web 應用普遍是 Nginx 在最前面做負載均衡伺服器、靜態資源伺服器,Apache 在下一層做實際 Web Server,響應動態請求 因此 Node.js 在 I/O 密集的 Web 場景相對於使用多程序模型語言有效能優勢,這個優勢不是來源於語言,而是作業系統實現,Java 按照這種模型實現效能一樣很高 > 得益於 V8 的優化和 C/C++ 拓展,Node.js 執行 CPU 密集任務效能並不差,但如果長時間進行 CPU 運算會阻塞後續 I/O 任務發起,用 Java 實現非阻塞模型也會遇到一樣問題 ## 參考 1. Node.js 作者 Ryan Dahl 介紹 Node.js[Ryan Dahl 2009 JSconf - Node.js.pdf](https://www.yuque.com/attachments/yuque/0/2020/pdf/87727/1579423237643-64f36605-1fa1-46fb-bf02-262cb52fbfc6.pdf?_lake_card=%7B%22uid%22%3A%221579423237514-0%22%2C%22src%22%3A%22https%3A%2F%2Fwww.yuque.com%2Fattachments%2Fyuque%2F0%2F2020%2Fpdf%2F87727%2F1579423237643-64f36605-1fa1-46fb-bf02-262cb52fbfc6.pdf%22%2C%22name%22%3A%22Ryan+Dahl+2009+JSconf+-+Node.js.pdf%22%2C%22size%22%3A447789%2C%22type%22%3A%22application%2Fpdf%22%2C%22ext%22%3A%22pdf%22%2C%22progress%22%3A%7B%22percent%22%3A99%7D%2C%22status%22%3A%22done%22%2C%22percent%22%3A0%2C%22id%22%3A%22s3fo2%22%2C%22card%22%3A%22file%22%7D) 1. [NGINX 如何實現高效能和可擴充套件性](https://www.infoq.cn/article/2015/06/nginx-design-performance-scale-/) 1. [深入理解 JavaScript Event Loop](https://zhuanlan.zhihu.com/p/34