Heim  >  Artikel  >  Web-Frontend  >  Ausführliche Erläuterung des Prinzips der Port-Wiederverwendung in Node.Js

Ausführliche Erläuterung des Prinzips der Port-Wiederverwendung in Node.Js

不言
不言Original
2018-05-05 11:28:431513Durchsuche

In diesem Artikel wird hauptsächlich das Prinzip der Port-Wiederverwendung in Node.Js vorgestellt. Es hat einen gewissen Referenzwert. Jetzt können Freunde in Not darauf zurückgreifen.

Dieser Artikel stellt vor Node.Js Eine detaillierte Erklärung des Prinzips der Port-Wiederverwendung wird mit allen geteilt. Die Details lauten wie folgt:

Origin, siehe Multi-Process-Shared-Ports aus offiziellen Beispielen

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(&#39;exit&#39;, (worker, code, signal) => {
  console.log(`worker ${worker.process.pid} died`);
 });
} else {
 http.createServer((req, res) => {
  res.writeHead(200);
  res.end(&#39;hello world\n&#39;);
 }).listen(8000);

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

Ausführungsergebnis:

$ node server.js
Master 3596 wird ausgeführt
Worker 4324 gestartet
Worker 4520 gestartet
Worker 6056 gestartet
Worker 5644 gestartet

Verstehen Sie das http.js-Modul:

Wir müssen alle einen http-Dienst erstellen und auf das http-Modul verweisen wird schließlich net.js aufrufen, um Netzwerkdienste zu implementieren

// lib/net.js
&#39;use strict&#39;;

 ...
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,细心的人一定会发现为什么是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,并且监听,
// 现在是不是很好奇_getServer方法做了什么
 cluster._getServer(server, serverQuery, listenOnMasterHandle);
}
 ...

Die Antwort kann schnell über die Funktion „cluster._getServer“ gefunden werden

  1. Proxy server._listen2-Methode Während der Ausführung des Arbeitsprozesses

  2. eine queryServer-Nachricht an den Master senden und einen internen TCP-Server beim Master registrieren

// lib/internal/cluster/child.js
cluster._getServer = function(obj, options, cb) {
 // ...
 const message = util._extend({
  act: &#39;queryServer&#39;,  // 关键点:构建一个queryServer的消息
  index: indexes[indexesKey],
  data: null
 }, options);

 message.address = address;

// 发送queryServer消息给master进程,master 在收到这个消息后,会创建一个开始一个server,并且listen
 send(message, (reply, handle) => {
   rr(reply, indexesKey, cb);       // Round-robin.
 });

 obj.once(&#39;listening&#39;, () => {
  cluster.worker.state = &#39;listening&#39;;
  const address = obj.address();
  message.act = &#39;listening&#39;;
  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 函数是net.js 中的listenOnMasterHandle 方法
  cb(0, handle);
}
// lib/net.js
/*
function listenOnMasterHandle(err, handle) {
  err = checkBindError(err, port, handle);
  server._handle = handle;
  // _listen2 函数中,调用的handle.listen方法,也就是上面被hack的listen
  server._listen2(address, port, addressType, backlog, fd);
 }
*/

Der Masterprozess startet den Dienst nach Erhalt der queryServer-Nachricht

  1. Wenn die Adresse nicht angegeben wurde überwacht, starten Sie den Dienst über die RoundRobinHandle-Überwachung

  2. Wenn die Adresse überwacht wurde, binden Sie das Handle direkt an den überwachten Dienst und verbrauchen Sie die Anforderung

// 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(&#39;:&#39;);
  var handle = handles[key];

  // 如果地址没被监听过,通过RoundRobinHandle监听开启服务
  if (handle === undefined) {
    var constructor = RoundRobinHandle;
    if (schedulingPolicy !== SCHED_RR ||
      message.addressType === &#39;udp4&#39; ||
      message.addressType === &#39;udp6&#39;) {
      constructor = SharedHandle;
    }

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

  // 如果地址已经被监听,直接绑定handel到已经监听到服务上,去消费请求
  // 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);
  });
}

Sehen Sie sich das an. Im ersten Schritt ist bereits klar, dass wir das Umsetzungsprinzip des Multi-Port-Sharings kennen

  1. Tatsächlich wird der Port nur einmal vom internen TCP-Server im Masterprozess abgehört

  2. Da das net.js-Modul bestimmt, ob der aktuelle Prozess ein Master- oder Masterprozess ist ein Worker-Prozess

  3. Wenn es sich um einen Worker-Prozess handelt, rufen Sie „cluster._getServer“ auf, um die native Listen-Methode zu hacken

  4. Also wird die Listen-Methode aufgerufen Das Kind ist eine leere Methode mit der Rückgabe 0, daher wird der Portbelegungsfehler nicht gemeldet.

Jetzt kommt das Problem, denn wie erhält der Worker-Prozess die vom Master-Prozess empfangene Verbindung? Zuhörerdienst?

  1. Überwachen Sie das vom Masterprozess gestartete Verbindungsereignis des TCP-Servers

  2. Wählen Sie einen Worker durch Abfrage aus

  3. Senden Sie eine interne Newconn-Nachricht daran. Der Nachrichtentext enthält das Client-Handle

  4. Mit dem Handle weiß jeder, was zu tun ist, haha

// 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(&#39;listening&#39;, () => {
    this.handle = this.server._handle;
    // 监听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: &#39;newconn&#39;, key: this.key };
  // 向work进程其发送newconn内部消息和客户端的句柄handle
  sendHelper(worker.process, message, handle, (reply) => {
  // ...
    this.handoff(worker);
  });
};

Sehen wir uns an, welche Vorgänge der Worker-Prozess nach Erhalt der Newconn-Nachricht durchgeführt hat

// lib/child.js
function onmessage(message, handle) {
  if (message.act === &#39;newconn&#39;)
   onconnection(message, handle);
  else if (message.act === &#39;disconnect&#39;)
   _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);
}

Zusammenfassung

  1. Das Netzmodul beurteilt den Prozess, ob es sich um einen Worker oder einen Master handelt, und hackt ihn net.Server-Instanz. Listen-Methode

  2. Die vom Worker aufgerufene Listen-Methode wird gehackt und gibt direkt 0 zurück, aber ein Verbindungsereignis wird beim Master registriert

  3. Nachdem der Master das Client-Verbindungsereignis empfangen hat, fragt er den Worker ab und sendet das Client-Handle

  4. Worker empfängt das vom Master gesendete Client-Handle und kann es dann verarbeiten Angefordert

Verwandte Empfehlungen:

Knoten implementiert statischen Ressourcenserver

Das obige ist der detaillierte Inhalt vonAusführliche Erläuterung des Prinzips der Port-Wiederverwendung in Node.Js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn