在配置了TCP的亚马逊弹性负载均衡器上使用Socket.io Websockets

16
我计划在 EC2 上配置一组运行 Socket.io 的 NodeJS 应用程序服务器,并希望使用 Elastic Load Balancer 在它们之间分配负载。 我知道 ELB 默认不支持Websockets,但我可以使用此处的第二种情况描述中所述的设置。

博客文章中所述,这种设置没有会话亲和性或源IP信息:

我们无法通过此设置实现会话亲和性或 X-Forward 标头, 因为 ELB 不解析 HTTP 消息,因此无法匹配 cookie 以确保会话亲和性或注入特殊的 X-Forward 标头。

在这些情况下,Socket.io 仍能工作吗?还是有另一种方法可以让一组支持SSL的Socket.io应用程序服务器位于负载均衡器后面?

编辑: Tim Caswell 在这里 谈到了如何进行这个操作。有没有文章解释如何设置它?虽然这里没有会话粘性,但事情似乎运行得很好。

顺便问一下,在使用websockets时,是否真的需要粘性会话?信息是作为新的、独立的请求传输还是只有一个请求+连接,所有信息都沿着这个连接移动?


简短回答:不,如果后续请求将前往不同的服务器,socket.io 将不起作用,您需要找到一种实现“粘性”会话的方法来设置它。 - Dmitry
@Dmitry:你确定吗?如果你使用共享存储,比如RedisStore,我认为socket.io应该可以工作。 - Linus Thiel
@LinusGThiel 如果没有启用粘性会话,它将无法正常工作。这里有更多细节:https://groups.google.com/d/topic/socket_io/d9a8c49uymc/discussion - Dmitry
谢谢,@Dmitry。我似乎认出了这个问题。 - Linus Thiel
如果您需要源IP地址,请参见此处:https://dev59.com/rGMl5IYBdhLWcg3w966L。 - josh3736
4个回答

15

即使使用TCP ELB,Socket.io也无法直接工作,因为它在升级到WebSockets之前会发出两个HTTP请求。

第一个连接用于建立协议,因为socket.io支持的不仅仅是WebSockets。

GET /socket.io/1/?t=1360136617252 HTTP/1.1
User-Agent: node-XMLHttpRequest
Accept: */*
Host: localhost:9999
Connection: keep-alive

HTTP/1.1 200 OK
Content-Type: text/plain
Date: Wed, 06 Feb 2013 07:43:37 GMT
Connection: keep-alive
Transfer-Encoding: chunked

47
xX_HbcG1DN_nufWddblv:60:60:websocket,htmlfile,xhr-polling,jsonp-polling
0

第二个请求用于实际升级连接:
GET /socket.io/1/websocket/xX_HbcG1DN_nufWddblv HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: MTMtMTM2MDEzNjYxNzMxOA==
Host: localhost:9999

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 249I3zzVp0SzEn0Te2RLp0iS/z0=

您可以在上面的示例中看到,xX_HbcG1DN_nufWddblv是请求之间共享的密钥。这是个问题。ELBs使用轮询路由,意味着升级请求会被发送到之前没有参与初始谈判的服务器上。因此,服务器不知道客户端是谁。
基于内存的有状态数据是负载均衡的敌人。幸运的是,socket.io支持使用Redis来存储数据。如果您将redis连接共享给多个服务器,它们实际上就分享了所有客户端的会话。
有关设置Redis的详细信息,请参见socket.io维基页面

ELB是轮询吗?其实并不是。如果请求来自同一个IP,它们会被重定向到同一台服务器上。这是我在进行负载测试时发现的一个问题。这也是为什么AWS建议在VPC前面放置代理的原因。https://www.stackdriver.com/elb-affinity-problems/ http://harish11g.blogspot.fr/2012/07/aws-elastic-load-balancing-elb-amazon.html(第9点) - mruellan
这还是现状吗?ELB有一个粘性选项,这可以解决问题吗?https://www.dropbox.com/s/scucxocqsw514ci/Screenshot%202014-03-31%2014.47.01.png - SteveEdson
1
@SteveEdson 不幸的是,ELB 没有 TCP 粘滞选项。 - Jake Hoffner
请注意,从控制台创建的Beanstalk实例不支持应用程序ELB。您需要在EB CLI中创建它,当被询问时:选择负载均衡器:1)经典2)应用程序 请选择应用程序。 - Lisandro
@JakeHoffner,我最近了解到AWS现在有三种类型的负载均衡器。传统的那种、网络负载均衡器和应用程序负载均衡器。后者支持粘性会话,这是设置在负载均衡器的目标组上的。 - Jochem Schulenklopper

15

远离 ELB 并使用 ALB 会有任何严重后果吗?在我的情况下 Socket.io 开始工作了,但我有点担心架构中可能存在的其他后果... 使用 ALB 是否有任何缺点? - user3766930
1
@user3766930 自从使用ALB以来,我没有遇到任何问题。我已经使用它六个月了,对我来说没有任何不利因素,也没有架构问题。 - anurag_29
谢谢,我在考虑同时使用ALB处理套接字和ELB处理目前所使用的其他所有内容,但我不确定是否值得保留两者。 - user3766930
顺便提一下,粘性会话应该在负载均衡器的目标组上启用。那里有一个属性。 - Jochem Schulenklopper
使用n ALB与socket.io配合工作时,是否需要额外的技巧? - coolboyjules

5

正如我在帖子中提到的那样,我们仅使用ELB来终止SSL,并在支持WebSockets的HTTP代理服务器集群上进行负载平衡。 ELB不直接与WebSocket服务器通信。 HTTP代理集群处理查找正确的socket.io服务器以连接并确保会话粘性。


如果http代理运行在与socket.io应用程序相同的服务器上,那么就不应该有任何问题,对吧?我正在考虑使用ELB在一组运行(-> http代理-> socket.io)的服务器之间进行负载平衡。 - Sudhir Jonathan

0

当您在云中运行具有负载均衡器/反向代理、路由器等的服务器时,需要配置它以正常工作,特别是当您将服务器扩展到使用多个实例时。

Socket.io、SockJS和类似库所具有的约束之一是它们需要不断地与服务器的同一实例进行通信。当只有一个服务器实例时,它们可以完美地工作。

当您在云环境中扩展应用程序时,负载均衡器(在Cloud Foundry的情况下为Nginx)将接管,并且请求将被发送到不同的实例,导致Socket.io出现故障。

为了帮助解决这种情况,负载均衡器具有称为“粘性会话”或“会话亲和力”的功能。其主要思想是,如果设置了此属性,则在第一个负载平衡请求之后,所有后续请求都将发送到同一服务器实例。

在Cloud Foundry中,启用基于cookie的粘性会话,适用于设置cookie jsessionid的应用程序。

注意:jsessionid是Java/Spring应用程序中常用于跟踪会话的cookie名称。Cloud Foundry只是采用它作为所有框架的粘性会话cookie。

因此,所有应用程序需要做的就是设置一个名为jsessionid的cookie,以使socket.io正常工作。

app.use(cookieParser); app.use(express.session({store:sessionStore, key:'jsessionid', secret:'your secret here'}));

这些是步骤:

Express设置了一个名为jsessionid的会话cookie。 当socket.io连接时,它使用相同的cookie并命中负载均衡器。 负载均衡器始终将其路由到设置cookie的同一台服务器。 如果您正在使用应用程序负载均衡器,则粘性会话设置位于目标组级别。


这跟 OP 的问题有什么关系吗? - Samuel Goldenbaum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接