眾所周知,Node.js中的JavaScript代碼執行在單線程中,非常脆弱,一旦出現了未捕獲的異常,那么整個應用就會崩潰。這在許多場景下,尤其是web應用中,是無法忍受的。通常的解決方案,便是使用Node.js中自帶的cluster模塊,以master-worker模式啟動多個應用實例。然而大家在享受cluster模塊帶來的福祉的同時,不少人也開始好奇:
讓我們從Node.js項目的lib/cluster.js中的代碼里,來一勘究竟。
問題一
為了得到這個問題的解答,我們先從worker進程的初始化看起,master進程在fork工作進程時,會為其附上環境變量NODE_UNIQUE_ID,是一個從零開始的遞增數:
// 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在初始化時,會根據該環境變量,來判斷該進程是否為cluster模塊fork出的工作進程,若是,則執行workerInit()函數來初始化環境,否則執行masterInit()函數。
在workerInit()函數中,定義了cluster._getServer方法,這個方法在任何net.Server實例的listen方法中,會被調用:
// 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); }}你可能已經猜到,問題一的答案,就在這個cluster._getServer函數的代碼中。它主要干了兩件事:
對于第一件事,由于master在接收,傳遞請求給worker時,會符合一定的負載均衡規則(在非Windows平臺下默認為輪詢),這些邏輯被封裝在RoundRobinHandle類中。故,初始化內部TCP服務器等操作也在此處:
// 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. /// ...}            
新聞熱點
疑難解答
圖片精選