最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解通過源碼解析Node.js中cluster模塊的主要功能實(shí)現(xiàn)

 更新時(shí)間:2018年05月16日 10:21:16   作者:DavidCai1993  
這篇文章主要介紹了詳解通過源碼解析Node.js中cluster模塊的主要功能實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

眾所周知,Node.js中的JavaScript代碼執(zhí)行在單線程中,非常脆弱,一旦出現(xiàn)了未捕獲的異常,那么整個(gè)應(yīng)用就會崩潰。這在許多場景下,尤其是web應(yīng)用中,是無法忍受的。通常的解決方案,便是使用Node.js中自帶的cluster模塊,以master-worker模式啟動多個(gè)應(yīng)用實(shí)例。然而大家在享受cluster模塊帶來的福祉的同時(shí),不少人也開始好奇:

  1. 為什么我的應(yīng)用代碼中明明有app.listen(port);,但cluter模塊在多次fork這份代碼時(shí),卻沒有報(bào)端口已被占用?
  2. Master是如何將接收的請求傳遞至worker中進(jìn)行處理然后響應(yīng)的?

讓我們從Node.js項(xiàng)目的lib/cluster.js中的代碼里,來一勘究竟。

問題一

為了得到這個(gè)問題的解答,我們先從worker進(jìn)程的初始化看起,master進(jìn)程在fork工作進(jìn)程時(shí),會為其附上環(huán)境變量NODE_UNIQUE_ID,是一個(gè)從零開始的遞增數(shù):

// lib/cluster.js
// ...

function createWorkerProcess(id, env) {
 // ...
 workerEnv.NODE_UNIQUE_ID = '' + id;

 // ...
 return fork(cluster.settings.exec, cluster.settings.args, {
  env: workerEnv,
  silent: cluster.settings.silent,
  execArgv: execArgv,
  gid: cluster.settings.gid,
  uid: cluster.settings.uid
 });
}

隨后Node.js在初始化時(shí),會根據(jù)該環(huán)境變量,來判斷該進(jìn)程是否為cluster模塊fork出的工作進(jìn)程,若是,則執(zhí)行workerInit()函數(shù)來初始化環(huán)境,否則執(zhí)行masterInit()函數(shù)。

在workerInit()函數(shù)中,定義了cluster._getServer方法,這個(gè)方法在任何net.Server實(shí)例的listen方法中,會被調(diào)用:

// lib/net.js
// ...

function listen(self, address, port, addressType, backlog, fd, exclusive) {
 exclusive = !!exclusive;

 if (!cluster) cluster = require('cluster');

 if (cluster.isMaster || exclusive) {
  self._listen2(address, port, addressType, backlog, fd);
  return;
 }

 cluster._getServer(self, {
  address: address,
  port: port,
  addressType: addressType,
  fd: fd,
  flags: 0
 }, cb);

 function cb(err, handle) {
  // ...

  self._handle = handle;
  self._listen2(address, port, addressType, backlog, fd);
 }
}

你可能已經(jīng)猜到,問題一的答案,就在這個(gè)cluster._getServer函數(shù)的代碼中。它主要干了兩件事:

  1. 向master進(jìn)程注冊該worker,若master進(jìn)程是第一次接收到監(jiān)聽此端口/描述符下的worker,則起一個(gè)內(nèi)部TCP服務(wù)器,來承擔(dān)監(jiān)聽該端口/描述符的職責(zé),隨后在master中記錄下該worker。
  2. Hack掉worker進(jìn)程中的net.Server實(shí)例的listen方法里監(jiān)聽端口/描述符的部分,使其不再承擔(dān)該職責(zé)。

對于第一件事,由于master在接收,傳遞請求給worker時(shí),會符合一定的負(fù)載均衡規(guī)則(在非Windows平臺下默認(rèn)為輪詢),這些邏輯被封裝在RoundRobinHandle類中。故,初始化內(nèi)部TCP服務(wù)器等操作也在此處:

// lib/cluster.js
// ...

function RoundRobinHandle(key, address, port, addressType, backlog, fd) {
 // ...
 this.handles = [];
 this.handle = null;
 this.server = net.createServer(assert.fail);

 if (fd >= 0)
  this.server.listen({ fd: fd });
 else if (port >= 0)
  this.server.listen(port, address);
 else
  this.server.listen(address); // UNIX socket path.

 /// ...
}

對于第二件事,由于net.Server實(shí)例的listen方法,最終會調(diào)用自身_handle屬性下listen方法來完成監(jiān)聽動作,故在代碼中修改之:

// lib/cluster.js
// ...

function rr(message, cb) {
 // ...
 // 此處的listen函數(shù)不再做任何監(jiān)聽動作
 function listen(backlog) {
  return 0;
 }

 function close() {
  // ...
 }
 function ref() {}
 function unref() {}

 var handle = {
  close: close,
  listen: listen,
  ref: ref,
  unref: unref,
 };
 // ...
 handles[key] = handle;
 cb(0, handle); // 傳入這個(gè)cb中的handle將會被賦值給net.Server實(shí)例中的_handle屬性
}

// lib/net.js
// ...
function listen(self, address, port, addressType, backlog, fd, exclusive) {
 // ...

 if (cluster.isMaster || exclusive) {
  self._listen2(address, port, addressType, backlog, fd);
  return; // 僅在worker環(huán)境下改變
 }

 cluster._getServer(self, {
  address: address,
  port: port,
  addressType: addressType,
  fd: fd,
  flags: 0
 }, cb);

 function cb(err, handle) {
  // ...
  self._handle = handle;
  // ...
 }
}

至此,第一個(gè)問題便已豁然開朗了,總結(jié)下:

  1. 端口僅由master進(jìn)程中的內(nèi)部TCP服務(wù)器監(jiān)聽了一次。
  2. 不會出現(xiàn)端口被重復(fù)監(jiān)聽報(bào)錯(cuò),是由于,worker進(jìn)程中,最后執(zhí)行監(jiān)聽端口操作的方法,已被cluster模塊主動hack。

問題二

解決了問題一,問題二的解決就明朗輕松許多了。通過問題一我們已得知,監(jiān)聽端口的是master進(jìn)程中創(chuàng)建的內(nèi)部TCP服務(wù)器,所以第二個(gè)問題的解決,著手點(diǎn)就是該內(nèi)部TCP服務(wù)器接手連接時(shí),執(zhí)行的操作。Cluster模塊的做法是,監(jiān)聽該內(nèi)部TCP服務(wù)器的connection事件,在監(jiān)聽器函數(shù)里,有負(fù)載均衡地挑選出一個(gè)worker,向其發(fā)送newconn內(nèi)部消息(消息體對象中包含cmd: 'NODE_CLUSTER'屬性)以及一個(gè)客戶端句柄(即connection事件處理函數(shù)的第二個(gè)參數(shù)),相關(guān)代碼如下:

// lib/cluster.js
// ...

function RoundRobinHandle(key, address, port, addressType, backlog, fd) {
 // ...
 this.server = net.createServer(assert.fail);
 // ...

 var self = this;
 this.server.once('listening', function() {
  // ...
  self.handle.onconnection = self.distribute.bind(self);
 });
}

RoundRobinHandle.prototype.distribute = function(err, handle) {
 this.handles.push(handle);
 var worker = this.free.shift();
 if (worker) this.handoff(worker);
};

RoundRobinHandle.prototype.handoff = function(worker) {
 // ...
 var message = { act: 'newconn', key: this.key };
 var self = this;
 sendHelper(worker.process, message, handle, function(reply) {
  // ...
 });
};

Worker進(jìn)程在接收到了newconn內(nèi)部消息后,根據(jù)傳遞過來的句柄,調(diào)用實(shí)際的業(yè)務(wù)邏輯處理并返回:

// lib/cluster.js
// ...

// 該方法會在Node.js初始化時(shí)由 src/node.js 調(diào)用
cluster._setupWorker = function() {
 // ...
 process.on('internalMessage', internal(worker, onmessage));

 // ...
 function onmessage(message, handle) {
  if (message.act === 'newconn')
   onconnection(message, handle);
  // ...
 }
};

function onconnection(message, handle) {
 // ...
 var accepted = server !== undefined;
 // ...
 if (accepted) server.onconnection(0, handle);
}

至此,問題二也得到了解決,也總結(jié)一下:

  1. 所有請求先同一經(jīng)過內(nèi)部TCP服務(wù)器。
  2. 在內(nèi)部TCP服務(wù)器的請求處理邏輯中,有負(fù)載均衡地挑選出一個(gè)worker進(jìn)程,將其發(fā)送一個(gè)newconn內(nèi)部消息,隨消息發(fā)送客戶端句柄。
  3. Worker進(jìn)程接收到此內(nèi)部消息,根據(jù)客戶端句柄創(chuàng)建net.Socket實(shí)例,執(zhí)行具體業(yè)務(wù)邏輯,返回。

最后

Node.js中的cluster模塊除了上述提到的功能外,其實(shí)還提供了非常豐富的API供master和worker進(jìn)程之前通信,對于不同的操作系統(tǒng)平臺,也提供了不同的默認(rèn)行為。本文僅挑選了一條功能線進(jìn)行了分析闡述。如果大家有閑,非常推薦完整領(lǐng)略一下cluster模塊的代碼實(shí)現(xiàn)。

參考:

https://github.com/nodejs/node/blob/master/lib/cluster.js
https://github.com/nodejs/node/blob/master/lib/net.js

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Node.js中創(chuàng)建和管理外部進(jìn)程詳解

    Node.js中創(chuàng)建和管理外部進(jìn)程詳解

    這篇文章主要介紹了Node.js中創(chuàng)建和管理外部進(jìn)程詳解,本文講解了執(zhí)行外部命令的方法、子進(jìn)程相關(guān)內(nèi)容等,需要的朋友可以參考下
    2014-08-08
  • 在nodejs中使用swagger方式

    在nodejs中使用swagger方式

    這篇文章主要介紹了在nodejs中使用swagger方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Node.js操作Firebird數(shù)據(jù)庫教程

    Node.js操作Firebird數(shù)據(jù)庫教程

    這篇文章主要為大家分享了Node.js操作Firebird數(shù)據(jù)庫教程,思路清晰便于大家理解,感興趣的小伙伴們可以參考一下
    2016-03-03
  • 在Node.js中使用TypeScript的方法示例

    在Node.js中使用TypeScript的方法示例

    在前端開發(fā)中,隨著TypeScript的不斷普及,越來越多的開發(fā)者使用TypeScript進(jìn)行開發(fā),然而,在后端開發(fā)中,在Node.js中使用 TypeScript還是相對較少見的,這篇文章將介紹如何在Node.js中配置TypeScript ,以及如何使用TypeScript來開發(fā)高質(zhì)量的應(yīng)用程序
    2023-06-06
  • 詳解nodeJS之二進(jìn)制buffer對象

    詳解nodeJS之二進(jìn)制buffer對象

    本篇文章主要介紹了nodeJS之二進(jìn)制buffer對象,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06
  • 利用node.js搭建簡單web服務(wù)器的方法教程

    利用node.js搭建簡單web服務(wù)器的方法教程

    本文主題是使用node來搭建最簡單的web服務(wù)器,其后可以自己根據(jù)需要深入了解,目前在開發(fā)過程中可以用來模擬與服務(wù)器進(jìn)行簡單的交互,比如返回的資源控制等。需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。
    2017-02-02
  • node.js中的fs.appendFileSync方法使用說明

    node.js中的fs.appendFileSync方法使用說明

    這篇文章主要介紹了node.js中的fs.appendFileSync方法使用說明,本文介紹了fs.appendFileSync方法說明、語法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • 基于Node.js實(shí)現(xiàn)一鍵生成個(gè)性化二維碼

    基于Node.js實(shí)現(xiàn)一鍵生成個(gè)性化二維碼

    這篇文章主要為大家詳細(xì)介紹了如何使用Node.js、Jimp和QRCode庫,結(jié)合一個(gè)簡單的腳本,通過命令行命令來快速給二維碼加上指定的背景,打造更有個(gè)性化的二維碼,感興趣的可以了解下
    2024-03-03
  • 一文秒懂nodejs中的異步編程

    一文秒懂nodejs中的異步編程

    這篇文章主要介紹了深入理解nodejs中的異步編程,本文給大家介紹的非常想詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • node.js實(shí)現(xiàn)多圖片上傳實(shí)例

    node.js實(shí)現(xiàn)多圖片上傳實(shí)例

    這篇文章主要介紹了node.js實(shí)現(xiàn)多圖片上傳實(shí)例,包括路由、控制器和視圖的源碼,重點(diǎn)在圖片上傳處理程序,需要的朋友可以參考下
    2014-06-06

最新評論

墨竹工卡县| 永州市| 星座| 阿鲁科尔沁旗| 象山县| 临城县| 康乐县| 新民市| 白山市| 雷波县| 金坛市| 怀远县| 仁布县| 三都| 淮南市| 德庆县| 河池市| 桦甸市| 宣武区| 馆陶县| 高州市| 海伦市| 宜君县| 武强县| 越西县| 和平区| 仙桃市| 陆川县| 新营市| 利川市| 黔东| 四平市| 象山县| 阜新| 蓬溪县| 德保县| 泗水县| 梓潼县| 丹阳市| 怀宁县| 苗栗县|