国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

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

leeon / 1371人閱讀

摘要:通常的解決方案,便是使用中自帶的模塊,以模式啟動(dòng)多個(gè)應(yīng)用實(shí)例。最后中的模塊除了上述提到的功能外,其實(shí)還提供了非常豐富的供和進(jìn)程之前通信,對于不同的操作系統(tǒng)平臺,也提供了不同的默認(rèn)行為。如果大家有閑,非常推薦完整領(lǐng)略一下模塊的代碼實(shí)現(xiàn)。

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

為什么我的應(yīng)用代碼中明明有app.listen(port);,但cluter模塊在多次fork這份代碼時(shí),卻沒有報(bào)端口已被占用?

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ì)為其附上環(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í),會(huì)根據(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方法中,會(huì)被調(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ù)的代碼中。它主要干了兩件事:

向master進(jìn)程注冊該worker,若master進(jìn)程是第一次接收到監(jiān)聽此端口/描述符下的worker,則起一個(gè)內(nèi)部TCP服務(wù)器,來承擔(dān)監(jiān)聽該端口/描述符的職責(zé),隨后在master中記錄下該worker。

Hack掉worker進(jìn)程中的net.Server實(shí)例的listen方法里監(jiān)聽端口/描述符的部分,使其不再承擔(dān)該職責(zé)。

對于第一件事,由于master在接收,傳遞請求給worker時(shí),會(huì)符合一定的負(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方法,最終會(huì)調(diào)用自身_handle屬性下listen方法來完成監(jiān)聽動(dòng)作,故在代碼中修改之:

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

function rr(message, cb) {
  // ...
  // 此處的listen函數(shù)不再做任何監(jiān)聽動(dòng)作
  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將會(huì)被賦值給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é)下:

端口僅由master進(jìn)程中的內(nèi)部TCP服務(wù)器監(jiān)聽了一次。

不會(huì)出現(xiàn)端口被重復(fù)監(jiān)聽報(bào)錯(cuò),是由于,worker進(jìn)程中,最后執(zhí)行監(jiān)聽端口操作的方法,已被cluster模塊主動(dòng)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
// ...

// 該方法會(huì)在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é)一下:

所有請求先同一經(jīng)過內(nèi)部TCP服務(wù)器。

在內(nèi)部TCP服務(wù)器的請求處理邏輯中,有負(fù)載均衡地挑選出一個(gè)worker進(jìn)程,將其發(fā)送一個(gè)newconn內(nèi)部消息,隨消息發(fā)送客戶端句柄。

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

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/78907.html

相關(guān)文章

  • 初識Node.js

    摘要:一旦替換已經(jīng)完成,該模塊將被完全棄用。用作錯(cuò)誤處理事件文件,由在標(biāo)準(zhǔn)功能上的簡單包裝器提供所有模塊都提供這些對象。 Node.js簡介 Node 定義 Node.js是一個(gè)建立在Chrome v8 引擎上的javascript運(yùn)行時(shí)環(huán)境 Node 特點(diǎn) 異步事件驅(qū)動(dòng) showImg(https://segmentfault.com/img/bVMLD1?w=600&h=237); no...

    lk20150415 評論0 收藏0
  • 深入理解Node.js 進(jìn)程與線程(8000長文徹底搞懂)

    摘要:在單核系統(tǒng)之上我們采用單進(jìn)程單線程的模式來開發(fā)。由進(jìn)程來管理所有的子進(jìn)程,主進(jìn)程不負(fù)責(zé)具體的任務(wù)處理,主要工作是負(fù)責(zé)調(diào)度和管理。模塊與模塊總結(jié)無論是模塊還是模塊,為了解決實(shí)例單線程運(yùn)行,無法利用多核的問題而出現(xiàn)的。 前言 進(jìn)程與線程是一個(gè)程序員的必知概念,面試經(jīng)常被問及,但是一些文章內(nèi)容只是講講理論知識,可能一些小伙伴并沒有真的理解,在實(shí)際開發(fā)中應(yīng)用也比較少。本篇文章除了介紹概念,通過...

    Harpsichord1207 評論0 收藏0
  • dubbo源碼解析(一)Hello,Dubbo

    摘要:英文全名為,也叫遠(yuǎn)程過程調(diào)用,其實(shí)就是一個(gè)計(jì)算機(jī)通信協(xié)議,它是一種通過網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請求服務(wù)而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議。 Hello,Dubbo 你好,dubbo,初次見面,我想和你交個(gè)朋友。 Dubbo你到底是什么? 先給出一套官方的說法:Apache Dubbo是一款高性能、輕量級基于Java的RPC開源框架。 那么什么是RPC? 文檔地址:http://dubbo.a...

    evin2016 評論0 收藏0
  • 系列3|走進(jìn)Node.js之多進(jìn)程模型

    摘要:例如,在方法中,如果需要主從進(jìn)程之間建立管道,則通過環(huán)境變量來告知從進(jìn)程應(yīng)該綁定的相關(guān)的文件描述符,這個(gè)特殊的環(huán)境變量后面會(huì)被再次涉及到。 文:正龍(滬江網(wǎng)校Web前端工程師)本文原創(chuàng),轉(zhuǎn)載請注明作者及出處 之前的文章走進(jìn)Node.js之HTTP實(shí)現(xiàn)分析中,大家已經(jīng)了解 Node.js 是如何處理 HTTP 請求的,在整個(gè)處理過程,它僅僅用到單進(jìn)程模型。那么如何讓 Web 應(yīng)用擴(kuò)展到...

    snowell 評論0 收藏0
  • 前端20個(gè)靈魂拷問 徹底搞明白你就是級前端工程師 【下篇】

    摘要:安裝后已經(jīng)完成了安裝,并且等待其他的線程被關(guān)閉。激活后在這個(gè)狀態(tài)會(huì)處理事件回調(diào)提供了更新緩存策略的機(jī)會(huì)。并可以處理功能性的事件請求后臺同步推送。廢棄狀態(tài)這個(gè)狀態(tài)表示一個(gè)的生命周期結(jié)束。 showImg(https://segmentfault.com/img/bVbwWJu?w=2056&h=1536); 不知不覺,已經(jīng)來到了最后的下篇 其實(shí)我寫的東西你如果認(rèn)真去看,跟著去寫,應(yīng)該能有...

    fireflow 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<