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

Node.Js中實現(xiàn)端口重用原理詳解

 更新時間:2018年05月03日 14:30:44   作者:G_Doe  
這篇文章主要介紹了Node.Js中實現(xiàn)端口重用原理詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

本文介紹了Node.Js中實現(xiàn)端口重用原理詳解,分享給大家,具體如下:

起源,從官方實例中看多進程共用端口

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
 console.log(`Master ${process.pid} is running`);

 for (let i = 0; i < numCPUs; i++) {
  cluster.fork();
 }

 cluster.on('exit', (worker, code, signal) => {
  console.log(`worker ${worker.process.pid} died`);
 });
} else {
 http.createServer((req, res) => {
  res.writeHead(200);
  res.end('hello world\n');
 }).listen(8000);

 console.log(`Worker ${process.pid} started`);
}

執(zhí)行結果:

$ node server.js
Master 3596 is running
Worker 4324 started
Worker 4520 started
Worker 6056 started
Worker 5644 started

了解http.js模塊:

我們都只有要創(chuàng)建一個http服務,必須引用http模塊,http模塊最終會調用net.js實現(xiàn)網(wǎng)絡服務

// lib/net.js
'use strict';

 ...
Server.prototype.listen = function(...args) {
  ...
 if (options instanceof TCP) {
   this._handle = options;
   this[async_id_symbol] = this._handle.getAsyncId();
   listenInCluster(this, null, -1, -1, backlogFromArgs); // 注意這個方法調用了cluster模式下的處理辦法
   return this;
  }
  ...
};

function listenInCluster(server, address, port, addressType,backlog, fd, exclusive) {
// 如果是master 進程或者沒有開啟cluster模式直接啟動listen
if (cluster.isMaster || exclusive) {
  //_listen2,細心的人一定會發(fā)現(xiàn)為什么是listen2而不直接使用listen
 // _listen2 包裹了listen方法,如果是Worker進程,會調用被hack后的listen方法,從而避免出錯端口被占用的錯誤
  server._listen2(address, port, addressType, backlog, fd);
  return;
 }
 const serverQuery = {
  address: address,
  port: port,
  addressType: addressType,
  fd: fd,
  flags: 0
 };

// 是fork 出來的進程,獲取master上的handel,并且監(jiān)聽,
// 現(xiàn)在是不是很好奇_getServer方法做了什么
 cluster._getServer(server, serverQuery, listenOnMasterHandle);
}
 ...

答案很快就可以通過cluster._getServer 這個函數(shù)找到

  1. 代理了server._listen2 這個方法在work進程的執(zhí)行操作
  2. 向master發(fā)送queryServer消息,向master注冊一個內部TCP服務器
// lib/internal/cluster/child.js
cluster._getServer = function(obj, options, cb) {
 // ...
 const message = util._extend({
  act: 'queryServer',  // 關鍵點:構建一個queryServer的消息
  index: indexes[indexesKey],
  data: null
 }, options);

 message.address = address;

// 發(fā)送queryServer消息給master進程,master 在收到這個消息后,會創(chuàng)建一個開始一個server,并且listen
 send(message, (reply, handle) => {
   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);
 });
};
 //...
 // Round-robin. Master distributes handles across workers.
function rr(message, indexesKey, cb) {
  if (message.errno) return cb(message.errno, null);
  var key = message.key;
  // 這里hack 了listen方法
  // 子進程調用的listen方法,就是這個,直接返回0,所以不會報端口被占用的錯誤
  function listen(backlog) {
    return 0;
  }
  // ...
  const handle = { close, listen, ref: noop, unref: noop };
  handles[key] = handle;
  // 這個cb 函數(shù)是net.js 中的listenOnMasterHandle 方法
  cb(0, handle);
}
// lib/net.js
/*
function listenOnMasterHandle(err, handle) {
  err = checkBindError(err, port, handle);
  server._handle = handle;
  // _listen2 函數(shù)中,調用的handle.listen方法,也就是上面被hack的listen
  server._listen2(address, port, addressType, backlog, fd);
 }
*/

master進程收到queryServer消息后進行啟動服務

  1. 如果地址沒被監(jiān)聽過,通過RoundRobinHandle監(jiān)聽開啟服務
  2. 如果地址已經(jīng)被監(jiān)聽,直接綁定handel到已經(jīng)監(jiān)聽到服務上,去消費請求
// lib/internal/cluster/master.js
function queryServer(worker, message) {

  const args = [
    message.address,
    message.port,
    message.addressType,
    message.fd,
    message.index
  ];

  const key = args.join(':');
  var handle = handles[key];

  // 如果地址沒被監(jiān)聽過,通過RoundRobinHandle監(jiān)聽開啟服務
  if (handle === undefined) {
    var constructor = RoundRobinHandle;
    if (schedulingPolicy !== SCHED_RR ||
      message.addressType === 'udp4' ||
      message.addressType === 'udp6') {
      constructor = SharedHandle;
    }

    handles[key] = handle = new constructor(key,
      address,
      message.port,
      message.addressType,
      message.fd,
      message.flags);
  }

  // 如果地址已經(jīng)被監(jiān)聽,直接綁定handel到已經(jīng)監(jiān)聽到服務上,去消費請求
  // Set custom server data
  handle.add(worker, (errno, reply, handle) => {
    reply = util._extend({
      errno: errno,
      key: key,
      ack: message.seq,
      data: handles[key].data
    }, reply);

    if (errno)
      delete handles[key]; // Gives other workers a chance to retry.

    send(worker, reply, handle);
  });
}

看到這一步,已經(jīng)很明顯,我們知道了多進行端口共享的實現(xiàn)原理

  1. 其實端口僅由master進程中的內部TCP服務器監(jiān)聽了一次
  2. 因為net.js 模塊中會判斷當前的進程是master還是Worker進程
  3. 如果是Worker進程調用cluster._getServer 去hack原生的listen 方法
  4. 所以在child調用的listen方法,是一個return 0 的空方法,所以不會報端口占用錯誤

那現(xiàn)在問題來了,既然Worker進程是如何獲取到master進程監(jiān)聽服務接收到的connect呢?

  1. 監(jiān)聽master進程啟動的TCP服務器的connection事件
  2. 通過輪詢挑選出一個worker
  3. 向其發(fā)送newconn內部消息,消息體中包含了客戶端句柄
  4. 有了句柄,誰都知道要怎么處理了哈哈
// lib/internal/cluster/round_robin_handle.js

function RoundRobinHandle(key, address, port, addressType, fd) {

  this.server = net.createServer(assert.fail);

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

  this.server.once('listening', () => {
    this.handle = this.server._handle;
    // 監(jiān)聽onconnection方法
    this.handle.onconnection = (err, handle) => this.distribute(err, handle);
    this.server._handle = null;
    this.server = null;
  });
}

RoundRobinHandle.prototype.add = function (worker, send) {
  // ...
};

RoundRobinHandle.prototype.remove = function (worker) {
  // ...
};

RoundRobinHandle.prototype.distribute = function (err, handle) {
  // 負載均衡地挑選出一個worker
  this.handles.push(handle);
  const worker = this.free.shift();
  if (worker) this.handoff(worker);
};

RoundRobinHandle.prototype.handoff = function (worker) {
  const handle = this.handles.shift();
  const message = { act: 'newconn', key: this.key };
  // 向work進程其發(fā)送newconn內部消息和客戶端的句柄handle
  sendHelper(worker.process, message, handle, (reply) => {
  // ...
    this.handoff(worker);
  });
};

下面讓我們看看Worker進程接收到newconn消息后進行了哪些操作

// lib/child.js
function onmessage(message, handle) {
  if (message.act === 'newconn')
   onconnection(message, handle);
  else if (message.act === 'disconnect')
   _disconnect.call(worker, true);
 }

// Round-robin connection.
// 接收連接,并且處理
function onconnection(message, handle) {
 const key = message.key;
 const server = handles[key];
 const accepted = server !== undefined;

 send({ ack: message.seq, accepted });

 if (accepted) server.onconnection(0, handle);
}

總結

  1. net模塊會對進程進行判斷,是worker 還是master, 是worker的話進行hack net.Server實例的listen方法
  2. worker 調用的listen 方法是hack掉的,直接return 0,不過會向master注冊一個connection接手的事件
  3. master 收到客戶端connection事件后,會輪詢向worker發(fā)送connection上來的客戶端句柄
  4. worker收到master發(fā)送過來客戶端的句柄,這時候就可以處理客戶端請求了

分享出于共享學習的目的,如有錯誤,歡迎大家留言指導,不喜勿噴。也希望大家多多支持腳本之家。

相關文章

  • node版本快速切換及管理方法

    node版本快速切換及管理方法

    這篇文章主要為大家介紹了node版本快速切換及管理方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • nodejs基礎之多進程實例詳解

    nodejs基礎之多進程實例詳解

    這篇文章主要介紹了nodejs基礎之多進程,結合實例形式分析了nodejs多進程的概念、原理、相關函數(shù)使用方法及操作注意事項,需要的朋友可以參考下
    2018-12-12
  • 詳解如何實現(xiàn)自由切換Node.js版本

    詳解如何實現(xiàn)自由切換Node.js版本

    作為開發(fā)者,我們經(jīng)常在不同的項目中需要使用不同版本的 Node.js,有時舊項目需要舊版本,而新項目則可能依賴最新的 Node.js 版本,本文將介紹幾種常用的方法來自由切換 Node.js 版本,需要的朋友可以參考下
    2024-10-10
  • NodeJS創(chuàng)建最簡單的HTTP服務器

    NodeJS創(chuàng)建最簡單的HTTP服務器

    這篇文章主要介紹了NodeJS創(chuàng)建最簡單的HTTP服務器的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • express中static中間件的具體使用方法

    express中static中間件的具體使用方法

    這篇文章主要介紹了express中static中間件的具體使用方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-10-10
  • Node.js使用http模塊實現(xiàn)后臺服務器流程解析

    Node.js使用http模塊實現(xiàn)后臺服務器流程解析

    這篇文章將會教會你前端工程師怎么搭建后臺服務器,做自己的后端開發(fā),同時,在這篇文章開始你就開始正式進入全棧的道路咯!本片文章將細解http模塊,在開始前我們將復習一點計算機網(wǎng)絡的知識
    2022-09-09
  • npm的lock機制解析

    npm的lock機制解析

    這篇文章主要介紹了npm的lock機制解析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-06-06
  • Node.js的進程管理的深入理解

    Node.js的進程管理的深入理解

    這篇文章主要介紹了Node.js的進程管理的深入理解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-01-01
  • 用Node.js遍歷多級目錄處理文件的代碼詳解

    用Node.js遍歷多級目錄處理文件的代碼詳解

    在日常開發(fā)中,處理大量的文件操作是一個常見的需求,特別是在圖像處理和存檔管理領域,本文給大家介紹了如何用Node.js遍歷多級目錄處理文件,文章給出了主要步驟和完整代碼,需要的朋友可以參考下
    2024-06-06
  • Nodejs 和 Electron ubuntu下快速安裝過程

    Nodejs 和 Electron ubuntu下快速安裝過程

    本文較為詳細的給大家介紹了Nodejs 和 Electron ubuntu下快速安裝過程,非常不錯,具有一定的參考借鑒價值,感興趣的朋友跟隨腳本之家小編一起學習吧
    2018-05-05

最新評論

玉山县| 哈尔滨市| 红河县| 荔波县| 邻水| 海城市| 栖霞市| 盘锦市| 泰州市| 彰化市| 神木县| 临安市| 金山区| 砀山县| 海伦市| 赣州市| 任丘市| 安图县| 奈曼旗| 开原市| 舒兰市| 密山市| 新安县| 武穴市| 涞水县| 军事| 绿春县| 临湘市| 甘南县| 金坛市| 扶风县| 友谊县| 鄂州市| 和政县| 宁远县| 长子县| 五台县| 清镇市| 乌拉特后旗| 辽阳市| 灵石县|