我正在尝试通过从保存所有数据的常规数据结构切换到使用redis和群集来使我的socket.io应用程序扩展。但是,我遇到了一些问题,因为在当前实现的某些时候,我将套接字对象与其他属性一起存储在此数据结构data[socket.id].socket = socket
中,因为在我的应用程序中,有时我需要执行data[someId].socket.disconnect()
手动断开插座。
我知道我无法将对象直接存储到redis中,因此我尝试使用JSON.stringify(socket)
但没有成功,因为socket
是循环的。是否有其他方法仅使用id
断开套接字?这样我可以像id
那样存储data[socket.id].id = socket.id
,也可以像data[someId].id.disconnect()
之类的那样调用它。所以基本上我正在寻找一种方法来断开套接字而无需访问实际的套接字对象(我有权访问io
对象)。
谢谢大家的帮助。
答案 0 :(得分:2)
看起来这已经完成但没有在任何地方记录...... io.sockets.clients(someId)
获取套接字对象而不管它被调用的实例,所以唯一需要的是使用io.sockets.clients(someId).disconnect()
和实际上将断开客户端,无论它连接到哪个实例。我把它们存放在我自己的阵列上而不需要。
答案 1 :(得分:0)
我做类似的事情:
var connTable = {};
function onConnection(socket) {
connTable[socket.id] = socket;
socket.on("close", function(data, callback) {
socket.disconnect();
onDisconnect(socket.id);
});
socket.on('disconnect', function(){
onDisconnect(socket.id);
});
}
function manuallyDisconnect(socket_id) {
connTable[socket_id].disconnect();
}
function onDisconnect() {
//closing process, or something....
}
答案 2 :(得分:0)
在nodejs,nginx和pm2之前使用socket.io-redis。
NGINX.conf
server {
server_name www.yoursite.io;
listen 443 ssl http2;
listen [::]:443 ssl http2;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy false;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://[::1]:4000;
}
}
PM2 运行集群模式,四个实例...
pm2 start app.js -i 4
app.js
console.clear()
require('dotenv').config()
const express = require('express'),
app = express(),
Redis = require('ioredis')
if(process.env.debug === 'true')
app.use(require('morgan')(':method :url :status :res[content-length] - :response-time ms'))
app.locals = Object.assign(app.locals, {
sock_transports: ['websocket', 'xhr-polling'],
sock_timeout: process.env.sock_timeout,
title: process.env.title,
meta_desc: process.env.meta_desc,
app_url: ['https://', process.env.app_subdomain, '.', process.env.app_domain].join('')
})
app.set('functions', require('./lib/functions')(app))
app.set('view engine', 'hbs')
app.set('view cache', false)
app.engine('hbs', require('./lib/hbs')(require('express-handlebars')).engine)
app.use(express.json({
type: [
'json'
]
}), express.urlencoded({
extended: true
}))
const redis = new Redis({
path: process.env.redis_socket,
db: 1,
enableReadyCheck: true
})
console.time('Redis')
redis.on('ready', () => {
console.timeEnd('Redis')
app.set('redis', redis)
})
redis.on('error', err => {
console.log('Redis: ' + app.get('colors').redBright(err))
exitHandler()
})
function loadSessionMiddleware() {
const session = require('express-session'),
RedisSession = require('connect-redis')(session),
client = new Redis({
path: process.env.redis_socket,
db: 5
}),
ua = require('useragent')
ua(true)
app.set('useragent', ua)
app.set('session_vars', {
secret: process.env.session_secret,
name: process.env.session_name,
store: new RedisSession({
client
}),
rolling: true,
saveUninitialized: true,
unset: 'destroy',
resave: true,
proxy: true,
logErrors: process.env.debug === 'true',
cookie: {
path: '/',
domain: '.' + process.env.app_domain,
secure: true,
sameSite: true,
httpOnly: true,
expires: false,
maxAge: 60000 * process.env.session_exp_mins,
}
})
app.set('session', session(app.get('session_vars')))
app.use(
app.get('session'),
require('./middleware')(app)
)
loadControllers()
}
function loadControllers() {
require('fs').readdirSync('./controllers').filter(file => {
return file.slice(-3) === '.js'
}).forEach(file => {
require('./controllers/' + file)(app)
})
app.get('*', (req, res) => {
app.get('functions').show404(req, res)
})
initServer()
}
function initServer() {
console.time('Server')
const server = require('http').createServer(app)
server.on('error', err => {
console.err('express err: ' + err)
app.get('functions').stringify(err)
})
server.listen(process.env.app_port)
app.set('server', server)
require('./websocket').listen(app, websocket => {
console.timeEnd('Server')
app.set('websocket', websocket)
// www-data
process.setuid(process.env.app_uid)
})
}
console.time('Database')
require('./model').load(app, db => {
console.timeEnd('Database')
app.set('model', db)
loadSessionMiddleware()
})
function exitHandler() {
if(app.get('server'))
app.get('server').close()
if(app.get('redis'))
app.get('redis').quit()
if(app.get('mail'))
app.get('mail').close()
process.exit(0)
}
process.on('SIGINT SIGUSR1 SIGUSR2', () => {
exitHandler()
})
process.stdin.resume()
websocket.js
var exports = {}
exports.listen = (app, cb) => {
const websocket = require('socket.io')(app.get('server'), {
transports: process.env.transports
}),
req = {}
websocket.setMaxListeners(0)
websocket.adapter(require('socket.io-redis')({
path: process.env.redis_socket,
key: 'socket_io',
db: 2,
enableOfflineQueue: true
}))
websocket.use((socket, next) => {
app.get('session')(socket.request, socket.request.res || {}, next)
})
websocket.isAccountLocked = cb => {
if(!req.session.user_id) {
cb(false)
return
}
if(isNaN(req.session.user_id)) {
cb(false)
return
}
app.get('model').users.get(req.session.user_id, user_rec => {
if(!user_rec) {
cb(false)
return
}
if(user_rec.account_locked === 'yes') {
websocket.showClient(client => {
app.get('model').users.logout(req.session, () => {
console.log(client + ' ' + app.get('colors').redBright('Account Locked'))
cb(true)
})
})
return
}
cb(false)
})
}
websocket.showClient = cb => {
var outp = []
if(!req.session.user_id && !req.session.full_name)
outp.push(req.session.ip)
if(req.session.user_id) {
outp.push('# ' + req.session.user_id)
if(req.session.full_name)
outp.push(' - ' + req.session.full_name)
}
cb(app.get('colors').black.bgWhite(outp.join('')))
}
websocket.on('connection', socket => {
if(!socket.request.session)
return
req.session = socket.request.session
socket.use((packet, next) => {
websocket.isAccountLocked(locked => {
if(locked)
return
var save_sess = false
if(typeof(socket.handshake.headers['x-real-ip']) !== 'undefined') {
if(socket.handshake.headers['x-real-ip'] !== req.session.ip) {
req.session.ip = socket.handshake.headers['x-real-ip']
save_sess = true
}
}
var ua = app.get('useragent').parse(socket.handshake.headers['user-agent']).toString()
if(ua !== req.session.useragent) {
req.session.useragent = ua
save_sess = true
}
websocket.of('/').adapter.remoteJoin(socket.id, req.session.id, err => {
delete socket.rooms[socket.id]
if(!save_sess) {
next()
return
}
req.session.save(() => {
next()
})
})
})
})
socket.on('disconnecting', () => {
websocket.of('/').adapter.remoteDisconnect(req.session.id, true, err => {
})
})
socket.on('auth', sess_vars => {
function setSess() {
if(sess_vars.path)
req.session.path = sess_vars.path
if(sess_vars.search_query)
req.session.search_query = sess_vars.search_query
if(sess_vars.search_query_long)
req.session.search_query_long = sess_vars.search_query_long
if(sess_vars.dispensary_id)
req.session.dispensary_id = sess_vars.dispensary_id
if(sess_vars.city)
req.session.city = sess_vars.city
if(sess_vars.state)
req.session.state = sess_vars.state
if(sess_vars.zip)
req.session.zip = sess_vars.zip
if(sess_vars.country)
req.session.country = sess_vars.country
if(sess_vars.hash)
req.session.hash = sess_vars.hash
req.session.save(() => {
websocket.to(req.session.id).emit('auth', sess)
app.get('functions').showVisitor({
session: sess
}, {
statusCode: 200
})
})
}
setSess()
})
socket.on('logout', () => {
var sess_ip = req.session.ip,
sess_id = req.session.id,
sess_email = req.session.email
app.get('model').users.logout(req.session, () => {
websocket.showClient(client => {
console.log(client + ' - Logged Out')
})
})
})
})
cb(websocket)
}
module.exports = exports
答案 3 :(得分:0)
从socket.io-redis文档中,您需要使用remoteDisconnect
:
io.of('/').adapter.remoteDisconnect(socketId, true, (err) => {
if (err) {
console.log('remoteDisconnect err:', err);
}
});