Heim >Web-Frontend >js-Tutorial >Detaillierte Erläuterung der Schritte zur Implementierung der Port-Wiederverwendung in Node.Js

Detaillierte Erläuterung der Schritte zur Implementierung der Port-Wiederverwendung in Node.Js

php中世界最好的语言
php中世界最好的语言Original
2018-05-14 11:31:091858Durchsuche

Dieses Mal werde ich Ihnen die Schritte zur Implementierung der Port-Wiederverwendung in Node.Js ausführlich erläutern. Welche Vorsichtsmaßnahmen gibt es für die Implementierung der Port-Wiederverwendung in Node.Js? Schauen wir uns das einmal an.

Origin, siehe den gemeinsam genutzten Multiprozess-Port der offiziellen Instanz

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('hello world\n');
 }).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 alle brauchen nur Um einen http-Dienst zu erstellen, müssen auf das http-Modul verweisen, das schließlich net.js aufruft, um Netzwerkdienste zu implementieren

// 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,细心的人一定会发现为什么是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 bald über die Funktion „cluster._getServer“

gefunden werden
  1. Proxy server._listen2 Diese Methode führt Vorgänge im Arbeitsprozess aus

  2. Sendet eine queryServer-Nachricht an den Master und registriert einen internen TCP-Server beim Master

// 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;
// 发送queryServer消息给master进程,master 在收到这个消息后,会创建一个开始一个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 函数是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 überwacht wurde, starten Sie den Dienst über die RoundRobinHandle-Überwachung

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

// 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];
  // 如果地址没被监听过,通过RoundRobinHandle监听开启服务
  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);
  }
  // 如果地址已经被监听,直接绑定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 diesen Schritt an Es ist bereits klar, dass wir wissen, wie wir mehr Ports nutzen können. Das Implementierungsprinzip des Teilens

  1. Tatsächlich wird der Port nur einmal vom internen TCP-Server im Masterprozess überwacht

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

  3. Wenn der Worker-Prozess „cluster._getServer“ aufruft, hacken die native Listen-Methode

  4. also im untergeordneten Element aufrufen Die Listen-Methode ist eine leere Methode, die 0 zurückgibt, sodass der Portbelegungsfehler nicht gemeldet wird

Jetzt kommt die Frage, denn wie erhält der Worker-Prozess den Empfang des Master-Prozess-Listening-Dienstes? Was ist mit der Verbindung?

  1. Überwachen Sie die VerbindungEreignis des vom Masterprozess gestarteten TCP-Servers

  2. Wählen Sie einen Worker durch Abfrage aus

  3. Senden Sie eine interne Nachricht von newconn 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('listening', () => {
    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: 'newconn', 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 === '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);
}

Zusammenfassung

  1. Das Netzmodul beurteilt, ob es sich um einen Worker oder einen Master handelt. Hacken Sie die Listen-Methode der net.Server-Instanz

  2. Listen wird vom Worker aufgerufen. Die Methode wird gehackt und gibt direkt 0 zurück, registriert jedoch ein Verbindungsübernahmeereignis beim Master.

  3. Nachdem der Master das Client-Verbindungsereignis empfangen hat, wird dies der Fall sein Abfragen und Senden der Verbindung an den Worker

  4. Der Worker empfängt das vom Master gesendete Client-Handle. Zu diesem Zeitpunkt kann er die Client-Anfrage verarbeiten

Ich glaube, ich habe diesen Artikel gelesen. Sie beherrschen die Fallmethode. Weitere spannende Informationen finden Sie in anderen verwandten Artikeln auf der chinesischen PHP-Website!

Empfohlene Lektüre:

Detaillierte Erläuterung der Schritte zur Verwendung der Vue-Mint-UI-Tabbar-Komponente

Vue+ Modifikator-Trigger Veranstaltung

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Schritte zur Implementierung 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