TCP 配置的 Amazon Elastic Load Balancer 上的 Socket.io Websockets

Posted

技术标签:

【中文标题】TCP 配置的 Amazon Elastic Load Balancer 上的 Socket.io Websockets【英文标题】:Socket.io Websockets on a TCP configured Amazon Elastic Load Balancer 【发布时间】:2012-11-01 19:24:33 【问题描述】:

我计划在 EC2 上设置一组运行 Socket.io 的 NodeJS 应用程序服务器,并且我想使用 Elastic Load Balancer 在它们之间分散负载。我知道 ELB 不支持开箱即用的 Websocket,但我可以使用here in Scenario 2 描述的设置。

不过,正如 the blog post 中所述,我注意到此设置不提供会话关联或源 IP 信息:

我们不能在这个设置中拥有 Session Affinity 或 X-Forward 标头 因为 ELB 不解析 HTTP 消息,所以不可能 匹配 cookie 以确保 Session Affinity 或 Inject 特殊 X-Forward 标头。

Socket.io 在这些情况下还能工作吗?或者有没有另一种方法在负载均衡器后面使用 SSL 设置一组 Socket.io 应用服务器?

编辑:Tim Caswell 已经谈论过这样做here。有没有解释如何设置的帖子?这里再次没有会话粘性,但事情似乎运行良好。

顺便说一句,websockets 真的需要粘性会话吗?信息是作为新的独立请求传播的,还是只有一个请求+连接所有信息都在传播?

【问题讨论】:

快速回答 - 不,如果后续请求将转到不同的服务器,socket.io 将无法工作,您需要找到一种方法在您的设置中实现“粘性”会话。 @Dmitry:你确定吗?如果您使用共享存储,我认为 socket.io 应该可以工作,例如RedisStore? @LinusGThiel 如果没有粘性会话,它将无法工作,这里有更多细节:groups.google.com/d/topic/socket_io/d9a8c49uymc/discussion 谢谢,@Dmitry。我似乎意识到了这个问题。 如需源IP地址,see here。 【参考方案1】:

即使使用 TCP ELB,Socket.io 也不能开箱即用,因为它会在升级到 websocket 连接之前发出两个 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 是请求之间的共享密钥。这就是问题。 ELB 执行循环路由,这意味着升级请求会到达未参与初始协商的服务器。因此,服务器不知道客户端是谁。

内存中的状态数据是负载平衡的敌人。值得庆幸的是,socket.io 支持使用 Redis 来存储数据。如果您与多个服务器共享您的 redis 连接,它们实际上共享所有客户端的会话。

有关设置 Redis 的详细信息,请参阅socket.io wiki page。

【讨论】:

ELB 是否进行循环?并不真地。如果请求来自单个 IP,它们将被重定向到同一台服务器。这是我在进行负载测试时发现的一个问题。这就是 AWS 建议在 VPC 前面设置代理的原因。 stackdriver.com/elb-affinity-problemsharish11g.blogspot.fr/2012/07/…(第 9 点) 还是这样吗? ELB 有粘性选项,这可以解决问题吗? dropbox.com/s/scucxocqsw514ci/… @SteveEdson ELB 没有 TCP 粘性选项。 请注意,从控制台创建的 beanstalk 实例不支持 Application ELB。您需要从 EB CLI 创建它,当被问及:选择负载均衡器:1) 经典 2) 应用程序选择应用程序。 @JakeHoffner,我最近了解到 AWS 现在有三种类型的负载均衡器。经典的,一个网络负载均衡器和一个应用程序负载均衡器。后者确实支持粘性会话,这是在负载均衡器的目标组上设置的。【参考方案2】:

您现在可以使用 AWS 最近推出的新应用程序负载均衡器。

只需将 ELB(现在称为经典负载平衡器)替换为 ALB(应用程序负载平衡器)并启用粘性会话。

ALB 支持 Web 套接字。这应该可以解决问题。

https://aws.amazon.com/blogs/aws/new-aws-application-load-balancer/

http://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html

【讨论】:

离开 ELB 并使用 ALB 有什么严重后果吗? Socket.io 开始在我的情况下工作,但我有点担心架构中可能出现的其他后果......使用 ALB 优于 ELB 有什么缺点吗? @user3766930 自从搬到 ALB 以来,我没有遇到任何问题。过去六个月一直在使用它。对我来说没有这样的缺点。也没有架构问题。 谢谢,我正在考虑同时使用 ALB 用于套接字处理,ELB 用于我目前一直在使用的所有其他东西,但我不确定是否值得同时使用 顺便说一句,要在负载均衡器的目标组上启用粘性会话。这是那里的一个属性。 要让 socket.io 与 n ALB 一起工作,是否需要做任何额外的技巧?【参考方案3】:

正如我在帖子中提到的,我们只使用 ELB 来在支持 websocket 的 http-proxy 服务器集群之间进行 ssl 终止和负载平衡。 ELB 不直接与 websocket 服务器通信。 HTTP 代理集群负责查找正确的 socket.io 服务器以连接以确保会话粘性。

【讨论】:

那么,如果 http-proxy 与 socket.io 应用程序在同一台服务器上运行,应该不会有任何问题?我正在考虑使用 ELB 在一组运行的服务器之间进行平衡 (-> http-proxy -> socket.io)【参考方案4】:

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

Socket.io、SockJS 和类似库的限制之一是它们需要不断地与服务器的同一个实例通信。当只有 1 个服务器实例时,它们工作得很好。

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

为了在这种情况下提供帮助,负载平衡器具有称为“粘性会话”又名“会话亲和性”的功能。主要思想是,如果设置了这个属性,那么在第一个负载均衡请求之后,后面的所有请求都会去同一个服务器实例。

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

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

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

app.use(cookieParser); app.use(express.session(store:sessionStore, key:'jsessionid', secret:'你的秘密'));

以下是这些步骤:

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

【讨论】:

这与OP问题有什么关系?

以上是关于TCP 配置的 Amazon Elastic Load Balancer 上的 Socket.io Websockets的主要内容,如果未能解决你的问题,请参考以下文章

Amazon Elastic Beanstalk ebextension 将 nginx 配置参数添加到默认配置中

Elastic Beanstalk V3.0.1 (Amazon Linux 2) 中用于配置更新的挂钩

Amazon Elastic Beanstalk ebextension

如何在没有负载均衡器的情况下使用配置文件在 Amazon Elastic Beanstalk 实例中配置 SSL?

使用 Amazon Linux 2 挂钩配置 Elastic Beanstalk 以通过 SSH 访问私有 git 存储库

apache_conf Meteor项目的Amazon Elastic Beanstalk配置文件。该文件需要保存在r的.ebconfiguration /目录中