HTTP 响应
{"code":1,"message":"会话 ID 未知"}
调试=socket.io:*
socket.io:socket 加入房间 2d1DHsPAtc1Sspm6AAAE +0ms
socket.io: id 为 rwLW7JMBp6E4TyexAAAG +3s 的服务器传入连接
socket.io:client 连接到命名空间/+3s
socket.io:namespace 将套接字添加到 nsp / +3s
socket.io:socket socket connected - 写包+3s
socket.io:socket 加入房间 rwLW7JMBp6E4TyexAAAG +0ms
socket.io:client 写入数据包 {"type":0,"nsp":"/"} +1ms
socket.io:client 写入数据包 {"type":2,"data":["news",{"hello":"world"}],"nsp":"/"} +0ms
socket.io:socket 加入房间 rwLW7JMBp6E4TyexAAAG +1ms
socket.io: id 为 03Izy4YOxaYm5-d-AAAH +1s 的服务器传入连接
socket.io:client 连接到命名空间/+1s
socket.io:namespace 将套接字添加到 nsp / +1s
socket.io:socket socket connected - 写入数据包+1s
socket.io:socket 加入房间 03Izy4YOxaYm5-d-AAAH +0ms
socket.io:client 写入数据包 {"type":0,"nsp":"/"} +1ms
socket.io:client 写入数据包 {"type":2,"data":["news",{"hello":"world"}],"nsp":"/"} +0ms
并且套接字立即断开
感谢您报告@ceram1。 你能用 1.0.2 重复这个问题吗? 如果是这样,那么查看客户端调试日志和/或获取一些示例代码来重现问题会很有趣。 会有很大帮助。 :)
啊..对不起...邮件是垃圾邮件...我会尽快测试它
+1
我们在 2 节点集群(由 socket.io-redis 支持)上的高连接率下注意到了这一点。
嗨,大家好,
套接字.io 1.0.6
socket.io-redis
回复:
{"code":1,"message":"会话 ID 未知"}
在 Heroku 上只有 1 个以上的实例。
一个实例一切正常(我们喜欢它),但有 2 个或更多实例却失败了。 400 错误。
初始化:
io = require('socket.io')(compound.server);
io.adapter(redisSocket({
host: redisHost
, port: redisPort
, pubClient: pub
, subClient: sub
}));
sub = redis.createClient(redisPort, redisHost,{return_buffers:true});
pub = redis.createClient(redisPort, redisHost,{return_buffers:true});
客户端 = redis.createClient(redisPort, redisHost,{return_buffers:true});
如果你愿意,我可以通过 Skype 分享你的屏幕,我们可以通过它。 网上到处都是这个错误
这里有什么新东西??
我现在实际上面临着同样的问题。 在一个实例中一切运行良好,当有多个实例运行时发生 400 错误。
附言。 我的运行版本为 1.0.6。
+1
好的,如果您想运行集群,最简单的方法是继续调用库 socketcluster。
虽然它基于engine.io,但它与socket.io 足够接近
https://github.com/TopCloud/socketcluster
+1,如果我在 ELB 之后设置了多个实例,我会遇到这个问题。
+1
嗯..我不能再犯这个错误了..(我没有它的来源,因为一些测试失败了)
也许环境设置可能是一个原因。
我知道有什么问题。 像池这样的所有请求每次都通过端口调用同一个实例。
但就我而言,它不起作用,因为我在 heroku 上。 而且 heroku 不支持通过端口访问实例。 它总是通过自己的负载均衡器选择实例。
问题在这里:
socket.io/node_modules/engine.io/lib/server.js 在函数“verify”上有变量“clients”,它分别存在于每个实例上。 因此,如果请求来自不同的实例,它会返回 400。
啊..我没有聚集在测试代码中。 也许这就是为什么我不能再成功了
这是解决此问题的另一种解决方案。 但是,从大多数用例来看,这个解决方案并不好...
我想出的最佳解决方案是给每个实例(套接字服务器)一个不同的端口,并将实例的地址与连接到它的客户端数量存储在 redis 中。 然后让客户端在连接或重新连接之前向单独的节点应用程序(套接字管理器)询问套接字服务器地址。 然后套接字管理器选择连接最少的 socket.io 服务器。 它并不漂亮,但它确实有效并且可以缩放。
只是说,有同样的问题。 然后我认为我正在通过 nginx 处理请求。 添加了适当的 nginx 配置,之后一切都很好。
http://socket.io/docs/using-multiple-nodes/
您需要上游服务器定义中的 ip_hash 和一些标头。
@dux对 Heroku 有用吗? 我不认为我们可以更改 nginx 配置。
ups,我不知道这是heroku独有的线程。 不,这是香草 nginx 前端服务器解决方案。
如果有人可以找到 HEROKU 的解决方案,请在此处发布,这将非常有帮助:)
@miklacko经过大量尝试和错误后,我以这个配置结束(请记住,我没有时间测试消息是否在浏览器之间共享。但我可以测试的是我之间有稳定的连接客户端和服务器使用集群 express 4.x 和套接字 1.x。
express.js
var sio_redis = require('socket.io-redis'),
url = require('url'),
http = require('http'),
// other imports...
// Some express set up
// CookieParser should be above session
app.use(cookieParser());
var sessionStore = new mongoStore({
db: db.connection.db,
collection: config.sessionCollection
});
// Express MongoDB session storage
var sessionMiddleware = session({
secret: config.sessionSecret,
store: sessionStore
});
app.use(sessionMiddleware);
// Create Server and Socket.io
var server = http.createServer(app);
var sio = require('socket.io')(server);
if(process.env.REDISCLOUD_URL){
var redisURL = url.parse(process.env.REDISCLOUD_URL);
redisURL.password = redisURL.auth.split(':')[1];
var pub = require('redis').createClient(redisURL.port, redisURL.hostname, {auth_pass: redisURL.password, return_buffers: true});
var sub = require('redis').createClient(redisURL.port, redisURL.hostname, {auth_pass: redisURL.password, return_buffers: true});
sio.adapter(sio_redis({pubClient: pub, subClient: sub}));
} else {
sio.adapter(sio_redis({ host: 'localhost', port: 6379 }));
}
// Authenticate Socket.io using Cookie Auth
sio.use(function(socket, next) {
var handshake = socket.handshake;
if (handshake.headers.cookie) {
var req = {
headers: {
cookie: handshake.headers.cookie
}
};
cookieParser(config.sessionSecret)(req, null, function(err) {
if (err) {
return next(err);
}
var sessionID = req.signedCookies['connect.sid'] || req.cookies['connect.sid'];
sessionStore.get(sessionID, function (err, session) {
if (err) {
return next(err);
}
if (session) {
next();
} else {
return next(new Error('Invalid Session'));
}
});
});
} else {
next(new Error('Missing Cookies'));
}
});
// other express middleware set up
// Handle Connection and disconnection
sio.sockets.on('connection', function(socket) {
console.log('user connected');
socket.on('disconnect', function(){
console.log('user disconnected');
});
});
return server;
server.js(整个文件)
'use strict';
/**
* Module dependencies.
*/
var init = require('./config/init')(),
config = require('./config/config'),
mongoose = require('mongoose'),
cluster = require('cluster'),
_ = require('lodash');
// Bootstrap db connection
var db = mongoose.connect(config.db);
// Init the express application
var server = require('./config/express')(db);
// Bootstrap passport config
require('./config/passport')();
// Start server.
var numCPUs = Math.ceil(require('os').cpus().length / 2);
if (cluster.isMaster) {
var workers = [];
// Helper function for spawning worker at index 'i'.
var spawn = function(i) {
workers[i] = cluster.fork();
console.log('worker ' + workers[i].process.pid + ' created');
// Optional: Restart worker on exit
// workers[i].on('exit', function(worker, code, signal) {
// console.log('respawning worker', i);
// spawn(i);
// });
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
};
// Spawn workers.
for (var i = 0; i < numCPUs; i++) {
spawn(i);
}
} else {
server.listen(config.port, function () {
console.log('server started on ' + config.port + ' port');
});
}
index.html(这里最重要的部分)
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect({transports: ['websocket']});
</script>
我们需要声明只在 Heroku 中使用websocket
协议。 因为 xhr-polling 不起作用! 不知道为什么。
我希望今天我可以测试一下以socket/redis为思想的浏览器之间的通信。 我将分享结果。
@gonzalodiaz非常感谢! 下周我会试试,如果我发现一些错误或改进,我会告诉你。
@gonzalodiaz完美运行。 唯一不好的是旧浏览器不支持 websockets http://caniuse.com/#feat =websockets。
再次感谢你。
@miklacko很高兴知道它对您有用。 它对我也很有效:) - 如果您找到旧浏览器的解决方案,请告诉我。 现在对我来说这不是问题,但如果有备份就好了。
当客户端使用长轮询传输,而服务器使用集群模式时会出现此问题。
使用粘性会话将解决这个问题。
@nauu Heroku 不支持粘性会话。
The Heroku routing infrastructure does not support “sticky sessions”.
Requests from clients will be distributed randomly to all dynos running your application.
@gonzalodiaz Heroku 可以运行集群并在不同的端口监听吗?
如果可以,您可以使用 nginx 反向代理来自客户端的请求。 并使用 nginx 的粘性会话模块。
@nauu不,Heroku 不支持监听不同的端口。 Heroku 在环境变量中设置端口,并在内部分配测功机之间的负载。 这真的很痛苦。
因此,关于 Heroku 上的这个错误,如果您想使用支持所有 socket.io 而不仅仅是 Web 套接字的集群之类的东西,没有解决方案?
谢谢!
您可以通过这种方式在本地模拟 heroku:
1- npm 安装工头 -g
2-回显“网络:节点app.js”> Procfile
3- nf 开始 -x 3000 网络=3
这将在端口 5001、5002 和 5003 上启动您的服务器 (app.js) 的 3 个实例,但您可以通过端口 3000 上的反向代理访问它们。
启动它并尝试建立 websocket 连接,您将看到与 heroku 相同的问题。 node-foreman 是在本地模拟 heroku 环境的好方法。 需要记住的重要一点是,尽管您可以直接访问每个本地实例,但您只能访问 Heorku 上的负载均衡器(上例中的端口 3000)
什么是好的解决方案?
@ceram1你解决问题了吗?
+1
@gonzalodiaz感谢您提及transports: ['websocket']
对于那些在亚马逊 ELB 背后遇到此问题的人,请确保启用应用程序控制的会话粘性 (http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-sticky-sessions.html)。 这为我解决了这个问题。
伙计们阅读这篇文章https://devcenter.heroku.com/articles/node-websockets
最后你会发现使用 Socket.io 的应用程序应该启用会话亲和性
然后继续https://devcenter.heroku.com/articles/session-affinity
然后放入 Heroku Toolbelt CLI heroku features:enable http-session-affinity --app APP
而且我很高兴在缩放的测功机上工作 socket.io。
对我来说,它是使用 nginx ssl http2,它正在轮询,所以好的配置是:
const ioSocket = io('', {
// Send auth token on connection, you will need to DI the Auth service above
// 'query': 'token=' + Auth.getToken()
path: '/socket.io',
transports: ['websocket'],
secure: true,
});
@p3x-robot 我是通过这种方法修复的
最有用的评论
对我来说,它是使用 nginx ssl http2,它正在轮询,所以好的配置是: