日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

解秘 Node.js 单线程实现高并发请求原理,以及串联同步执行并发请求的方案

發布時間:2025/1/21 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 解秘 Node.js 单线程实现高并发请求原理,以及串联同步执行并发请求的方案 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近在做一個支持多進程請求的 Node 服務,要支持多并發請求,而且請求要按先后順序串聯同步執行返回結果。

對,這需求就是這么奇琶,業務場景也是那么奇琶。

需求是完成了,為了對 Node.js 高并發請求原理有更深一些的理解,特意寫一篇文章來鞏固一下相關的知識點。

問題

Node.js 由這些關鍵字組成:事件驅動、非阻塞I/O、高效、輕量

于是在我們剛接觸 Node.js 時,會有所疑問:

  • 為什么在瀏覽器中運行的 JavaScript 能與操作系統進行如此底層的交互?

  • Node 真的是單線程嗎?

  • 如果是單線程,他是如何處理高并發請求的?

  • Node 事件驅動是如何實現的?

下來我們一起來解秘這是怎么一回事!

架構一覽

上面的問題,都挺底層的,所以我們從 Node.js 本身入手,先來看看 Node.js 的結構。

  • Node.js 標準庫,這部分是由 Javascript編寫的,即我們使用過程中直接能調用的 API。在源碼中的 lib 目錄下可以看到。

  • Node bindings,這一層是 Javascript 與底層 C/C++ 能夠溝通的關鍵,前者通過 bindings 調用后者,相互交換數據。

  • 第三層是支撐 Node.js 運行的關鍵,由 C/C++ 實現。

  • V8:Google 推出的 Javascript VM,也是 Node.js 為什么使用的是 JavaScript 的關鍵,它為 JavaScript 提供了在非瀏覽器端運行的環境,它的高效是 Node.js 之所以高效的原因之一。

  • Libuv:它為 Node.js 提供了跨平臺,線程池,事件池,異步 I/O 等能力,是 Node.js 如此強大的關鍵。

  • C-ares:提供了異步處理 DNS 相關的能力。

  • http_parser、OpenSSL、zlib 等:提供包括 http 解析、SSL、數據壓縮等其他的能力。

單線程、異步

  • 單線程:所有任務需要排隊,前一個任務結束,才會執行后一個任務。如果前一個任務耗時很長,后一個任務就不得不一直等著。Node 單線程指的是 Node 在執行程序代碼時,主線程是單線程

  • 異步:主線程之外,還維護了一個"事件隊列"(Event queue)。當用戶的網絡請求或者其它的異步操作到來時,Node 都會把它放到 Event Queue 之中,此時并不會立即執行它,代碼也不會被阻塞,繼續往下走,直到主線程代碼執行完畢。

注:

  • JavaScript 是單線程的,Node 本身其實是多線程的,只是 I/O 線程使用的 CPU 比較少;還有個重要的觀點是,除了用戶的代碼無法并行執行外,所有的 I/O (磁盤 I/O 和網絡 I/O) 則是可以并行起來的。

  • libuv 線程池默認打開 4 個,最多打開 128 個 線程。

事件循環

Nodejs 所謂的單線程,只是主線程是單線程。

  • 主線程運行 V8 和 JavaScript

  • 多個子線程通過?事件循環?被調度

可以抽象為:主線程對應于老板,正在工作。一旦發現有任務可以分配給職員(子線程)來做,將會把任務分配給底下的職員來做。同時,老板繼續做自己的工作,等到職員(子線程)把任務做完,就會通過事件把結果回調給老板。老板又不停重復處理職員(子線程)子任務的完成情況。

老板(主線程)給職員(子線程)分配任務,當職員(子線程)把任務做完之后,通過事件把結果回調給老板。老板(主線程)處理回調結果,執行相應的 JavaScript。

更具體的解釋請看下圖:

1、每個 Node.js 進程只有一個主線程在執行程序代碼,形成一個執行棧(execution context stack)。

2、Node.js 在主線程里維護了一個"事件隊列"(Event queue),當用戶的網絡請求或者其它的異步操作到來時,Node 都會把它放到 Event Queue之中,此時并不會立即執行它,代碼也不會被阻塞,繼續往下走,直到主線程代碼執行完畢。

3、主線程代碼執行完畢完成后,然后通過 Event Loop,也就是事件循環機制,檢查隊列中是否有要處理的事件,這時要分兩種情況:如果是非 I/O 任務,就親自處理,并通過回調函數返回到上層調用;如果是 I/O 任務,就從 線程池 中拿出一個線程來處理這個事件,并指定回調函數,當線程中的 I/O 任務完成以后,就執行指定的回調函數,并把這個完成的事件放到事件隊列的尾部,線程歸還給線程池,等待事件循環。當主線程再次循環到該事件時,就直接處理并返回給上層調用。這個過程就叫 事件循環 (Event Loop)。

4、期間,主線程不斷的檢查事件隊列中是否有未執行的事件,直到事件隊列中所有事件都執行完了,此后每當有新的事件加入到事件隊列中,都會通知主線程按順序取出交 Event Loop 處理。

優缺點

Nodejs 的優點:I/O 密集型處理是 Nodejs 的強項,因為 Nodejs 的 I/O 請求都是異步的(如:sql 查詢請求、文件流操作操作請求、http 請求...)

Nodejs 的缺點:不擅長 cpu 密集型的操作(復雜的運算、圖片的操作)

總結

1、Nodejs 與操作系統交互,我們在 JavaScript 中調用的方法,最終都會通過 process.binding 傳遞到 C/C++ 層面,最終由他們來執行真正的操作。Node.js 即這樣與操作系統進行互動。

2、Nodejs 所謂的單線程,只是主線程是單線程,所有的網絡請求或者異步任務都交給了內部的線程池去實現,本身只負責不斷的往返調度,由事件循環不斷驅動事件執行。

3、Nodejs 之所以單線程可以處理高并發的原因,得益于 libuv 層的事件循環機制,和底層線程池實現。

4、Event loop 就是主線程從主線程的事件隊列里面不停循環的讀取事件,驅動了所有的異步回調函數的執行,Event loop 總共 7 個階段,每個階段都有一個任務隊列,當所有階段被順序執行一次后,event loop 完成了一個 tick。

參考文章:Nodejs探秘:深入理解單線程實現高并發原理

串聯同步執行并發請求

就像上面說的:Node.js 在主線程里維護了一個"事件隊列"(Event queue),當用戶的網絡請求或者其它的異步操作到來時,Node 都會把它放到 Event Queue之中,此時并不會立即執行它,代碼也不會被阻塞,繼續往下走,直到主線程代碼執行完畢。

所以要串聯同步執行并發請求的關鍵在于維護一個隊列,隊列的特點是 先進先出,按隊列里面的順序執行就可以達到串聯同步執行并發請求的目的。

方案

  • 根據每個請求的 uniqueId 變量作為唯一令牌

  • 隊列里面維護一個結果數組和一個執行隊列,把執行隊列完成的 令牌與結果 存儲在結果數組里面

  • 根據唯一令牌,一直去獲取執行完成的結果,間隔 200 毫秒,超時等待時間為 10 分鐘

  • 一直等待并獲取結果,等待到有結果時,才返回給請求;并根據令牌把結果數組里面相應的項刪除

隊列

代碼:

class?Recorder?{private?list:?any[];private?queueList:?any[];private?intervalTimer;constructor()?{this.list?=?[];this.queueList?=?[];this.intervalTimer?=?null;}//?根據?id?獲取任務結果public?get(id:?string)?{let?data;console.log('this.list:?',?this.list);let?index;for?(let?i?=?0;?i?<?this.list.length;?i++)?{const?item?=?this.list[i];if?(id?===?item.id)?{data?=?item.data;index?=?i;break;}}//?刪除獲取到結果的項if?(index?!==?undefined)?{this.list.splice(index,?1);}return?data;}public?clear()?{this.list?=?[];this.queueList?=?[];}//?添加項public?async?addQueue(item:?any)?{this.queueList.push(item);}public?async?runQueue()?{clearInterval(this.intervalTimer);if?(!this.queueList.length)?{//?console.log('隊列執行完畢');return;}//?取出隊列里面的最后一項const?item?=?this.queueList.shift();console.log('item:?',?item);//?執行隊列的回調const?data?=?await?item.callback();console.log('回調執行完成:?',?data);//?把結果放進?結果數組this.list.push({?id:?item.id,?data?});}public?interval()?{clearInterval(this.intervalTimer);this.intervalTimer?=?setInterval(async?()?=>?{clearInterval(this.intervalTimer);//?一直執行里面的任務await?this.runQueue();this.interval();},?200);} }const?recorder?=?new?Recorder(); recorder.interval();export?default?recorder;

服務

下面模擬一個請求端口的的 Node 服務。

代碼:

const?Koa?=?require('koa') const?Router?=?require('koa-router') const?cuid?=?require('cuid'); const?bodyParser?=?require('koa-bodyparser') import?recorder?from?"./libs/recorder";const?MAX_WAITING_TIME?=?60?*?5;?//?最大等待時長 //?web服務端口 const?SERVER_PORT:?number?=?3000; const?app?=?new?Koa(); app.use(bodyParser()); const?router?=?new?Router();/***?程序睡眠*?@param?time?毫秒*/ const?timeSleep?=?(time:?number)?=>?{return?new?Promise((resolve)?=>?{setTimeout(()?=>?{resolve("");},?time);}); };/***?程序睡眠*?@param?second?秒*/ const?sleep?=?(second:?number)?=>?{return?timeSleep(second?*?1000); };router.post("/getPort",?async?(ctx,?next)?=>?{const?{?num?}?=?ctx.request.body;const?uniqueId?=?cuid();console.log('uniqueId:?',?uniqueId);recorder.addQueue({id:?uniqueId,callback:?getPortFun(num)});let?waitTime?=?0;while?(!ctx.body)?{await?sleep(0.2);console.log('1');const?data:?any?=?recorder.get(uniqueId);if?(data)?{ctx.body?=?{code:?0,data:?data,msg:?'success'};}waitTime++;//?超過最大時間就返回一個結果if?(waitTime?>?MAX_WAITING_TIME)?{ctx.body?=?{};}} });//?返回一個函數 function?getPortFun(num)?{return?()?=>?{return?new?Promise((resolve)?=>?{//?模擬異步程序setTimeout(()?=>?{console.log(`num${num}:?`,?num);resolve(num?*?num);},?num?*?1000);});}; }app.use(router.routes()).use(router.allowedMethods());app.listen(SERVER_PORT);

最后

最近狀態很差勁,上班工作多人的時候還好,但是自己一個人的時候,心情常常不能平靜,心好亂,有點心慌 ????

心情不好時,啥都不想干,心態有點扭轉不過來,集中不了注意力,所以最近想專心寫篇原創技術文章都不行,想重構自己開源的 blog 項目也不行,很糟糕 ????

所以最近的原創技術文章有點難產了 ????

心態急需調整,周末想出去玩,放松一下自己,找回那個斗志滿滿的真我才行,唉。


可以加貓哥的 wx:CB834301747?,一起閑聊前端。

微信搜 “前端GitHub”,回復 “電子書” 即可以獲得?160?本前端精華書籍哦。

往期精文

  • 10 個 GitHub 上超火的前端面試項目,打造自己的加薪寶庫!

  • Vue3 的學習教程匯總、源碼解釋項目、支持的 UI 組件庫、優質實戰項目

總結

以上是生活随笔為你收集整理的解秘 Node.js 单线程实现高并发请求原理,以及串联同步执行并发请求的方案的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。