搜尋
首頁web前端js教程Node.js 事件循環內部:深入探究

Inside the Node.js Event Loop: A Deep Dive

Node.js 單線程模型探索

Node.js採用事件驅動和非同步I/O的方式,實作了單執行緒、高並發的JavaScript運行環境。既然單執行緒意味著一次只能做一件事,那麼Node.js如何透過一個執行緒實現高並發和非同步I/O呢?本文將圍繞這個問題探討 Node.js 的單線程模型。

高並發策略

一般來說,高並發的解決方案是提供多執行緒模型。伺服器為每個客戶端請求分配一個執行緒並使用同步 I/O。系統透過執行緒切換來彌補同步I/O呼叫的時間成本。例如,Apache就使用這種策略。由於 I/O 操作通常非常耗時,因此這種方法很難獲得高效能。不過,它非常簡單,可以實現複雜的互動邏輯。

事實上,大多數 Web 伺服器端並不執行太多運算。接收到請求後,將請求傳遞給其他服務(例如讀取資料庫),然後等待結果返回,最後將結果傳送給客戶端。因此,Node.js 使用單線程模型來處理這種情況。它不是為每個傳入的請求分配一個線程,而是使用一個主線程來處理所有請求,然後非同步處理 I/O 操作,避免了創建、銷毀線程以及線程之間切換的開銷和複雜性。

事件循環

Node.js 在主執行緒中維護一個事件佇列。當收到請求時,它會作為事件新增到此佇列中,然後繼續接收其他請求。當主執行緒空閒時(沒有請求傳入),它開始循環遍歷事件佇列以檢查是否有事件需要處理。有兩種情況:對於非I/O任務,主執行緒會直接處理,並透過回呼函數傳回上層;對於I/O任務,它會從執行緒池中取出一個執行緒來處理事件,指定一個回呼函數,然後繼續循環佇列中的其他事件。

一旦執行緒中的I/O任務完成,就執行指定的回呼函數,並將完成的事件放在事件佇列的末尾,等待事件循環。當主線程再次循環到這個事件時,直接處理並返回給上層。這個過程稱為Event Loop,其運作原理如下圖所示:

Inside the Node.js Event Loop: A Deep Dive

此圖展示了Node.js的整體運作原理。 Node.js 從左到右、從上到下分為四層:應用層、V8 引擎層、Node API 層、LIBUV 層。

  • 應用層:是JavaScript交互層。常見的範例是 Node.js 模組,例如 http 和 fs。
  • V8引擎層:使用V8引擎解析JavaScript語法,然後與下層API互動。
  • Node API層:為上層模組提供系統調用,通常用C實現,與作業系統互動。
  • LIBUV Layer:是跨平台的底層封裝,實作事件循環、檔案操作等,是Node.js實作非同步的核心。

無論是Linux平台或Windows平台,Node.js內部都使用執行緒池來完成非同步I/O操作,LIBUV統一了不同平台差異的呼叫。所以,Node.js 中的單執行緒僅意味著 JavaScript 在單執行緒中運行,而不是 Node.js 整體是單線程的。

工作原理

Node.js 實作非同步的核心在於事件。也就是說,它將每個任務視為一個事件,然後透過事件循環來模擬非同步效果。為了更具體、更清楚地理解和接受這個事實,我們下面用偽代碼來描述它的運作方式。

1. 定義事件隊列

由於它是一個佇列,所以它是先進先出(FIFO)的資料結構。我們用JS數組來描述,如下:

/**
 * Define the event queue
 * Enqueue: push()
 * Dequeue: shift()
 * Empty queue: length === 0
 */
let globalEventQueue = [];

我們用陣列來模擬佇列結構:陣列的第一個元素是佇列的頭,最後一個元素是佇列的尾端。 push() 在佇列尾部插入一個元素,shift() 從佇列頭部刪除一個元素。這樣就實作了一個簡單的事件隊列。

2.定義請求接收入口

每個請求都會被攔截並進入處理函數,如下圖:

/**
 * Receive user requests
 * Every request will enter this function
 * Pass parameters request and response
 */
function processHttpRequest(request, response) {
    // Define an event object
    let event = createEvent({
        params: request.params, // Pass request parameters
        result: null, // Store request results
        callback: function() {} // Specify a callback function
    });

    // Add the event to the end of the queue
    globalEventQueue.push(event);
}

該函數只是將使用者的請求封裝為一個事件,放入佇列中,然後繼續接收其他請求。

3. 定義事件循環

當主執行緒空閒時,開始循環事件佇列。所以我們要定義一個函數來循環事件佇列:

/**
 * The main body of the event loop, executed by the main thread when appropriate
 * Loop through the event queue
 * Handle non-IO tasks
 * Handle IO tasks
 * Execute callbacks and return to the upper layer
 */
function eventLoop() {
    // If the queue is not empty, continue to loop
    while (this.globalEventQueue.length > 0) {
        // Take an event from the head of the queue
        let event = this.globalEventQueue.shift();

        // If it's a time-consuming task
        if (isIOTask(event)) {
            // Take a thread from the thread pool
            let thread = getThreadFromThreadPool();
            // Hand it over to the thread to handle
            thread.handleIOTask(event);
        } else {
            // After handling non-time-consuming tasks, directly return the result
            let result = handleEvent(event);
            // Finally, return to V8 through the callback function, and then V8 returns to the application
            event.callback.call(null, result);
        }
    }
}

主執行緒持續監聽事件佇列。對於I/O任務,它交給線程池處理,對於非I/O任務,它自己處理並返回。

4. 處理I/O任務

執行緒池收到任務後,直接處理I/O操作,例如讀取資料庫:

/**
 * Define the event queue
 * Enqueue: push()
 * Dequeue: shift()
 * Empty queue: length === 0
 */
let globalEventQueue = [];

當I/O任務完成時,執行回調,將請求結果儲存到事件中,並將事件放回佇列中,等待循環。最後,當前線程被釋放。當主執行緒再次循環到該事件時,直接處理。

總結上面的過程,我們發現Node.js只使用一個主執行緒來接收請求。接收到請求後,不要直接處理,而是放入事件佇列中,然後繼續接收其他請求。當它空閒時,它會透過事件循環處理這些事件,從而達到非同步的效果。當然,對於I/O任務,還是需要依賴系統層面的線程池來處理。

因此,我們可以簡單地理解為 Node.js 本身是一個多執行緒平台,但它在單執行緒中處理 JavaScript 層級的任務。

CPU 密集型任務是個缺點

到目前為止,我們應該對 Node.js 的單線程模型有了一個簡單清晰的認識。它透過事件驅動模型實現高並發和非同步I/O。然而,Node.js 也有不擅長的地方。

如上所述,對於I/O任務,Node.js將其交給執行緒池進行非同步處理,高效且簡單。因此,Node.js 適合處理 I/O 密集型任務。但並非所有任務都是 I/O 密集型的。當遇到CPU密集型任務,也就是只依賴CPU計算的操作,如資料加解密(node.bcrypt.js)、資料壓縮解壓縮(node-tar)時,Node.js會一一處理一。如果前面的任務沒有完成,後面的任務就只能等待。如下圖:

Inside the Node.js Event Loop: A Deep Dive

在事件佇列中,如果前面的CPU運算任務沒有完成,後面的任務就會被阻塞,導致反應緩慢。如果作業系統是單核心的話,可能還可以忍受。但現在大多數伺服器都是多CPU或多核心的,而Node.js只有一個EventLoop,也就是說只佔用一個CPU核。當 Node.js 被 CPU 密集型任務佔用,導致其他任務阻塞時,仍有 CPU 核心閒置,造成資源浪費。

所以,Node.js 不適合 CPU 密集型任務。

應用場景

  • RESTful API:請求和回應只需要少量文本,不需要太多邏輯處理。因此,可以並發處理數萬個連接。
  • 聊天服務:輕量級,流量大,沒有複雜的運算邏輯。

Leapcell:用於 Web 託管、非同步任務和 Redis 的下一代無伺服器平台

Inside the Node.js Event Loop: A Deep Dive

最後介紹一下最適合部署Node.js服務的平台:Leapcell。

1. 多語言支持

  • 使用 JavaScript、Python、Go 或 Rust 進行開發。

2.免費部署無限個項目

  • 只需支付使用費用-無請求,不收費。

3. 無與倫比的成本效益

  • 即用即付,無閒置費用。
  • 範例:25 美元支援 694 萬個請求,平均回應時間為 60 毫秒。

4.簡化的開發者體驗

  • 直覺的使用者介面,輕鬆設定。
  • 完全自動化的 CI/CD 管道和 GitOps 整合。
  • 即時指標和日誌記錄以獲取可行的見解。

5. 輕鬆的可擴充性和高效能

  • 自動擴展,輕鬆處理高並發。
  • 零營運開銷-只需專注於建置。

Inside the Node.js Event Loop: A Deep Dive

在文件中探索更多內容!

Leapcell Twitter:https://x.com/LeapcellHQ

以上是Node.js 事件循環內部:深入探究的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript數據類型:瀏覽器和nodejs之間是否有區別?JavaScript數據類型:瀏覽器和nodejs之間是否有區別?May 14, 2025 am 12:15 AM

JavaScript核心數據類型在瀏覽器和Node.js中一致,但處理方式和額外類型有所不同。 1)全局對像在瀏覽器中為window,在Node.js中為global。 2)Node.js獨有Buffer對象,用於處理二進制數據。 3)性能和時間處理在兩者間也有差異,需根據環境調整代碼。

JavaScript評論:使用//和 / * * / * / * /JavaScript評論:使用//和 / * * / * / * /May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript:開發人員的比較分析Python vs. JavaScript:開發人員的比較分析May 09, 2025 am 12:22 AM

Python和JavaScript的主要區別在於類型系統和應用場景。 1.Python使用動態類型,適合科學計算和數據分析。 2.JavaScript採用弱類型,廣泛用於前端和全棧開發。兩者在異步編程和性能優化上各有優勢,選擇時應根據項目需求決定。

Python vs. JavaScript:選擇合適的工具Python vs. JavaScript:選擇合適的工具May 08, 2025 am 12:10 AM

選擇Python還是JavaScript取決於項目類型:1)數據科學和自動化任務選擇Python;2)前端和全棧開發選擇JavaScript。 Python因其在數據處理和自動化方面的強大庫而備受青睞,而JavaScript則因其在網頁交互和全棧開發中的優勢而不可或缺。

Python和JavaScript:了解每個的優勢Python和JavaScript:了解每個的優勢May 06, 2025 am 12:15 AM

Python和JavaScript各有優勢,選擇取決於項目需求和個人偏好。 1.Python易學,語法簡潔,適用於數據科學和後端開發,但執行速度較慢。 2.JavaScript在前端開發中無處不在,異步編程能力強,Node.js使其適用於全棧開發,但語法可能複雜且易出錯。

JavaScript的核心:它是在C還是C上構建的?JavaScript的核心:它是在C還是C上構建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; sanInterpretedlanguagethatrunsonenginesoftenwritteninc.1)JavascriptwasdesignedAsignedAsalightWeight,drackendedlanguageforwebbrowsers.2)Enginesevolvedfromsimpleterterpretpretpretpretpreterterpretpretpretpretpretpretpretpretpretcompilerers,典型地,替代品。

JavaScript應用程序:從前端到後端JavaScript應用程序:從前端到後端May 04, 2025 am 12:12 AM

JavaScript可用於前端和後端開發。前端通過DOM操作增強用戶體驗,後端通過Node.js處理服務器任務。 1.前端示例:改變網頁文本內容。 2.後端示例:創建Node.js服務器。

Python vs. JavaScript:您應該學到哪種語言?Python vs. JavaScript:您應該學到哪種語言?May 03, 2025 am 12:10 AM

選擇Python還是JavaScript應基於職業發展、學習曲線和生態系統:1)職業發展:Python適合數據科學和後端開發,JavaScript適合前端和全棧開發。 2)學習曲線:Python語法簡潔,適合初學者;JavaScript語法靈活。 3)生態系統:Python有豐富的科學計算庫,JavaScript有強大的前端框架。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具