cluster的一些理解

看了cluster不明白他是怎么搞得。為什么master進(jìn)程沒(méi)有監(jiān)聽(tīng)端口號(hào),就能實(shí)現(xiàn)集群模狭∩┧冢看了下資料和源碼读虏,這里做一下簡(jiǎn)單的總結(jié)责静。。

1.cluster.isMaster怎么識(shí)別的盖桥?主進(jìn)程fork的時(shí)候給到子進(jìn)程一個(gè)NODE_UNIQUE_ID灾螃。所以只要環(huán)境變量有這個(gè)參數(shù)就是子進(jìn)程。

2.主進(jìn)程沒(méi)有創(chuàng)建服務(wù)器的語(yǔ)句揩徊,到底有沒(méi)有創(chuàng)建腰鬼?都知道http基于tcp傳輸層的,既然主進(jìn)程沒(méi)有創(chuàng)建服務(wù)器的語(yǔ)句塑荒,是否是通過(guò)子進(jìn)程的相關(guān)操作實(shí)現(xiàn)的熄赡。這里翻開(kāi)nodejs源碼對(duì)于listen的實(shí)現(xiàn):

Server.prototype.listen=function(...args) {

varnormalized=normalizeArgs(args);

varoptions=normalized[0];

varcb=normalized[1];

if(this._handle) {

thrownewerrors.Error('ERR_SERVER_ALREADY_LISTEN');

}

varhasCallback=(cb!==null);

if(hasCallback) {

this.once('listening', cb);

}

varbacklogFromArgs=

// (handle, backlog) or (path, backlog) or (port, backlog)

toNumber(args.length>1&&args[1])||

toNumber(args.length>2&&args[2]);// (port, host, backlog)

options=options._handle||options.handle||options;

// (handle[, backlog][, cb]) where handle is an object with a handle

if(optionsinstanceofTCP) {

this._handle=options;

this[async_id_symbol]=this._handle.getAsyncId();

listenInCluster(this,null,-1,-1, backlogFromArgs);

return this;

}

// (handle[, backlog][, cb]) where handle is an object with a fd

if(typeofoptions.fd==='number'&&options.fd>=0) {

listenInCluster(this,null,null,null, backlogFromArgs, options.fd);

return this;

}

// ([port][, host][, backlog][, cb]) where port is omitted,

// that is, listen(), listen(null), listen(cb), or listen(null, cb)

// or (options[, cb]) where options.port is explicitly set as undefined or

// null, bind to an arbitrary unused port

if(args.length===0|| typeofargs[0]==='function'||

(typeofoptions.port==='undefined'&&'port'inoptions)||

options.port===null) {

options.port=0;

}

// ([port][, host][, backlog][, cb]) where port is specified

// or (options[, cb]) where options.port is specified

// or if options.port is normalized as 0 before

varbacklog;

if(typeofoptions.port==='number'|| typeofoptions.port==='string') {

if(!isLegalPort(options.port)) {

thrownewRangeError('"port" argument must be >= 0 and < 65536');

}

backlog=options.backlog||backlogFromArgs;

// start TCP server listening on host:port

if(options.host) {

lookupAndListen(this, options.port|0, options.host, backlog,

options.exclusive);

}else{// Undefined host, listens on unspecified address

// Default addressType 4 will be used to search for master server

listenInCluster(this,null, options.port|0,4,

backlog,undefined, options.exclusive);

}

return this;

}

// (path[, backlog][, cb]) or (options[, cb])

// where path or options.path is a UNIX domain socket or Windows pipe

if(options.path&&isPipeName(options.path)) {

varpipeName=this._pipeName=options.path;

backlog=options.backlog||backlogFromArgs;

listenInCluster(this, pipeName,-1,-1,

backlog,undefined, options.exclusive);

return this;

}

thrownewError('Invalid listen argument: '+util.inspect(options));

};

可以看出參數(shù)可以是很多類型,這里針對(duì)端口進(jìn)行分析齿税,發(fā)現(xiàn)參數(shù)是端口時(shí)彼硫,執(zhí)行了listenInCluster()函數(shù)然,翻開(kāi)這個(gè)listenInCluster函數(shù)凌箕,發(fā)現(xiàn)代碼是:


function listenInCluster(server, address, port, addressType,

backlog, fd, exclusive) {

exclusive= !!exclusive;

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

if(cluster.isMaster||exclusive) {

// Will create a new handle

// _listen2 sets up the listened handle, it is still named like this

// to avoid breaking code that wraps this method

server._listen2(address, port, addressType, backlog, fd);

return;

}

constserverQuery={

address:address,

port:port,

addressType:addressType,

fd:fd,

flags:0

};

// Get the master's server handle, and listen on it

cluster._getServer(server, serverQuery, listenOnMasterHandle);

function listenOnMasterHandle(err, handle) {

err=checkBindError(err, port, handle);

if(err) {

varex=exceptionWithHostPort(err,'bind', address, port);

returnserver.emit('error', ex);

}

// Reuse master's server handle

server._handle=handle;

// _listen2 sets up the listened handle, it is still named like this

// to avoid breaking code that wraps this method

server._listen2(address, port, addressType, backlog, fd);

}

}

這個(gè)代碼很有意思拧篮,如果是主進(jìn)程執(zhí)行server._listen2(address, port, addressType, backlog, fd);然后return

如果手機(jī)子進(jìn)程發(fā)送serverQuery然后執(zhí)行回調(diào)listenOnMasterHandle,這個(gè)回調(diào)里也是server._listen2(address, port, addressType, backlog, fd);
cluster_getServer的源碼是:

cluster._getServer = function(obj, options, cb) {
  const indexesKey = [options.address,
                      options.port,
                      options.addressType,
                      options.fd ].join(':');

  if (indexes[indexesKey] === undefined)
    indexes[indexesKey] = 0;
  else
    indexes[indexesKey]++;

  const message = util._extend({
    act: 'queryServer',
    index: indexes[indexesKey],
    data: null
  }, options);

  // Set custom data on handle (i.e. tls tickets key)
  if (obj._getServerData)
    message.data = obj._getServerData();

  send(message, (reply, handle) => {
    if (typeof obj._setServerData === 'function')
      obj._setServerData(reply.data);

    if (handle)
      shared(reply, handle, indexesKey, cb);  // Shared listen socket.
    else
      rr(reply, indexesKey, cb);              // Round-robin.
  });

  obj.once('listening', () => {
    cluster.worker.state = 'listening';
    const address = obj.address();
    message.act = 'listening';
    message.port = address && address.port || options.port;
    send(message);
  });
};

大概的意思是將這個(gè)worker的信息比如端口什么的發(fā)給主進(jìn)程牵舱,按照內(nèi)容起個(gè)服務(wù)器串绩,然后就是

  if (handle)
      shared(reply, handle, indexesKey, cb);  // Shared listen socket.
    else
      rr(reply, indexesKey, cb);              // Round-robin.
  });

有句柄的話分享這個(gè)shock,沒(méi)有的話執(zhí)行rr函數(shù):

function rr(message, indexesKey, cb) {
  if (message.errno)
    return cb(message.errno, null);

  var key = message.key;

  function listen(backlog) {
    // TODO(bnoordhuis) Send a message to the master that tells it to
    // update the backlog size. The actual backlog should probably be
    // the largest requested size by any worker.
    return 0;
  }

  function close() {
    // lib/net.js treats server._handle.close() as effectively synchronous.
    // That means there is a time window between the call to close() and
    // the ack by the master process in which we can still receive handles.
    // onconnection() below handles that by sending those handles back to
    // the master.
    if (key === undefined)
      return;

    send({ act: 'close', key });
    delete handles[key];
    delete indexes[indexesKey];
    key = undefined;
  }

  function getsockname(out) {
    if (key)
      util._extend(out, message.sockname);

    return 0;
  }

  // Faux handle. Mimics a TCPWrap with just enough fidelity to get away
  // with it. Fools net.Server into thinking that it's backed by a real
  // handle. Use a noop function for ref() and unref() because the control
  // channel is going to keep the worker alive anyway.
  const handle = { close, listen, ref: noop, unref: noop };

  if (message.sockname) {
    handle.getsockname = getsockname;  // TCP handles only.
  }

  assert(handles[key] === undefined);
  handles[key] = handle;
  cb(0, handle);
}

發(fā)現(xiàn)這個(gè)listen函數(shù) return了并沒(méi)有操作芜壁,將函數(shù)給到handles赏参,然后callback出去,由上面的server._handle=handle接收沿盅。所以其實(shí)工作進(jìn)程的監(jiān)聽(tīng)被hack了,并沒(méi)有操作纫溃。腰涧。

這里再看下server._listen2,源碼中Server.prototype._listen2=setupListenHandle;那么看下setupListenHandle的實(shí)現(xiàn)吧:


function setupListenHandle(address, port, addressType, backlog, fd) {

debug('setupListenHandle', address, port, addressType, backlog, fd);

// If there is not yet a handle, we need to create one and bind.

// In the case of a server sent via IPC, we don't need to do this.

if(this._handle) {

debug('setupListenHandle: have a handle already');

}else{

debug('setupListenHandle: create a handle');

varrval=null;

// Try to bind to the unspecified IPv6 address, see if IPv6 is available

if(!address&& typeoffd!=='number') {

rval=createServerHandle('::', port,6, fd);

if(typeofrval==='number') {

rval=null;

address='0.0.0.0';

addressType=4;

}else{

address='::';

addressType=6;

}

}

if(rval===null)

rval=createServerHandle(address, port, addressType, fd);

if(typeofrval==='number') {

varerror=exceptionWithHostPort(rval,'listen', address, port);

process.nextTick(emitErrorNT,this, error);

return;

}

this._handle=rval;

}

this[async_id_symbol]=getNewAsyncId(this._handle);

this._handle.onconnection=onconnection;

this._handle.owner=this;

// Use a backlog of 512 entries. We pass 511 to the listen() call because

// the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1);

// which will thus give us a backlog of 512 entries.

varerr=this._handle.listen(backlog||511);

if(err) {

varex=exceptionWithHostPort(err,'listen', address, port);

this._handle.close();

this._handle=null;

nextTick(this[async_id_symbol], emitErrorNT,this, ex);

return;

}

// generate connection key, this should be unique to the connection

this._connectionKey=addressType+':'+address+':'+port;

// unref the handle if the server was unref'ed prior to listening

if(this._unref)

this.unref();

nextTick(this[async_id_symbol], emitListeningNT,this);

}

這里發(fā)現(xiàn)代碼成功了執(zhí)行的時(shí)createServerHandle函數(shù)紊浩,聽(tīng)名字是創(chuàng)造socket句柄 窖铡,如果_handle存在就不創(chuàng)建,不存在創(chuàng)建socket坊谁,如前面所寫(xiě)子進(jìn)程已經(jīng)創(chuàng)建了socket费彼,所以不會(huì)再創(chuàng)建socket,所以子進(jìn)程雖然listen了口芍,但是其實(shí)只是表面的而已箍铲。具體服務(wù)器如何接收客戶端請(qǐng)求,涉及到c鬓椭,不會(huì)c颠猴,應(yīng)該是調(diào)用一些底層的東西實(shí)現(xiàn)的吧关划。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市翘瓮,隨后出現(xiàn)的幾起案子贮折,更是在濱河造成了極大的恐慌,老刑警劉巖资盅,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件调榄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡呵扛,警方通過(guò)查閱死者的電腦和手機(jī)每庆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)择份,“玉大人扣孟,你說(shuō)我怎么就攤上這事∪俑希” “怎么了凤价?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)拔创。 經(jīng)常有香客問(wèn)我利诺,道長(zhǎng),這世上最難降的妖魔是什么剩燥? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任慢逾,我火速辦了婚禮,結(jié)果婚禮上灭红,老公的妹妹穿的比我還像新娘侣滩。我一直安慰自己,他們只是感情好变擒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布君珠。 她就那樣靜靜地躺著,像睡著了一般娇斑。 火紅的嫁衣襯著肌膚如雪策添。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天毫缆,我揣著相機(jī)與錄音唯竹,去河邊找鬼。 笑死苦丁,一個(gè)胖子當(dāng)著我的面吹牛浸颓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼猾愿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鹦聪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蒂秘,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泽本,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后姻僧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體规丽,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年撇贺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赌莺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡松嘶,死狀恐怖艘狭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情翠订,我是刑警寧澤巢音,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站尽超,受9級(jí)特大地震影響官撼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜似谁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一傲绣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巩踏,春花似錦秃诵、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至屈梁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間榛了,已是汗流浹背在讶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留霜大,地道東北人构哺。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親曙强。 傳聞我的和親對(duì)象是個(gè)殘疾皇子残拐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測(cè)試 ...
    KeKeMars閱讀 6,313評(píng)論 0 6
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)碟嘴,斷路器溪食,智...
    卡卡羅2017閱讀 134,629評(píng)論 18 139
  • http://python.jobbole.com/85231/ 關(guān)于專業(yè)技能寫(xiě)完項(xiàng)目接著寫(xiě)寫(xiě)一名3年工作經(jīng)驗(yàn)的J...
    燕京博士閱讀 7,557評(píng)論 1 118
  • 1、TCP狀態(tài)linux查看tcp的狀態(tài)命令:1)娜扇、netstat -nat 查看TCP各個(gè)狀態(tài)的數(shù)量2)错沃、lso...
    北辰青閱讀 9,410評(píng)論 0 11
  • 那年,我上初二雀瓢,我們那個(gè)學(xué)校很奇葩枢析,每年都要調(diào)整一下班級(jí),就是每年開(kāi)學(xué)的時(shí)候刃麸,各個(gè)年級(jí)都到操場(chǎng)上去醒叁,每個(gè)班各自報(bào)數(shù)...
    張好奇閱讀 300評(píng)論 5 9