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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

node开启子线程_多进程 amp; Node.js web 实现

發布時間:2024/9/19 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 node开启子线程_多进程 amp; Node.js web 实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

更好閱讀體驗:

多進程 & Node.js 實現 · 語雀?www.yuque.com

進程與線程

進程和線程的誕生要從多任務談起,多任務是指操作系統可以在同一時間內運行多個應用程序,CPU 按順序執行代碼,在同一時間內只能處理一個任務,而在單核時代主流操作系統都有了多任務能力,主要靠快速在多個任務之間切換,讓人感覺多個任務同時執行

進程是指操作系統正在運行的應用程序,而一個進程內部可能有多個并發的子任務,這就是線程

Web 服務器模型

Web 服務器需要同時處理多個用戶的請求,返回給用戶響應內容,有幾種不同的服務器模型實現多任務

多進程單線程

這種服務模型通過進程復制實現同時響應多個請求,每個請求使用一個單獨的進程處理,但操作系統復制進程需要復制進程內部狀態,這樣相同的狀態在內存中存在多份,對內存有一定的開銷,可以同時處理的請求數和內存大小正相關

單進程多線程

為了避免復制多進程帶來的內存浪費問題,多線程被引入 Web 服務器模型,一個線程響應一個用戶請求,線程可以共享進程的內存,不會造成內存浪費,同時線程相對于進程的內存開銷要小得多。但每個線程有自己的獨立堆棧,需要占據一定的內存空間,因此只是緩解了多進程帶來的資源浪費問題

另外操作系統在切換線程的同時需要切換線程的 context,當線程數量過多時 CPU 會被耗在 context 切換中。同時一個線程的崩潰可能會導致整個進程 crash,為服務器帶來了相當程度的穩定性風險

多進程多線程

顧名思義多進程多線程模型就是啟用多個進程,在每個進程內啟用多個線程來解決高并發問題,集成了多進程和多線程模型的好處,但當用戶量足夠大的時候也同時擁有了另外兩種模型的缺陷

當并發數達到千萬級內存好用問題就會暴露出來,這就是著名的 C10k 問題,C10k 問題的本質在于:為了處理高并發創建的進程線程太多,數據拷貝頻繁、進程/線程上下文切換消耗大, 導致操作系統崩潰

事件驅動

為了解決 Web 高并發問題 Nginx 使用了事件驅動的模型,在一個 CPU 上使用單進程、單線程來響應用戶請求,把最耗時的阻塞任務 I/O 任務異步化,處理完成后通過事件通知主進程給用戶響應,在等待 I/O 任務的時候處理下一個請求

這樣的模型性能取決于 CPU 的運算能力,但不受多進程、多線程模式中資源上限的影響,非常適合 Web I/O 密集的特征,成了現在 Web 服務器的主流模型

master-worker 模式

Node.js 本身就使用的事件驅動模型,為了解決單進程單線程對多核使用不足問題,可以按照 CPU 數目多進程啟動,理想情況下一個每個進程利用一個 CPU

Node.js 提供了 child_process 模塊支持多進程,通過 child_process.fork(modulePath) 方法可以調用指定模塊,衍生新的 Node.js 進程 worker.js

const http = require('http'); const randomPort = parseInt(Math.random() * 10000); http.createServer((req, res) => {res.end('Hello world') }).listen(randomPort);

master.js

const { fork } = require('child_process'); const os = require('os');for (let i = 0, len = os.cpus().length; i < len; i++) {fork('./worker.js'); }

使用 node master.js 啟動,會復制 CPU 數量的進程數執行 worker.js,使用 ps aux | grep worker.js 可以看到對應的進程

undefined 5271 4931720 21584 0:00.13 /usr/local/bin/node ./worker.js undefined 5270 4931720 21624 0:00.13 /usr/local/bin/node ./worker.js undefined 5269 4931720 21640 0:00.13 /usr/local/bin/node ./worker.js undefined 5268 4931720 21636 0:00.12 /usr/local/bin/node ./worker.js undefined 5267 4931720 21616 0:00.13 /usr/local/bin/node ./worker.js undefined 5266 4931720 21696 0:00.12 /usr/local/bin/node ./worker.js undefined 5265 4931720 21648 0:00.13 /usr/local/bin/node ./worker.js undefined 5264 4931720 21640 0:00.12 /usr/local/bin/node ./worker.js

這就是 Master-Worker 模式,主進程負責調度和管理工作進程,工作進程負責具體業務邏輯處理

進程通信

主進程管理工作進程,經常需要和工作進程通信,通過 child_process 復制的進程和主進程通信可以使用 WebWorker API worker.js

const http = require('http'); const randomPort = parseInt(Math.random() * 10000);http.createServer((req, res) => {res.end('Hello world') }).listen(randomPort);process.on('message', msg => {console.log(`worker get message: ${msg}`); });process.send(`${randomPort} ready`);

master.js

const { fork } = require('child_process'); const os = require('os');for (let i = 0, len = os.cpus().length; i < len; i++) {const worker = fork('./worker.js');worker.on('message', msg => {console.log(`master get message: ${msg}`);});worker.send('ok'); }

句柄傳遞

在上面的例子中每個工作進程都使用了一個隨機端口,如果設置成一樣的會出現端口號被占用的錯誤

Error: listen EADDRINUSE :::9527at Server.setupListenHandle [as _listen2] (net.js:1360:14)at listenInCluster (net.js:1401:12)at Server.listen (net.js:1485:7)

這個問題可以通過 master 監聽 80 端口,分發請求給工作進程,工作進程使用不同的端口號解決,所以上面例子使用了隨機端口號

但進程每接收一個連接會使用一個文件描述符,上面的模型因為使用了代理服務,每次連接需要消耗兩個文件描述符,而操作系統的文件描述符是有限的,代理方案浪費了一倍的文件描述符影響了系統吞吐量

為了解決這個問題 master 可以把句柄(標識資源的引用,內部包含了指向對象的文件描述符)發送給工作進程

send(message, handler);

也就是說 master 進程接收到請求后把 socket 直接發送給 worker,不用為了和 worker 連接重新創建一個 socket master.js

const { fork } = require('child_process'); const net = require('net'); const os = require('os');const workers = []; for (let i = 0, len = os.cpus().length; i < len; i++) {const worker = fork('./worker.js');workers.push(worker); }const server = net.createServer(); server.listen(9527, () => {workers.forEach(worker => {worker.send('SERVER', server);});server.close(); });

在主進程中創建一個 tcp server,監聽 9527 端口后把 tcp server 發送給所有 worker,然后關閉 tcp server,所有監聽交給 worker 處理 worker.js

const http = require('http');// 創建 http 服務器,不監聽任何端口號 const httpServer = http.createServer((req, res) => {res.end(`Hello world by ${process.pid}n`); });process.on('message', (msg, tcpServer) => {// 如果是 master 傳遞來的 tcp serverif (msg === 'SERVER') {// 新連接建立的時候觸發tcpServer.on('connection', socket => {// 把 tcp server 的連接轉給 http server 處理httpServer.emit('connection', socket);});} });

這樣寫之后為什么多個進程可以監聽同樣的端口號,不報 EADDRINUSE 錯誤了呢?

Node.js 對每個端口監聽的時候設置了 SO_REUSEADDR 選項,允許不同的進程對相同的端口號監聽

setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

獨立啟動的進程服務器 socket 的文件描述符(listenfd)不同,所以監聽相同的端口號會失敗,而上面代碼 socket 都使用 master 發送的 socket,所以可以監聽成功

多個應用監聽相同的端口號時文件描述符同一時間只能被一個進程占用,也就是說網絡請求向服務器發送的時候只有一個進程可以搶占到對請求提供服務

穩定性

利用 master 和 worker 的通信機制可以讓 master 對 worker 進行管理

worker 自動重啟

master.js

const { fork } = require('child_process'); const net = require('net'); const os = require('os');const workers = {};function createWorker(server) {const worker = fork('./worker.js');worker.send('SERVER', server);workers[worker.pid] = worker;console.log(`worker ${worker.pid} created`);worker.on('exit', () => {// worker 進程退出,自動重新創建console.log(`worker ${worker.pid} exited`);delete workers[worker.pid];createWorker(server);}); }const server = net.createServer(); server.listen(9527);for (let i = 0, len = os.cpus().length; i < len; i++) {createWorker(server); }

master 關閉自動關閉 worker

master.js

process.on('exit', () => {for (const pid in workers) {workers[pid].kill();} });

cluster 模塊

上面講的內容可以通過 Node.js 的內置模塊 cluster 實現

const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length;if (cluster.isMaster) {console.log(`主進程 ${process.pid} 正在運行`);// 衍生工作進程for (let i = 0; i < numCPUs; i++) {cluster.fork();}cluster.on('exit', (worker, code, signal) => {console.log(`工作進程 ${worker.process.pid} 已退出`);}); } else {// 工作進程可以共享任何 TCP 連接,在本例子中,共享的是 HTTP 服務器。http.createServer((req, res) => {res.writeHead(200);res.end('你好世界n');}).listen(8000);console.log(`工作進程 ${process.pid} 已啟動`); }

cluster 事件

cluster 模塊也暴露了一些事件給開發者更多的定制性

  • disconnect:在工作進程的 IPC 管道被斷開后觸發。
  • exit:當任何一個工作進程關閉的時候,cluster 模塊都將會觸發
  • fork:當新的工作進程被衍生時,cluster 模塊將會觸發
  • listening:當一個工作進程調用 listen() 后,主進程上會觸發
  • message:當集群主進程從任何工作進程接收到消息時觸發
  • online:當衍生一個新的工作進程后,工作進程應當響應一個上線消息。 當主進程收到上線消息后將會觸發此事件
  • setup:當 .setupMaster() 被調用時觸發
  • 總結

    以上是生活随笔為你收集整理的node开启子线程_多进程 amp; Node.js web 实现的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    主站蜘蛛池模板: 91麻豆免费视频 | mm131国产精品 | 操天天 | 欧美日韩激情网 | 国产91在线观看丝袜 | 狠狠干干干 | 成人欧美在线 | 九一av| 国家队动漫免费观看在线观看晨光 | www亚洲视频 | www狠狠干 | 一区二区精品在线观看 | 美女久久久久久久久久 | 国产一级二级在线 | 情五月| 日韩一级大片 | 色婷婷综合久久久久中文 | 欧美人体一区二区 | 欧美日韩精品一区二区 | 午夜精品久久久久久久久久久久久蜜桃 | 日韩成人动漫在线观看 | 五月婷婷丁香综合 | 黄色激情在线 | 欧美特一级 | 天天躁日日躁狠狠躁免费麻豆 | 成人黄色三级视频 | 久草久| 亚洲欧洲一二三区 | 色综合天天综合网国产成人网 | 亚洲熟妇一区 | 91天天射 | 一级全黄裸体免费视频 | 欧美日韩一区二区在线观看 | 亚洲视频第一页 | 精品国产乱码久久久久久免费 | 欧美黑人精品一区二区不卡 | 欧美透逼视频 | 亚洲色图欧美在线 | 欧美女优一区二区 | 亚洲国产精品18久久久久久 | 亚洲自拍网站 | 无遮挡裸光屁屁打屁股男男 | 日韩精品第二页 | 国产欧美一区二区三区沐欲 | 日本精品黄 | 国产成人精品网 | 日韩和欧美一区二区 | 永久免费视频网站直接看 | 美女一二三区 | 午夜寂寞视频 | 91精品人妻一区二区三区蜜桃欧美 | 日日草视频 | 欧美,日韩,国产在线 | 亚洲一区二区在线免费观看 | 午夜影院在线免费观看 | 最新国产露脸在线观看 | 国产精品自拍偷拍视频 | 精品国产乱码久久久久久牛牛 | 亚洲成a人片在线 | 欧美一区二区三区免费在线观看 | 欧美性生活一级 | 91丝袜美腿 | 日本少妇一区 | 少妇h视频 | 在线播放日韩av | 91丨九色丨丰满 | 亚洲黄色激情 | 99爱在线观看 | av加勒比在线 | 日皮毛片 | 特大黑人巨交吊性xxxx视频 | 一本色道久久hezyo无码 | 国产一区二区三区18 | 亚洲一区色 | 五月婷婷激情网 | 黄色一几片 | 亚洲乱码少妇 | av福利在线| 国产av无码专区亚洲av毛片搜 | 国产ts人妖调教重口男 | 国产日产精品一区二区三区四区 | 欧美色噜噜| 亚洲综合免费观看高清完整版在线 | 欧美色第一页 | 欧美日韩国产成人 | 高潮毛片又色又爽免费 | av在线网页 | 91亚洲免费| 九九热超碰 | 少妇又紧又色又爽又刺激视频 | 国产日韩欧美在线观看视频 | www.夜夜爱| 午夜三区 | 最新天堂中文在线 | 五月开心网 | 国产精品国产三级国产三级人妇 | 黄色录像网址 | 91在线视频在线观看 | 午夜电影你懂的 |