#node.js #redis #socket.io
#node.js #редис #socket.io
Вопрос:
Я пишу Доказательство Концепции (уже не менее 2 месяцев), в которой используется кластер узлов (рабочие), Redis и socket.io.
Разъем.В данном случае io не используется для чата — только для обратной связи. Ajax-это не вариант.
Я использую pub/sub для redis, и эта часть работает (я думаю). По крайней мере, значения, возвращенные из pubClient.get («ключ»), верны.
Когда я делаю запрос с внешнего интерфейса и никак не перемещаюсь или не перезагружаю страницу, все работает идеально — я могу сделать 10 запросов и получить 10 ответов.
И наоборот, когда я перемещаюсь, то же самое неверно — и мне нужно предоставлять результаты независимо от того, сколько кто-то перемещается по интерфейсу.
Похоже, что после перезагрузки происходит отключение. Как в инструментах разработки консолей, так и в узле js идентификаторы сокетов одинаковы. Я действительно ломаю голову над этим вопросом!
Есть там какая-нибудь помощь?
Итак, для некоторых в основном сокет.код ввода-вывода:
клиент:
socket = io('https://' location.hostname ':4444/', { transports: ['websocket', 'polling'], secure: true, }); socket.on('download', function(data){// after reload, this never hits console.log('DOWNLOAD ' data.download); }); var pkgs = ['y14Vfk617n6j', 'My77gWYmBLxT', 'IYd6dL9UoXkx']; if(pkgs.length gt; 0){ for(var i = 0; i lt; pkgs.length; i ){ socket.emit('get-request', pkgs[i]); } }
сервер:
var cluster = require('cluster'); var express = require('express'); var numCPUs = require('os').cpus().length; const { setupMaster, setupWorker } = require("@socket.io/sticky"); const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter"); var app = express(); const https = require('https'); const { Server } = require("socket.io"); const Redis = require("ioredis"); const sock_nodes = [ {port: 6379, host: '192.168.0.41'}, {port: 6380, host: '192.168.0.34'}, {port: 6381, host: '192.168.0.35'}, {port: 6379, host: '192.168.0.34'}, {port: 6380, host: '192.168.0.35'}, {port: 6381, host: '192.168.0.41'} ]; const port = 4444; const httpServer = https.createServer(options, app); const io = new Server(httpServer, {maxHttpBufferSize: 10240000}); const pubClient = new Redis.Cluster(sock_nodes, { redisOptions: { password: 'my secret!' } }); const subClient = pubClient.duplicate(); // I am not actually using this - should I be? if (cluster.isMaster) { for (var i = 0; i lt; numCPUs; i ) { // Create a worker cluster.fork(); } cluster.on("exit", (worker) =gt; { console.log(`Worker PID ${worker.process.pid} died`); var w = cluster.fork(); console.log('WORKER %d died (%s). restarting...', worker.process.pid, worker.state); w.on('message', function(msg){ console.log("Message Received : " , msg); }); }); } else { app.use((req, res, next) =gt; { var reqip = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress; //~ console.log(reqip, md5(reqip)); var sess = parseCookies(req, 'session_state'); if(!sess){ res.cookie('session_state', md5(reqip)); } next(); }); app.get('/', (req, res) =gt; { getSession(req, res, function(sess){ getPub('currSockets', sess, function(err, socket){ res.render("pages/shared/index", {'ns': sess, 'socket': socket}); }); }); }); }); app.get('/start', function(req, res){ getSession(req, res, function(sess){ getPub('currSockets', sess, function(err, socket){ res.render("pages/shared/start", {'ns': sess, 'socket': socket}); }); }); }); io.on('connection', function (socket) { var currUser = parseCookies(socket.request, 'session_state'); socket.join(currUser); getPub('currSockets', currUser, function(err, currSockets){ if (currSockets) { currSockets = JSON.parse(currSockets); if (currSockets[currUser]) { if (currSockets[currUser].stream) { currSockets[currUser].sock = socket.id; setCurrSockets(currSockets, currUser, null, function(cSocks){ }); } } } }); socket.on('get-request', function(data){ // can be one or many requests // there is a similar, currently irrelevant, socket.on('new-request') that is left out here if(data){ getPub('currSockets', currUser, function(err, currSockets){ currSockets = JSON.parse(currSockets); if(currSockets){ if(currUser){ if(currSockets[currUser]){ if(currSockets[currUser].stream){ var str = Object.keys(currSockets[currUser].stream); for(var i = 0; i lt; str.length; i ){ if(str[i] !== 'sock'){ if(!currSockets[currUser].stream[str[i]]){ delete currSockets[currUser].stream[str[i]]; setCurrSockets(currSockets, currUser, null, function(cSocks){ checkCurrSockets(currUser, data, socket); }); } } } } } } } }); } }); }); httpServer.listen(port, () =gt; { logs(__line__, `Worker ${process.pid} listening on ${port}`); }); } function existsPub(key, cb){ return pubClient.exists(key, cb); } function setPub(key, val, cb){ if(val === JSON.stringify({})){ return pubClient.get(key, cb); } return pubClient.set(key, val, cb); } function getPub(key, currUser, cb){ existsPub(key, function(err, reply){ if(reply === 1){ return pubClient.get(key, cb);// always getting an old socket.id } }); } // Here is the piece that doesn't work after reloading the page function ioEmit (currSock, target, payload) { io.to(currSock).emit(target, payload); // doesn't work after page reload } // end piece where after reload does not work getPub('currSockets', currUser, function(err, currSockets){ if( currSockets){ currSockets = JSON.parse(currSockets); ioEmit(currUser, 'download', {'download': currSockets[currUser].stream[data]); } }); function parseCookies (req, name) { var list = {}, rc; rc amp;amp; rc.split(';').forEach(function( cookie ) { var parts = cookie.split('='); list[parts.shift().trim()] = decodeURI(parts.join('=')); }); return list[name]; } function getSession(req, res, callback) { var sess = false; if(req.headers) {// handle req var reqip = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress; if(req.headers.cookie){ sess = req.headers.cookie.split('=')[1].split(';')[0]; } else { res.cookie('session_state', md5(reqip)); } return callback(sess); } else if(req.request) {// handle socket //~ console.log('req.request.headers.cookie', req.request.headers.cookie.split('=')[1]); if(req.request.headers.cookie){ sess = req.request.headers.cookie.split('=')[1].split(';')[0]; //~ req.emit('join', sess); //~ callback({[sess]: {'sock': req.id}}); callback(req.id); } } else { return callback(null); } } function setCurrSockets(currSockets, currUser, data, cb){ if(Object.keys(currSockets[currUser].stream).length gt; 0){ if(data){ if(ready(currSockets, currUser, data)){ delete currSockets[currUser].stream[data];// it appears that setCurrSockets is getting called too soon } } setPub('currSockets', JSON.stringify(currSockets), function(err){ }); if(typeof cb === 'function'){ setTimeout(() =gt; { getPub('currSockets', currUser, function(err, cSocks){ cb(cSocks);// updated callback to return cSocks }, 2000); }); } } else { currSockets[currUser].stream = {}; setPub('currSockets', JSON.stringify(currSockets), function(err){ if(err){ } else { if(typeof cb === 'function'){ cb(currSockets);// updated callback to return cSocks } } }); } }
Комментарии:
1. Ты не можешь. Когда будет загружена новая страница, клиент запросит НОВЫЙ сокет. подключение ввода — вывода и тому правильно будет присвоено новое
socket.id
. Если вы хотите отслеживать одного и того же клиента с одной страницы на другую, для этого вам следует использовать файл cookie или экспресс-сеанс, а затем вы можете присоединить новыйsocket.id
к этому сеансу, когда клиент подключится на новой странице.2. Спасибо. Я думал, что использую печенье? Похоже, я упустил какой-то необходимый код. Обновление сейчас — добавлена сессия getSession
3. @jfriend00 — добавил getSession в код выше — я опустил его по какой-то глупой причине!
4. Что это такое
sess = req.headers.cookie.split('=')[1].split(';')[0];
? Похоже, вы пытаетесь получить доступ к файлу cookie по позиции в разделенном массиве, даже не глядя на его имя. Это нехорошо. К вашему сведению, существует готовое промежуточное программное обеспечение для анализа файлов cookie, которое полностью протестировано для доступа к определенному файлу cookie без его самостоятельного анализа вручную.5. ну, это POC, и я не знаю об этом промежуточном программном обеспечении, и есть только один файл cookie, но я ценю ваш вклад @jfriend00
Ответ №1:
понял это. Проблема была здесь:
for(var i = 0; i lt; str.length; i ){ if(str[i] !== 'sock'){ gt;gt;gt;gt; if(!currSockets[currUser].stream[str[i]]){ // never true // delete currSockets[currUser].stream[str[i]]; setCurrSockets(currSockets, currUser, null, function(cSocks){ checkCurrSockets(currUser, data, socket); }); } } }
поэтому я прокомментировал цикл for и сохранил часть setCurrSockets, и это работает. Просто подумал, что я хотел бы поделиться, на случай, если кто-то еще попытается использовать redis, кластер узлов и сокет.ио вместе. Как сказал @jfreind00, вы должны использовать систему аутентификации со случайной строкой для хранения файлов cookie.
Комментарии:
1. Еще раз спасибо @jfriend00 — ваши комментарии привели меня к решению!