Node.js http-proxy会丢弃websocket请求

好吧,我已经花了一个多星期的时间来试图解决这个问题,所以如果有人有线索,你是一个英雄。 这不是一个容易回答的问题,除非我是一个笨蛋。

我正在使用node-http-proxy将粘性会话代理到运行在不同端口上的16个node.js worker。

我使用Socket.IO的Web套接字来处理一堆不同types的请求,并使用传统的请求。

当我通过node-http-proxy将服务器切换到代理服务器时,有时出现了一个新问题,我的Socket.IO会话无法build立连接。

我从字面上不能稳定地重现它的生活,唯一的方法就是把它从多个客户端扔到服务器很多的stream量。

如果我重新加载用户的浏览器,它有时可能会重新连接,有时甚至不能。

粘滞的会话

我必须代理粘性会话,因为我的应用程序在每个工作人员的基础上进行身份validation,所以它根据Connect.SID cookie(我正在使用connect / express)路由请求。

好的,一些代码

这是我的proxy.js文件,它运行在节点中并路由到每个worker:

var http = require('http'); var httpProxy = require('http-proxy'); // What ports the proxy is routing to. var data = { proxyPort: 8888, currentPort: 8850, portStart: 8850, portEnd: 8865, }; // Just gives the next port number. nextPort = function() { var next = data.currentPort++; next = (next > data.portEnd) ? data.portStart : next; data.currentPort = next; return data.currentPort; }; // A hash of Connect.SIDs for sticky sessions. data.routes = {} var svr = httpProxy.createServer(function (req, res, proxy) { var port = false; // parseCookies is just a little function // that... parses cookies. var cookies = parseCookies(req); // If there is an SID passed from the browser. if (cookies['connect.sid'] !== undefined) { var ip = req.connection.remoteAddress; if (data.routes[cookies['connect.sid']] !== undefined) { // If there is already a route assigned to this SID, // make that route's port the assigned port. port = data.routes[cookies['connect.sid']].port; } else { // If there isn't a route for this SID, // create the route object and log its // assigned port. port = data.currentPort; data.routes[cookies['connect.sid']] = { port: port, } nextPort(); } } else { // Otherwise assign a random port, it will/ // pick up a connect SID on the next go. // This doesn't really happen. port = nextPort(); } // Now that we have the chosen port, // proxy the request. proxy.proxyRequest(req, res, { host: '127.0.0.1', port: port }); }).listen(data.proxyPort); // Now we handle WebSocket requests. // Basically, I feed off of the above route // logic and try to route my WebSocket to the // same server regular requests are going to. svr.on('upgrade', function (req, socket, head) { var cookies = parseCookies(req); var port = false; // Make sure there is a Connect.SID, if (cookies['connect.sid'] != undefined) { // Make sure there is a route... if (data.routes[cookies['connect.sid']] !== undefined) { // Assign the appropriate port. port = data.routes[cookies['connect.sid']].port; } else { // this has never, ever happened, i've been logging it. } } else { // this has never, ever happened, i've been logging it. }; if (port === false) { // this has never happened... }; // So now route the WebSocket to the same port // as the regular requests are getting. svr.proxy.proxyWebSocketRequest(req, socket, head, { host: 'localhost', port: port }); }); 

客户端/现象

套接字如此连接:

 var socket = io.connect('http://whatever:8888'); 

login后大约10秒后,我在这个监听器上发现了这个错误,这并没有什么帮助。

 socket.on('error', function (data) { // this is what gets triggered. -> // Firefox can't establish a connection to the server at ws://whatever:8888/socket.io/1/websocket/Nnx08nYaZkLY2N479KX0. }); 

浏览器发送的Socket.IO GET请求永远不会回来 – 即使在错误恢复后,它也会挂起,因此看起来像是超时错误。 服务器从不回应。

服务器端 – 一名工人

这是一个工作人员如何收到套接字请求。 很简单。 所有工作人员都有相同的代码,所以你认为他们中的一个会得到请求,并承认它…

 app.sio.socketio.sockets.on('connection', function (socket) { // works... some of the time! all of my workers run this // exact same process. }); 

概要

这是很多的数据,我怀疑有人愿意面对它,但我完全难住,不知道在哪里检查,然后login,无论如何,解决它。 我已经试过我所知道的一切,看看问题是什么,无济于事。

UPDATE

好的,我相当确定问题是在node-http-proxy github主页上的这个声明中:

node-http-proxy与<= 0.8.x兼容,如果你正在寻找一个> = 0.10的兼容版本,请检查caronte

我正在运行Node.js v0.10.13,这种现象正如一些在这个问题上的github问题中评论:它只是随机丢弃websocket连接。

我尝试过实现caronte,这个'newer'fork,但是它没有被logging下来,而且我尽我最大的努力把他们的文档拼凑在一个可行的解决scheme中,但是我无法得到它转发websockets,我的Socket。 IO降级到投票。

是否有任何其他想法如何得到这个实施和工作? node-http-proxy昨天有8200个下载! 当然有人使用今年的Node构build和代理websockets ….

我正在寻找什么

我想完成一个代理服务器(最好是Node),它代理多个node.js worker,并通过基于浏览器cookie的粘性会话来路由请求。 这个代理将需要稳定地支持传统的请求以及networking套接字。

要么…

我不介意通过集群节点工作人员完成上述工作。 我唯一真正的要求是在请求头中保持基于cookie的粘性会话。

如果还有更好的办法来完成上述的事情,那么我就是为了这个。

一般来说,我不认为节点不是最常用的代理服务器选项,我使用nginx作为节点的前端服务器,这是一个非常好的组合。 以下是一些安装和使用nginx粘性会话模块的说明。

这是一个轻量级的前端服务器与JSONconfiguration,坚实和非常良好的testing。

如果你想提供静态页面,css,nginx也快很多。 这是理想的configuration您的caching标题,根据域redirectstream量到多个服务器,粘滞会话,压缩CSS和JavaScript等

你也可以考虑像HAProxy这样的纯粹的负载均衡开源解决scheme。 在任何情况下,我都不相信node是最好的工具,但最好使用它来实现后端,并在它之前放置像nginx这样的东西来处理通常的前端服务器任务。

我同意hexacyanide。 对我来说,通过诸如redis或某种消息查询系统之类的服务排队工人是最有意义的。 工作人员将通过Web节点(通过代理)通过Redis Pub / Subfunction进行排队。 工作人员会在发生错误时callback,结束或通过“数据”事件实时传输数据。 也许去图书馆kue吧 。 你也可以推出你自己的类似库。 RabbitMQ是另一个用于类似目的的系统。

如果您已经使用了该技术,那么我使用socket.io,但是您需要使用工具来达到预期的目的。 Redis或MQ系统最有意义,并且与websockets(socket.io)配对以创build实时,有洞察力的应用程序。

Session Affinity(粘性会话)通过Elastic LoadBalancer for aws支持,它支持webSockets。 PaaS提供商( Modulus )正是这样做的。 Theres也satalite提供粘性会话的节点http代理,但我不知道它是否支持webSockets。

我一直在寻找与此非常相似的东西,目的是即时生成(并销毁)Node.js群集节点。

免责声明:我仍然不build议这样做与节点; nginx对于您正在寻找的devise架构更加稳定,或者更加如此,HAProxy(非常成熟,并且轻松支持粘性会话代理)。 正如@tsturzl所指出的那样,有satellite ,但是由于下载量低,我会小心翼翼(至less在生产环境中)。

也就是说,因为你似乎已经拥有了Node的所有东西,所以重build和重新构build可能比它更值得。 因此,要安装NPM的caronte分支:

  1. 使用npm uninstall node-proxy和/或sudo npm -d uninstall node-proxy npm uninstall node-proxy ,删除以前的http-node-proxy主安装

  2. 下载caronte分支.zip并提取它。

  3. 运行npm -g install /path/to/node-http-proxy-caronte
  4. 在我的情况下,安装链接被打破,所以我不得不运行sudo npm link http-proxy

我已经用它们的基本代理例子来运行它 – 无论这个问题是否解决了你的丢失会话问题,只有你会知道。