Ошибка: проверка схемы не удалась из-за следующих ошибок: путь к данным «» НЕ должен иметь дополнительных свойств (проект)

Изменить: в Socket.IO 1.0+ вместо установки хранилища с несколькими клиентами Redis теперь можно использовать более простой модуль адаптера Redis.

var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));

Пример, показанный ниже, будет больше похож на это:

var cluster = require('cluster');
var os = require('os');

if (cluster.isMaster) {
  // we create a HTTP server, but we do not use listen
  // that way, we have a socket.io server that doesn't accept connections
  var server = require('http').createServer();
  var io = require('socket.io').listen(server);
  var redis = require('socket.io-redis');

  io.adapter(redis({ host: 'localhost', port: 6379 }));

  setInterval(function() {
    // all workers will receive this in Redis, and emit
    io.emit('data', 'payload');
  }, 1000);

  for (var i = 0; i < os.cpus().length; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  }); 
}

if (cluster.isWorker) {
  var express = require('express');
  var app = express();

  var http = require('http');
  var server = http.createServer(app);
  var io = require('socket.io').listen(server);
  var redis = require('socket.io-redis');

  io.adapter(redis({ host: 'localhost', port: 6379 }));
  io.on('connection', function(socket) {
    socket.emit('data', 'connected to worker: ' + cluster.worker.id);
  });

  app.listen(80);
}

Если у вас есть главный узел, который необходимо публиковать в других процессах Socket.IO, но сам не принимает соединения сокетов, используйте socket.io-emitter вместо socket.io-redis .

Если у вас проблемы с масштабированием, запустите приложения Node с помощью DEBUG=*. Socket.IO теперь реализует debug , который также будет распечатывать отладочные сообщения адаптера Redis. Пример вывода:

socket.io:server initializing namespace / +0ms
socket.io:server creating engine.io instance with opts {"path":"/socket.io"} +2ms
socket.io:server attaching client serving req handler +2ms
socket.io-parser encoding packet {"type":2,"data":["event","payload"],"nsp":"/"} +0ms
socket.io-parser encoded {"type":2,"data":["event","payload"],"nsp":"/"} as 2["event","payload"] +1ms
socket.io-redis ignore same uid +0ms

Если оба основных и дочерних процесса отображают одинаковые сообщения синтаксического анализатора, ваше приложение правильно масштабируется.


Не должно быть проблема с вашей настройкой, если вы испускаете одного работника. То, что вы делаете, исходит от всех четырех рабочих, и из-за публикации / подписки Redis сообщения не дублируются, а четыре раза записываются, как вы просили приложение сделать. Вот простая схема того, что делает Редис:

Client  <--  Worker 1 emit -->  Redis
Client  <--  Worker 2  <----------|
Client  <--  Worker 3  <----------|
Client  <--  Worker 4  <----------|

Как вы можете видеть, когда вы испускаете работника, он опубликует излучение в Redis, и он будет отражен от других работников, которые имеют подписались на базу данных Redis. Это также означает, что вы можете использовать несколько серверов сокетов, подключенных к одному и тому же экземпляру, и на всех подключенных серверах будет запущен излучение на одном сервере.

С кластером, когда клиент подключается, он подключается к одному из ваши четыре рабочих, а не все четыре. Это также означает, что все, что вы испускаете от этого работника, будет отображаться только одному клиенту. Итак, да, приложение масштабируется, но, как вы это делаете, вы испускаете всех четырех рабочих, а база данных Redis делает это так, как если бы вы звонили ему четыре раза на одного работника. Если клиент фактически подключился ко всем четырем экземплярам вашего сокета, они получат шестнадцать сообщений в секунду, а не четыре.

Тип обработки сокетов зависит от типа приложения, которое вы собираетесь использовать , Если вы собираетесь обрабатывать клиентов по отдельности, тогда у вас не должно быть проблем, потому что событие соединения будет срабатывать только для одного рабочего на одного клиента. Если вам нужно глобальное «сердцебиение», тогда у вас может быть обработчик сокета в вашем основном процессе. Поскольку работники умирают, когда мастер-процесс умирает, вы должны компенсировать нагрузку на соединение основного процесса и позволить дочерним элементам обрабатывать соединения. Вот пример:

var cluster = require('cluster');
var os = require('os');

if (cluster.isMaster) {
  // we create a HTTP server, but we do not use listen
  // that way, we have a socket.io server that doesn't accept connections
  var server = require('http').createServer();
  var io = require('socket.io').listen(server);

  var RedisStore = require('socket.io/lib/stores/redis');
  var redis = require('socket.io/node_modules/redis');

  io.set('store', new RedisStore({
    redisPub: redis.createClient(),
    redisSub: redis.createClient(),
    redisClient: redis.createClient()
  }));

  setInterval(function() {
    // all workers will receive this in Redis, and emit
    io.sockets.emit('data', 'payload');
  }, 1000);

  for (var i = 0; i < os.cpus().length; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  }); 
}

if (cluster.isWorker) {
  var express = require('express');
  var app = express();

  var http = require('http');
  var server = http.createServer(app);
  var io = require('socket.io').listen(server);

  var RedisStore = require('socket.io/lib/stores/redis');
  var redis = require('socket.io/node_modules/redis');

  io.set('store', new RedisStore({
    redisPub: redis.createClient(),
    redisSub: redis.createClient(),
    redisClient: redis.createClient()
  }));

  io.sockets.on('connection', function(socket) {
    socket.emit('data', 'connected to worker: ' + cluster.worker.id);
  });

  app.listen(80);
}

В примере есть пять экземпляров Socket.IO, один из которых является мастером, а четыре - дочерними. Главный сервер никогда не вызывает listen(), поэтому в этом процессе нет никаких дополнительных затрат на соединение. Однако, если вы вызываете emit в основном процессе, он будет опубликован в Redis, и четыре рабочих процесса будут выполнять emit на своих клиентах. Это компенсирует нагрузку на соединение для рабочих, и если работник должен умереть, ваша основная логика приложения будет не затронута в главном.

Обратите внимание, что при использовании Redis все испускает даже в пространстве имен или в комнате другими рабочими процессами, как если бы вы инициировали испускание этого процесса. Другими словами, если у вас есть два экземпляра Socket.IO с одним экземпляром Redis, вызов emit() в сокете первого работника отправит данные своим клиентам, а рабочий два будет делать так же, как если бы вы вызвали излучение из этот работник.

18
задан dota2pro 4 October 2019 в 16:29
поделиться