欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Nodejs搭建多進(jìn)程Web服務(wù)器實現(xiàn)過程

 更新時間:2022年10月19日 14:34:29   作者:SaraiNoQ  
這篇文章主要為大家介紹了Nodejs搭建多進(jìn)程Web服務(wù)器實現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

上節(jié)我們講到,通過 fork() 或者其他API,創(chuàng)建子進(jìn)程之后,可以通過 send()process.on('message') 進(jìn)行父子進(jìn)程間的通信。這樣就實現(xiàn)了主進(jìn)程代理請求到工作進(jìn)程,實現(xiàn)了 Nodejs集群

父子進(jìn)程間通信

負(fù)載均衡

通過代理,可以避免端口不能重復(fù)監(jiān)聽的問題,甚至可以在代理進(jìn)程上做適當(dāng)?shù)?strong>負(fù)載均衡,使得每個子進(jìn)程可以較為均衡地執(zhí)行任務(wù)。下面我們構(gòu)建了一個簡單的 Web 服務(wù)器,并實現(xiàn)在兩個工作進(jìn)程之間做簡單的負(fù)載均衡。

主進(jìn)程,負(fù)責(zé)代理到對應(yīng)進(jìn)程中:

// main.js
const { fork } = require('child_process');
const normal = fork('subprocess.js', ['normal']);
const special = fork('subprocess.js', ['special']);
// Open up the server and send sockets to child. Use pauseOnConnect to prevent
// 套接字在發(fā)送給子進(jìn)程之前不會被讀取
const server = require('net').createServer({ pauseOnConnect: true });
let flag = 0;
server.on('connection', (socket) => {
  flag++;
  // this is special priority.
  if (flag % 2 === 0) {
    special.send('socket', socket);
    return;
  }
  // This is normal priority.
  normal.send('socket', socket);
});
server.listen(1337);

這是工作進(jìn)程,接收socket對象并做出響應(yīng):

// subprocess.js
process.on('message', (m, socket) => {
  if (m === 'socket') {
    // Check that the client socket exists. 
    // It is possible for the socket to be closed between the time it is
    if (socket) {
      // console.log(`Request handled with ${process.argv[2]} priority`);
      socket.end(`Request handled with ${process.argv[2]} priority, running on ${process.pid}`);
    }
  }
});

然后我又編寫了一個 Nodejs 腳本,來發(fā)出十個 HTTP 請求:

const cp = require("child_process");
for (let i = 0; i < 10; i++) {
  cp.exec(`curl --http0.9 "http://127.0.0.1:1337"`, (err, stdout, stderr) => {
    console.log(`finished: ${i}, and received: `, stdout);
  })
}

最后運行結(jié)果如下:

句柄傳遞

在使用 send() 方法時,我們注意到,除了能通過IPC發(fā)送數(shù)據(jù)外,還能發(fā)送句柄。第二個可選參數(shù)就是一個句柄:

child.send(message, [sendHandle]);

?? 句柄是一種可以用來標(biāo)識資源的引用,它的內(nèi)部包含了指向?qū)ο蟮奈募枋龇1热缇浔梢杂脕順?biāo)識一個服務(wù)器端socket對象、一個客戶端socket對象、一個UDP套接字、一個管道等。

在主進(jìn)程將句柄發(fā)送給子進(jìn)程之后,工作模型就從主進(jìn)程響應(yīng)用戶請求變成了子進(jìn)程監(jiān)聽用戶活動:

進(jìn)程對象send()方法可以發(fā)送的句柄類型包括如下幾種:

  • net.Socket。TCP套接字。
  • net.Server。TCP服務(wù)器,任意建立在TCP服務(wù)上的應(yīng)用層服務(wù)都可以享受到它帶來的好處。
  • net.Native。C++層面的TCP套接字或IPC管道。
  • dgram.Socket。UDP套接字。
  • dgram.Native。C++層面的UDP套接字。

?? 另外要注意,send()方法能發(fā)送消息和句柄并不意味著它能發(fā)送任意對象,message 參數(shù)和文件句柄都要先通過 JSON.stringfy() 進(jìn)行序列化后再放入IPC通道中:

集群

通過 child_process模塊,我們完成了父子進(jìn)程的創(chuàng)建和通信,已經(jīng)初步搭建了一個Node集群。還有一些問題需要考慮:

  • 性能問題。
  • 多個工作進(jìn)程的存活狀態(tài)管理。
  • 工作進(jìn)程的平滑重啟。
  • 配置或者靜態(tài)數(shù)據(jù)的動態(tài)重新載入。
  • 其他細(xì)節(jié)。

這其中最重要的便是集群的穩(wěn)定性,這決定了該服務(wù)模型能否真正用于實踐生成中。雖然我們創(chuàng)建了很多工作進(jìn)程,但每個工作進(jìn)程依然是在單線程上執(zhí)行的,它的穩(wěn)定性還不能得到完全的保障。我們需要建立起一個健全的機制來保障Node應(yīng)用的健壯性。

子進(jìn)程事件

父進(jìn)程能監(jiān)聽到的,與子進(jìn)程相關(guān)的事件:

  • error:當(dāng)子進(jìn)程無法被復(fù)制創(chuàng)建、無法被殺死、無法發(fā)送消息時會觸發(fā)該事件。
  • exit:子進(jìn)程退出時觸發(fā)該事件。如果是正常退出,這個事件的第一個參數(shù)為退出碼,否則為null。如果進(jìn)程是通過kill()方法被殺死的,會得到第二個參數(shù),它表示殺死進(jìn)程時的信號。
  • close:在子進(jìn)程的標(biāo)準(zhǔn)輸入輸出流中止時觸發(fā)該事件,參數(shù)與exit相同。
  • disconnect:在父進(jìn)程或子進(jìn)程中調(diào)用disconnect()方法時觸發(fā)該事件,在調(diào)用該方法時將關(guān)閉監(jiān)聽IPC通道。

除了 send() 外,還能通過 kill() 方法給子進(jìn)程發(fā)送消息。kill() 方法并不能真正地將通過IPC相連的子進(jìn)程殺死,它只是給子進(jìn)程發(fā)送了一個系統(tǒng)信號。默認(rèn)情況下,父進(jìn)程將通過 kill() 方法給子進(jìn)程發(fā)送一個 SIGTERM信號。

// 子進(jìn)程
child.kill([signal]);
// 當(dāng)前進(jìn)程
process.kill(pid, [signal]);
// 監(jiān)聽
process.on(signal, callback)

?? 在POSIX標(biāo)準(zhǔn)中,有一套完備的信號系統(tǒng),在命令行中執(zhí)行kill -l可以看到詳細(xì)的信號列表,如下所示:

而 Node 提供了這些信號對應(yīng)的信號事件,每個進(jìn)程都可以監(jiān)聽這些信號事件。這些信號事件是用來通知進(jìn)程的,每個信號事件有不同的含義,進(jìn)程在收到響應(yīng)信號時,應(yīng)當(dāng)做出約定的行為:

process.on('SIGTERM', () => {
    console.log("got sigterm, exiting...");
    process.exit(1);
});
console.log("process running on: ", process.pid);
process.kill(process.pid, "SIGTERM");

自動重啟

有了父子進(jìn)程之間的相關(guān)事件之后,就可以在這些關(guān)系之間創(chuàng)建出需要的機制了,至少我們能夠通過監(jiān)聽子進(jìn)程的 exit事件 來獲知其退出的信息。接著前文的多進(jìn)程架構(gòu),我們在主進(jìn)程上要加入一些子進(jìn)程管理的機制,比如重新啟動一個工作進(jìn)程來繼續(xù)服務(wù):

主進(jìn)程代碼:

// master.js
// master.js
const { fork } = require('child_process');
const cpus = require('os').cpus();
const server = require('net').createServer();
server.listen(1337);
const workers = {};
// process.on('uncaughtException', function (err) {
//   console.log(`Master uncaughtException:\r\n`);
//   console.log(err);
// });
const createWorker = () => {
  const worker = fork('./worker.js');
  // 收到信號后立即重啟新進(jìn)程
  worker.on('message', function (message) {
    if (message.act === 'suicide') {
      createWorker();
    }
  });
  // 某個進(jìn)程終止時重新啟動新的進(jìn)程
  worker.on('exit', () => {
    console.log('Worker ' + worker.pid + ' exited.');
    delete workers[worker.pid];
    // createWorker();
  });
  // 句柄轉(zhuǎn)發(fā)
  worker.send('server', server);
  workers[worker.pid] = worker;
  console.log('Create worker. pid: ' + worker.pid);
};
for (let i = 0; i < cpus.length; i++) {
  createWorker();
}
// server.close();
// 進(jìn)程自己退出時,讓所有工作進(jìn)程退出
process.on('exit', () => {
  for (let pid in workers) {
    workers[pid].kill();
  }
});

子進(jìn)程代碼:

// worker.js
const http = require('http');
const server = http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('handled by child, pid is ' + process.pid + '\n');
  // 拋出異常,捕獲后終止進(jìn)程
  throw new Error('throw exception');
});
var worker;
process.on('message', (m, tcp) => {
  if (m === 'server') {
    worker = tcp;
    worker.on('connection', (socket) => {
      server.emit('connection', socket);
    });
  }
});
// 捕獲異常后終止進(jìn)程
process.on('uncaughtException', (err) => {
  // 主動發(fā)出信號,避免等待連接斷開時收到新請求而缺少進(jìn)程無法響應(yīng)
  process.send({
    act: 'suicide'
  });
  // 停止接收新的連接
  worker.close(function () {
    // 所有已有連接斷開后,退出進(jìn)程
    process.exit(1);
  });
  // 避免長連接請求長時間無法終止,5s后自動終止
  setTimeout(() => {
    process.exit(1);
  }, 5000)
});

運行父進(jìn)程 master.js,控制臺中會打印出開啟的進(jìn)程 PID

在 Linux 中,你可以直接使用 kill -9 [pid] 來終止進(jìn)程。在 Windows 中,你需要打開任務(wù)管理器,找到 node.exe 的進(jìn)程,終止其中某個。此時命令行會顯示該進(jìn)程被終止了,然后重新開啟一個新的進(jìn)程。

當(dāng)然,你也可以使用我們之前寫的 run.js 腳本,每發(fā)起一個請求,子進(jìn)程響應(yīng)請求之后會拋出一個異常,異常在捕獲之后會終止該進(jìn)程。

?? 我們之前寫的 run.js 腳本是并行執(zhí)行的,此時會存在多個請求被分配到同一個 socket ,即分配到同一個進(jìn)程中執(zhí)行。那么就會存在互斥的問題,即某個請求結(jié)束后就終止該進(jìn)程,導(dǎo)致其他請求無法獲得響應(yīng)而終止。此時你需要將 exec 方法改為同步方法:

const cp = require("child_process");
const cpus = require("os").cpus();
const sleep = (delay) => {
  const now = Date.now();
  while (Date.now() - now < delay);
  return;
}
for (let i = 0; i < cpus.length; i++) {
  const out = cp.execSync(`curl --http0.9 "http://127.0.0.1:1337"`);
  sleep(1000);
  console.log(out.toString());
}

該模型一旦有異常出現(xiàn),主進(jìn)程會創(chuàng)建新的工作進(jìn)程來為用戶服務(wù),舊的進(jìn)程一旦處理完已有連接就自動斷開。整個過程使得我們的應(yīng)用的穩(wěn)定性和健壯性大大提高:

總結(jié)

至此,我們完成了一個簡單的基于父子進(jìn)程通信、具備異常重啟進(jìn)程功能的 Web服務(wù)器 就已經(jīng)搭建完成了。對于 Nodejs 多進(jìn)程編程你也有了初步的了解。接下來我們將介紹 cluster模塊,并介紹一下在 Nodejs 中進(jìn)行多線程編程。

以上就是Nodejs搭建多進(jìn)程Web服務(wù)器實現(xiàn)過程的詳細(xì)內(nèi)容,更多關(guān)于Nodejs搭建多進(jìn)程Web服務(wù)器的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 預(yù)防NodeJS命令注入的方法詳解

    預(yù)防NodeJS命令注入的方法詳解

    Node.js和npm為前端生態(tài)中提供了統(tǒng)一的開發(fā)語言、強大的包管理和模塊生態(tài)系統(tǒng)、靈活的構(gòu)建工具和任務(wù)自動化、以及豐富的前端框架和庫等等,本文給大家介紹了如何預(yù)防NodeJS命令注入,文中有詳細(xì)的代碼講解,需要的朋友可以參考下
    2023-12-12
  • nodejs下載指定版本完整圖文步驟

    nodejs下載指定版本完整圖文步驟

    node.js官方版是一款專業(yè)性非常強的瀏覽輔助工具軟件,這款軟件操作十分的簡單、功能也是非常的強勁,下面這篇文章主要給大家介紹了關(guān)于nodejs下載指定版本的相關(guān)資料,需要的朋友可以參考下
    2023-12-12
  • node.js中使用ejs渲染數(shù)據(jù)的代碼實現(xiàn)

    node.js中使用ejs渲染數(shù)據(jù)的代碼實現(xiàn)

    這篇文章主要介紹了node.js中使用ejs渲染數(shù)據(jù),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-11-11
  • 使用upstart把nodejs應(yīng)用封裝為系統(tǒng)服務(wù)實例

    使用upstart把nodejs應(yīng)用封裝為系統(tǒng)服務(wù)實例

    這篇文章主要介紹了使用upstart把nodejs應(yīng)用封裝為系統(tǒng)服務(wù)實例,需要的朋友可以參考下
    2014-06-06
  • express啟用https使用小記

    express啟用https使用小記

    這篇文章主要介紹了express啟用https使用小記,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-05-05
  • Node.js斷點續(xù)傳的實現(xiàn)

    Node.js斷點續(xù)傳的實現(xiàn)

    最近做了個項目,應(yīng)項目需求,需要傳圖片、Excel等,幾M的大小可以很快就上傳到服務(wù)器,但是大的就需要斷點上傳,本文就介紹一下,感興趣的可以了解一下
    2021-05-05
  • NodeJS之優(yōu)缺點及適用場景討論

    NodeJS之優(yōu)缺點及適用場景討論

    這篇文章主要介紹了NodeJS之優(yōu)缺點及適用場景討論,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • node.js中使用socket.io的方法

    node.js中使用socket.io的方法

    這篇文章主要介紹了node.js中使用socket.io的方法,需要的朋友可以參考下
    2014-12-12
  • 淺談Node.js 子進(jìn)程與應(yīng)用場景

    淺談Node.js 子進(jìn)程與應(yīng)用場景

    這篇文章主要介紹了淺談Node.js 子進(jìn)程與應(yīng)用場景,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • Node.js的基本應(yīng)用你知道多少

    Node.js的基本應(yīng)用你知道多少

    這篇文章主要為大家詳細(xì)介紹了Node.js的基本應(yīng)用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02

最新評論