node高并發(fā)原理機(jī)制解讀
概念
- 1.事件循環(huán):
事件循環(huán)是一種編程構(gòu)造,用于等待和分派程序中的事件或消息, 主線程從"任務(wù)隊(duì)列"中讀取事件,這個(gè)過程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為Event Loop(事件循環(huán))
- 2.事件隊(duì)列:
當(dāng)用戶的網(wǎng)絡(luò)請(qǐng)求或者其它的異步操作到來時(shí),node都會(huì)把它放到Event Queue之中,此時(shí)并不會(huì)立即執(zhí)行它,代碼也不會(huì)被阻塞,繼續(xù)往下走,直到主線程代碼執(zhí)行完畢。
- 3.任務(wù)隊(duì)列:
任務(wù)隊(duì)列"是一個(gè)事件的隊(duì)列(也可以理解成消息的隊(duì)列),IO設(shè)備完成一項(xiàng)任務(wù),就在"任務(wù)隊(duì)列"中添加一個(gè)事件,表示相關(guān)的異步任務(wù)可以進(jìn)入"執(zhí)行棧"了。主線程讀取"任務(wù)隊(duì)列",就是讀取里面有哪些事件。
- 4.事件驅(qū)動(dòng):
實(shí)質(zhì)是通過主循環(huán)加事件觸發(fā)方式運(yùn)行程序
- 5.node
Node.js 不是一門語言也不是框架,它只是基于 Google V8 引擎的 JavaScript 運(yùn)行時(shí)環(huán)境,是對(duì) js 功能的拓展。提供了網(wǎng)絡(luò)、文件、dns 解析、進(jìn)程線程等功能。
- 6.libuv
libuv是專門為Node.js開發(fā)的一個(gè)封裝庫,提供跨平臺(tái)的異步I/O能力。
注:
1. 一個(gè)事件循環(huán)有一個(gè)或多個(gè)任務(wù)隊(duì)列。一個(gè)任務(wù)隊(duì)列是一組的任務(wù)
2. Libuv 主要是,利用系統(tǒng)提供的事件驅(qū)動(dòng)模塊解決網(wǎng)絡(luò)異步 IO,利用線程池解決文件IO。另外還實(shí)現(xiàn)了定時(shí)器,對(duì)進(jìn)程、線程等使用進(jìn)行了封裝。
node架構(gòu)圖
Node Standard Library 標(biāo)準(zhǔn)庫,如Http, event模塊。
Node Bindings 是溝通JS 和 C++的橋梁,封裝V8和Libuv的細(xì)節(jié),向上層提供基礎(chǔ)API服務(wù)。由 C/C++ 實(shí)現(xiàn)。
V8 是Google開發(fā)的JavaScript引擎,提供JavaScript運(yùn)行環(huán)境,可以說它就是 Node.js 的發(fā)動(dòng)機(jī)。
Libuv 是專門為Node.js開發(fā)的一個(gè)封裝庫,提供跨平臺(tái)的異步I/O能力.
單線程、異步
單線程就意味著,所有任務(wù)需要排隊(duì),前一個(gè)任務(wù)結(jié)束,才會(huì)執(zhí)行后一個(gè)任務(wù)。
如果前一個(gè)任務(wù)耗時(shí)很長,后一個(gè)任務(wù)就不得不一直等著。
node單線程指的是node在執(zhí)行程序代碼時(shí),主線程是單線程。
異步體現(xiàn)在,主線程之外,還維護(hù)了一個(gè)"事件隊(duì)列"(Event queue)。
當(dāng)用戶的網(wǎng)絡(luò)請(qǐng)求或者其它的異步操作到來時(shí),node都會(huì)把它放到Event Queue之中,此時(shí)并不會(huì)立即執(zhí)行它,代碼也不會(huì)被阻塞,繼續(xù)往下走,直到主線程代碼執(zhí)行完畢.
注:
- JavaScript 是單線程的,Node 本身其實(shí)是多線程的,只是 I/O 線程使用的 CPU 比較少;還有個(gè)重要的觀點(diǎn)是,除了用戶的代碼無法并行執(zhí)行外,所有的 I/O (磁盤 I/O 和網(wǎng)絡(luò) I/O) 則是可以并行起來的。
- libuv 線程池默認(rèn)打開 4 個(gè),最多打開 128個(gè) 線程。
事件循環(huán)
Node 開始執(zhí)行腳本時(shí),會(huì)先進(jìn)行事件循環(huán)的初始化,但是這時(shí)事件循環(huán)還沒有開始,會(huì)先完成下面的事情。
- 同步任務(wù)
- 發(fā)出異步請(qǐng)求
- 規(guī)劃定時(shí)器生效的時(shí)間
- 執(zhí)行process.nextTick()等
然后就調(diào)用libuv事件循環(huán)階段,事件循環(huán)會(huì)無限次地執(zhí)行,一輪又一輪。
只有異步任務(wù)的回調(diào)函數(shù)隊(duì)列清空了,才會(huì)停止執(zhí)行。
Node中的事件循環(huán)有6個(gè)階段
event loop 的每個(gè)階段都有一個(gè)任務(wù)隊(duì)列
當(dāng) event loop 到達(dá)某個(gè)階段時(shí),將執(zhí)行該階段的任務(wù)隊(duì)列,直到隊(duì)列清空或執(zhí)行的回調(diào)達(dá)到系統(tǒng)上限后,才會(huì)轉(zhuǎn)入下一個(gè)階段
當(dāng)所有階段被順序執(zhí)行一次后,稱 event loop 完成了一個(gè) tick
注:
node中的事件循環(huán)與瀏覽器有區(qū)別~
事件驅(qū)動(dòng)+事件循環(huán)實(shí)現(xiàn)高并發(fā)
具體執(zhí)行順序:
1、每個(gè)Node.js進(jìn)程只有一個(gè)主線程在執(zhí)行程序代碼,形成一個(gè)執(zhí)行棧(execution context stack)
2、主線程之外,還維護(hù)了一個(gè)"事件隊(duì)列"(Event queue)。當(dāng)用戶的網(wǎng)絡(luò)請(qǐng)求或者其它的異步操作到來時(shí),node都會(huì)把它放到Event Queue之中,此時(shí)并不會(huì)立即執(zhí)行它,代碼也不會(huì)被阻塞,繼續(xù)往下走,直到主線程代碼執(zhí)行完畢。
3、主線程代碼執(zhí)行完畢完成后,然后通過Event Loop,也就是事件循環(huán)機(jī)制,開始到Event Queue的開頭取出第一個(gè)事件,從線程池中分配一個(gè)線程去執(zhí)行這個(gè)事件,接下來繼續(xù)取出第二個(gè)事件,再從線程池中分配一個(gè)線程去執(zhí)行,然后第三個(gè),第四個(gè)。主線程不斷的檢查事件隊(duì)列中是否有未執(zhí)行的事件,直到事件隊(duì)列中所有事件都執(zhí)行完了,此后每當(dāng)有新的事件加入到事件隊(duì)列中,都會(huì)通知主線程按順序取出交EventLoop處理。當(dāng)有事件執(zhí)行完畢后,會(huì)通知主線程,主線程執(zhí)行回調(diào),線程歸還給線程池。
總線程不斷重復(fù)第三步
注意:
我們所看到的node.js單線程只是一個(gè)js主線程與ui渲染共享一個(gè)線程,本質(zhì)上的異步操作還是由線程池完成的,node將所有的阻塞操作都交給了內(nèi)部的線程池去實(shí)現(xiàn),本身只負(fù)責(zé)不斷的往返調(diào)度,并沒有進(jìn)行真正的I/O操作,從而實(shí)現(xiàn)異步非阻塞I/O,這便是node單線程和事件驅(qū)動(dòng)的精髓之處了
總結(jié)
libuv 線程池默認(rèn)打開 4 個(gè),最多打開 128個(gè) 線程。(例如:以前 web 服務(wù)器同一時(shí)間比如說最多只能接收 100 個(gè)請(qǐng)求,多的就無法接收了。nodejs 所謂的高并發(fā)是指可以同時(shí)接收 1000、10000 個(gè)請(qǐng)求,只不過以排隊(duì)的方式在等待。)
主線程執(zhí)行js,是單線程的,js代碼在做大量計(jì)算就是cpu密集了。主線程不空閑出來也沒法處理 io 的事,所以就會(huì)阻塞了。
回調(diào)只能保證某個(gè)請(qǐng)求按照順序執(zhí)行,不能保證多個(gè)請(qǐng)求訪問一個(gè)資源的先后順序,多個(gè)請(qǐng)求訪問一個(gè)資源是要加鎖的,用事務(wù)加鎖就行。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
node如何實(shí)現(xiàn)cmd彈窗交互之inquirer
這篇文章主要介紹了node如何實(shí)現(xiàn)cmd彈窗交互之inquirer問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10node.js基于fs模塊對(duì)系統(tǒng)文件及目錄進(jìn)行讀寫操作的方法詳解
這篇文章主要介紹了node.js基于fs模塊對(duì)系統(tǒng)文件及目錄進(jìn)行讀寫操作的方法,結(jié)合實(shí)例形式分析了nodejs使用fs模塊針對(duì)文件與目錄的讀寫、創(chuàng)建、刪除等相關(guān)操作技巧,需要的朋友可以參考下2017-11-11Node.js環(huán)境下編寫爬蟲爬取維基百科內(nèi)容的實(shí)例分享
WikiPedia平時(shí)在國內(nèi)不大好訪問-- 所以用爬蟲一次性把要看的東西都爬下來保存慢慢看還是比較好的XD 這里我們就來看一下Node.js環(huán)境下編寫爬蟲爬取維基百科內(nèi)容的實(shí)例分享2016-06-06pnpm workspace管理monorepo項(xiàng)目使用過程詳解
這篇文章主要為大家介紹了pnpm workspace管理monorepo項(xiàng)目使用過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Node.js 中 cookie-parser 依賴安裝使用詳解
文章介紹了如何在Node.js中使用cookie-parser中間件來解析、設(shè)置、簽名和清除HTTP請(qǐng)求中的Cookie,感興趣的朋友一起看看吧2025-02-02使用NodeJS對(duì)一個(gè)字符串加密的操作方法示例
Node.js中的crypto模塊提供了多種加密算法,可以用來加密字符串,AES和RSA是其中兩種常用的算法,本文介紹如何使用NodeJS對(duì)一個(gè)字符串加密,感興趣的朋友一起看看吧2025-01-01