Сокет.io не работает при использовании нескольких узлов через pm2

#node.js #socket.io #socket.io-redis #vue-socket.io

Вопрос:

У меня запущено приложение NodeJS с сокетом.ввод-вывод для обновлений в реальном времени. Я использую PM2 Process manager для производства Node.js применение. Теперь я хочу использовать кластерный режим в PM2. Поскольку приложения nodejs выполняются в одном процессе, я хочу использовать максимальный процессор, доступный в моей серверной системе. В настоящее время в моей системе 4 ядра. Таким образом, в режиме кластера PM2 я могу использовать все ядра, а PM2 будет обрабатывать все самостоятельно за сценой.

При использовании экземпляра одного сервера мое приложение nodejs с сокетом.io отлично работает с клиентом. Но когда я использую кластерный режим, PM2 запускает 4 экземпляра сервера. Я попытался подключить несколько клиентов (открыв серверные терминалы и запустив на них часть на стороне клиента), и они успешно подключаются к экземплярам, запущенным PM2 случайным образом, что ожидаемо и нормально.

Что я хочу сделать?

  1. Запустите pm2 в режиме кластера
  2. клиенты могут подключаться к любому запущенному экземпляру через pm2
  3. после подключения клиента Server-1 будет выдавать события, которые должны быть отправлены всем клиентам, подключенным ко всем серверам
  4. После успешного тестирования вышеуказанных шагов я интегрирую логику room, но поскольку я не добился успеха на шаге 3, я не собираюсь использовать setp 4.

Моя проблема в том, что когда я хочу передать событие с сервера-1 клиентам, оно должно быть отправлено всем клиентам, подключенным ко всем 4 экземплярам сервера (экземпляры server-1, server-2, server-3, server-4, запущенные PM2).

Но всякий раз, когда я отправляю событие с сервера-1, оно отправляется только клиентам, подключенным только к серверу-1.

и я получаю сообщение об ошибке в журналах сервера,

 You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection:

Error: The client is closed
    at Commander._RedisClient_sendCommand (/var/www/html/test/server-socket/node_modules/@node-redis/client/dist/lib/client/index.js:387:31)
    at Commander.commandsExecutor (/var/www/html/test/server-socket/node_modules/@node-redis/client/dist/lib/client/index.js:160:154)
    at Commander.BaseClass.<computed> [as publish] (/var/www/html/test/server-socket/node_modules/@node-redis/client/dist/lib/commander.js:8:29)
    at RedisAdapter.broadcast (/var/www/html/test/server-socket/node_modules/@socket.io/redis-adapter/dist/index.js:374:28)
    at BroadcastOperator.emit (/var/www/html/test/server-socket/node_modules/socket.io/dist/broadcast-operator.js:109:22)
    at Namespace.emit (/var/www/html/test/server-socket/node_modules/socket.io/dist/namespace.js:170:73)
    at Server.<computed> [as emit] (/var/www/html/test/server-socket/node_modules/socket.io/dist/index.js:576:33)
    at Timeout._onTimeout (/var/www/html/test/server-socket/index.js:25:16)
    at listOnTimeout (internal/timers.js:549:17)
    at processTimers (internal/timers.js:492:7)
 

index.js (На стороне сервера)

 const { Server } = require("socket.io")
const { createAdapter } = require("@socket.io/redis-adapter");
const { createClient } = require("redis");

const io = new Server({ transports: ['websocket'] })

const pubClient = createClient({ host: 'localhost', port: 6379, auth_pass: "root" });
const subClient = pubClient.duplicate();
subClient.psubscribe = pubClient.pSubscribe

io.adapter(createAdapter(pubClient, subClient))
io.listen(3000)

// console.log('process.env.NODE_APP_INSTANCE: '   process.env.NODE_APP_INSTANCE);

io.on("connection", (socket) => {
    console.log(socket.id   ' connected on server: '   process.env.NODE_APP_INSTANCE)

    // emitting event from only server-1 to test whether all client gets it or not
    if(process.env.NODE_APP_INSTANCE == 0){
        setInterval(() => {
            io.emit('test_msg', 'server: '   process.env.NODE_APP_INSTANCE)
        }, 5000)
    }
});
 

index.js (At client side)

 const { io } = require("socket.io-client");
const socket = io("http://localhost:3000", {
    transports: ["websocket"]
});

socket.on("connect", () => {
    console.log(socket.id   ' connected with server')
})

socket.on("test_msg", (data) => {
    console.log('test_msg caught: '   data)
})
 

ecosystem.config.js файл (на стороне сервера для pm2)

 module.exports = {
  apps : [{
    name: 'server-socket',
    script: 'index.js',
    watch: '.',
    // instances  : "max",
    instances  : 4,
    exec_mode  : "cluster",
    increment_var : 'PORT',
    env_development: {
      PORT: 3000,
      NODE_ENV: "development"
    },
    env_production: {
      NODE_ENV: "production"
    },
  }]
};
 

Пакеты, которые я использовал:

 "@socket.io/redis-adapter": "^7.0.1",
"nodemon": "^2.0.15",
"redis": "^4.0.0",
"socket.io": "^4.4.0" 
"socket.io-client": "^4.4.0" // this package used at client side

NodeJs version: v12.16.1  
NPM version: v6.13.4  
PM2 version: v5.1.2
 

Я следую документации (сокет.ввод-вывод) отсюда

Я не знаю, что означает эта ошибка, но я потратил значительное количество времени на многие вещи, но не нашел ничего полезного.

  • Может ли кто-нибудь подсказать мне пример кода или решения?

Ответ №1:

Я думаю, это потому, что клиенты redis @ 4 должны быть подключены вручную в первую очередь:

  Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
   io.adapter(createAdapter(pubClient, subClient));
   io.listen(3000);
 });
 

@socket.io/redis-adapter ПРОЧИТАЙ МЕНЯ

Комментарии:

1. Спасибо за помощь, похоже, что они обновили свою документацию только 2 недели назад. Теперь он подключается, как и ожидалось.