搜尋
首頁web前端js教程詳細了解Nodejs中的事件循環機制

這篇文章帶大家了解一下nodejs事件循環機制。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

詳細了解Nodejs中的事件循環機制

相關推薦:《nodejs 教學

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

高並發策略

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

而事實上,大多數網站的伺服器端都不會做太多的計算,它們接收到請求以後,把請求交給其它服務來處理(例如讀取資料庫),然後等著結果返回,最後再把結果發給客戶端。因此,Node.js 針對這一事實採用了單線程模型來處理,它不會為每個接入請求分配一個線程,而是用一個主線程處理所有的請求,然後對I/O 操作進行非同步處理,避開了創建、銷毀線程以及在線程間切換所需的開銷和複雜性。

事件循環

Node.js 在主執行緒裡維護了一個事件佇列,當接到請求後,就將該請求當作一個事件放入這個佇列中,然後繼續接收其他請求。當主執行緒空閒時(沒有請求存取),就開始循環事件佇列,檢查佇列中是否有要處理的事件,這時要分兩種情況:如果是非I/O 任務,就親自處理,並透過回調函數回到上層呼叫;如果是I/O 任務,就從 執行緒池 中拿出一個執行緒來處理這個事件,並指定回呼函數,然後繼續循環佇列中的其他事件。

當執行緒中的I/O 任務完成以後,就執行指定的回呼函數,並把這個完成的事件放到事件佇列的尾部,等待事件循環,當主執行緒再次循環到該事件時,就直接處理並傳回給上層呼叫。這個過程叫做 事件循環 (Event Loop),其運作原理如下圖:

這個圖是整個Node.js 的運作原理,從左到右,從上到下,Node.js 被分成了四層,分別是 應用層V8引擎層Node API層和 LIBUV層。

應用程式層:   即JavaScript 互動層,常見的是Node.js 的模組,例如http,fs

V8引擎層:  即利用V8 引擎來解析JavaScript語法,進而和下層API 互動

NodeAPI層:  為上層模組提供系統調用,一般是由C 語言來實現,和作業系統進行互動。

LIBUV層: 是跨平台的底層封裝,實作了 事件循環、檔案操作等,是 Node.js 實作非同步的核心 。

#無論是Linux 平台還是Windows 平台,Node.js 內部都是透過 執行緒池 來完成非同步I/O 操作的,而LIBUV 針對不同平台的差異性實現了統一呼叫。因此,Node.js 的單執行緒只是指 JavaScript 運行在單一執行緒中,而非 Node.js 是單執行緒。

工作原理

Node.js 實現非同步的核心是事件,也就是說,它把每個任務都當成 事件 來處理,然後透過Event Loop 模擬了非同步的效果,為了更具體、更清晰的理解和接受這個事實,下面我們用偽代碼來描述一下其工作原理。

【1】定義事件隊列

既然是隊列,那就是先進先出(FIFO) 的資料結構,我們用JS數組來描述,如下:

/**
 * 定义事件队列
 * 入队:push()
 * 出队:shift()
 * 空队列:length == 0
 */
globalEventQueue: []

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

【2】定義接收請求入口

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

#
/**
 * 接收用户请求
 * 每一个请求都会进入到该函数
 * 传递参数request和response
 */
processHttpRequest:function(request,response){
    
    // 定义一个事件对象
    var event = createEvent({
        params:request.params, // 传递请求参数
        result:null, // 存放请求结果
        callback:function(){} // 指定回调函数
    });

    // 在队列的尾部添加该事件   
    globalEventQueue.push(event);
}

这个函数很简单,就是把用户的请求包装成事件,放到队列里,然后继续接收其他请求。

【3】定义 Event Loop

当主线程处于空闲时就开始循环事件队列,所以我们还要定义一个函数来循环事件队列: 

/**
 * 事件循环主体,主线程择机执行
 * 循环遍历事件队列
 * 处理非IO任务
 * 处理IO任务
 * 执行回调,返回给上层
 */
eventLoop:function(){
    // 如果队列不为空,就继续循环
    while(this.globalEventQueue.length > 0){
        
        // 从队列的头部拿出一个事件
        var event = this.globalEventQueue.shift();
        
        // 如果是耗时任务
        if(isIOTask(event)){
            // 从线程池里拿出一个线程
            var thread = getThreadFromThreadPool();
            // 交给线程处理
            thread.handleIOTask(event)
        }else {
            // 非耗时任务处理后,直接返回结果
            var result = handleEvent(event);
            // 最终通过回调函数返回给V8,再由V8返回给应用程序
            event.callback.call(null,result);
        }
    }
}

主线程不停的检测事件队列,对于 I/O 任务,就交给线程池来处理,非 I/O 任务就自己处理并返回。

【4】处理 I/O 任务

线程池接到任务以后,直接处理IO操作,比如读取数据库:

/**
 * 处理IO任务
 * 完成后将事件添加到队列尾部
 * 释放线程
 */
handleIOTask:function(event){
    //当前线程
    var curThread = this; 

    // 操作数据库
    var optDatabase = function(params,callback){
        var result = readDataFromDb(params);
        callback.call(null,result)
    };
    
    // 执行IO任务
    optDatabase(event.params,function(result){
        // 返回结果存入事件对象中
        event.result = result; 

        // IO完成后,将不再是耗时任务
        event.isIOTask = false; 
        
        // 将该事件重新添加到队列的尾部
        this.globalEventQueue.push(event);
        
        // 释放当前线程
        releaseThread(curThread)
    })
}

当 I/O 任务完成以后就执行回调,把请求结果存入事件中,并将该事件重新放入队列中,等待循环,最后释放当前线程,当主线程再次循环到该事件时,就直接处理了。

总结以上过程我们发现,Node.js 只用了一个主线程来接收请求,但它接收请求以后并没有直接做处理,而是放到了事件队列中,然后又去接收其他请求了,空闲的时候,再通过 Event Loop 来处理这些事件,从而实现了异步效果,当然对于IO类任务还需要依赖于系统层面的线程池来处理。

因此,我们可以简单的理解为: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 就会亲自处理,一个一个的计算,前面的任务没有执行完,后面的任务就只能干等着 。如下图所示:

在事件队列中,如果前面的 CPU 计算任务没有完成,后面的任务就会被阻塞,出现响应缓慢的情况,如果操作系统本身就是单核,那也就算了,但现在大部分服务器都是多 CPU 或多核的,而 Node.js 只有一个 EventLoop,也就是只占用一个 CPU 内核,当 Node.js 被CPU 密集型任务占用,导致其他任务被阻塞时,却还有 CPU 内核处于闲置状态,造成资源浪费。

因此,Node.js 并不适合 CPU 密集型任务。

适用场景

RESTful API - 请求和响应只需少量文本,并且不需要大量逻辑处理, 因此可以并发处理数万条连接。

聊天服务 - 轻量级、高流量,没有复杂的计算逻辑。

更多编程相关知识,请访问:编程教学!!

以上是詳細了解Nodejs中的事件循環機制的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:博客园。如有侵權,請聯絡admin@php.cn刪除
Vercel是什么?怎么部署Node服务?Vercel是什么?怎么部署Node服务?May 07, 2022 pm 09:34 PM

Vercel是什么?本篇文章带大家了解一下Vercel,并介绍一下在Vercel中部署 Node 服务的方法,希望对大家有所帮助!

node.js gm是什么node.js gm是什么Jul 12, 2022 pm 06:28 PM

gm是基于node.js的图片处理插件,它封装了图片处理工具GraphicsMagick(GM)和ImageMagick(IM),可使用spawn的方式调用。gm插件不是node默认安装的,需执行“npm install gm -S”进行安装才可使用。

火了!新的JavaScript运行时:Bun,性能完爆Node火了!新的JavaScript运行时:Bun,性能完爆NodeJul 15, 2022 pm 02:03 PM

今天跟大家介绍一个最新开源的 javaScript 运行时:Bun.js。比 Node.js 快三倍,新 JavaScript 运行时 Bun 火了!

nodejs中lts是什么意思nodejs中lts是什么意思Jun 29, 2022 pm 03:30 PM

在nodejs中,lts是长期支持的意思,是“Long Time Support”的缩写;Node有奇数版本和偶数版本两条发布流程线,当一个奇数版本发布后,最近的一个偶数版本会立即进入LTS维护计划,一直持续18个月,在之后会有12个月的延长维护期,lts期间可以支持“bug fix”变更。

聊聊Node.js中的多进程和多线程聊聊Node.js中的多进程和多线程Jul 25, 2022 pm 07:45 PM

大家都知道 Node.js 是单线程的,却不知它也提供了多进(线)程模块来加速处理一些特殊任务,本文便带领大家了解下 Node.js 的多进(线)程,希望对大家有所帮助!

node爬取数据实例:聊聊怎么抓取小说章节node爬取数据实例:聊聊怎么抓取小说章节May 02, 2022 am 10:00 AM

node怎么爬取数据?下面本篇文章给大家分享一个node爬虫实例,聊聊利用node抓取小说章节的方法,希望对大家有所帮助!

深入浅析Nodejs中的net模块深入浅析Nodejs中的net模块Apr 11, 2022 pm 08:40 PM

本篇文章带大家带大家了解一下Nodejs中的net模块,希望对大家有所帮助!

怎么获取Node性能监控指标?获取方法分享怎么获取Node性能监控指标?获取方法分享Apr 19, 2022 pm 09:25 PM

怎么获取Node性能监控指标?本篇文章来和大家聊聊Node性能监控指标获取方法,希望对大家有所帮助!

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脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。