Приложение стартует в кластерном режиме, на каждый воркер устанавливаем соединение с сокетом, юзаем редис адаптер:
app.set('port', httpPort);
let server = http.createServer(app);
let io = require('./socketServer')(server);
io.adapter(redis({host: host, port: port}));
app.set('io', io);
тут мы подключаем основной файл socket.io (socketServer), где проходит авторизация сокета и событие on.connection, где в переменную socketID мы получаем id сессии и запоминаем в массив io.clients текущего клиента.
io.sockets.on('connection', (socket) =>{
var socketID = socket.handshake.user.sid;
io.clients[socketID] = socket;
io.clients[socketID].broadcast.emit('loggedIn',socket.handshake.user.data);
socket.on('disconnect', () =>{
delete io.clients[socketID];
});
});
Перед всем этим стоит nginx с настроенным upstream чтобы организовать "липкие сессии" (как здесь:
socket.io/docs/using-multiple-nodes/#nginx-configu...
Далее, когда хотим отправить сообщение конкретному клиенту, уже из контроллера мы по id юзера узнаем его session-id (мы заранее при авторизации храним эти соответствия в redis), и потом так отправляем сообщение:
this.redis.getByMask(`sid_clients:*`,(err,rdbData) =>{
Async.each(clients,(client,next)=>{
let sid = `sid_clients:${client}`;
let currentClient = rdbData[sid];
if(!currentClient || !this.io.clients[currentClient]) return next();
this.io.clients[currentClient].emit(event,data);
return next();
});
Все здорово работает когда мы запускаем приложение в одном процессе, но при запуске в кластере сообщение при коннекте "loggedIn" получают все клиенты на всех процессах, а вот если с конкретного процесса отправить сообщение клиенту который законнектился на другом процессе не получается, т.к. у каждого процесса свой массив io.clients и они всегда отличаются содержимым, поэтому сообщение не доходит до нужного клиента.
Так вот, как правильно реализовать отправку клиенту в кластерном режиме? Как хранить все приконнекченные сокеты в одном месте (может в редисе) чтобы избежать таких ситуаций как у меня? Я думаю что я зря это намутил и я не пойму сути работы io.adapter(redis(...)).
Пока в голову приходит только мысль, когда из конкретного воркера мы хотим сделать `emit` клиенту чей сокет находится на другом воркере, то сначала мы посылаем сообщение в масер процесс (со всеми данными что хотим отослать), в мастере ищем есть ли нужный клиент и на каком он воркере, например через redis мы находим это соответствие, и уже тогда шлем данные из мастера в нужный воркер в котором в `proccess.on` ловим это сообщение из мастера и шлем нужному клиенту.
Но, что тогда будет если приложение будет запущено в кластере на нескольких физических машинах (серверах), всё опять сломается. В целом мне не нравится эта идея, все же мне необходимы советы спецов!