Кластеры серверов ввода-вывода, работающие с Redis Pub / Sub

#node.js #redis #socket.io

#node.js #redis #socket.io

Вопрос:

Итак, во-первых, я создал микросервис, который извлекает Football API, и через pub / sub систему redis он публикует любые изменения, если таковые имеются, для livescores.

Теперь мой сервер с сокетами и маршрутами будет находиться в режиме кластера. Я уже настроил это с помощью socketio-redis. Вот фрагмент этой настройки:

 const io = require('socket.io')();
const sRedis = require('socket.io-redis');
const adapter = sRedis({ host: 'localhost', port: 6379 });
const { promisify } = require('util');
const Redis = require('ioredis');
const redis = new Redis();
redis.subscribe('livescore');



io.adapter(adapter);
const ioa = io.of('/').adapter;
ioa.clients = promisify(ioa.clients);
ioa.clientRooms = promisify(ioa.clientRooms);
ioa.remoteJoin = promisify(ioa.remoteJoin);
ioa.remoteLeave = promisify(ioa.remoteLeave);
ioa.allRooms = promisify(ioa.allRooms);

// notice this listener
redis.on('message', (channel, message) => {
    io.emit('livescore', message);
})


io.on('connect', async (socket) => {

    socket.clientRooms = () => ioa.clientRooms(socket.id);
    socket.remoteJoin = (room) => ioa.remoteJoin(socket.id, room);
    socket.remoteLeave = (room) => ioa.remoteLeave(socket.id, room);
    socket.remoteDisconnect = () => ioa.remoteDisconnect(socket.id);


    socket.on('join room', async (id) => {
        await socket.remoteJoin(id);
        socket.emit('join room', `You have joined room ${id}`)
        socket.broadcast.emit('join room', `${socket.id} has joined.`)
    });

    socket.on('leave room', (id) => {
        socket.remoteLeave(id);
    });


})

module.exports = io;
  

Итак, если я запускаю единственный экземпляр этого приложения node, все работает отлично.

Но если я запускаю его в режиме кластера, допустим, есть 4 кластера (я запускаю режим кластера с pm2), произойдет следующее:

  1. Микросервис публикует событие.
  2. У каждого кластера есть подписка на канал «прямая трансляция»
  3. Каждый кластер выполняет io.emit() (для всех клиентов)
  4. Клиент получает 4 одинаковых события практически в одно и то же время.

Я понял, почему клиент получает 4 одинаковых события, но я хочу знать, как правильно с этим справиться?

Моя единственная мысль о решении заключается в том, что я делаю redis sub только в одном кластере и публикую все из него, но я боюсь, что это было бы слишком большой работой для одного кластера?

Есть идеи?

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

1. вы нашли его решение?

2. есть какие-либо обновления по поиску решения?

Ответ №1:

Вероятно, существует несколько решений для исправления этого, вы могли бы, например:

Используйте очередь сообщений вместо pub / sub

В зависимости от количества операций обработки, вы, вероятно, хотите, чтобы сообщение обрабатывал только один узел. Pub / sub — это не то, что вам нужно в этом случае. Вы могли бы, например, сохранить свои сообщения в списке и использовать команду LPOP для получения и удаления сообщения. Тогда вы могли бы сказать, что «первый поймает это» — таким образом, только один из ваших серверов будет выполнять работу, но в основном случайный. Вы также могли бы использовать отдельную очередь сообщений, такую как RabbitMQ, SQS и т.д.

Используйте socket.io-emitter для отправки сообщений

Поскольку вы в любом случае используете socket.io-redis, ваши сообщения будут распределены по вашим узлам. Есть проект, который является частью socket.io-redis, он называется socket.io-emitter. Которые можно использовать для отправки сообщений на все ваши узлы, не являясь таковыми сами по себе. Когда вы реализуете это в своем рабочем микросервисе (том, который в данный момент записывает сообщение в «live»), вы можете отправлять сообщения напрямую своим клиентам. Однако это может не сработать, если вам нужно обрабатывать сообщения в вашем приложении node.