AWS应用负载均衡器和socket.io

14

我正在运行一个socket.io聊天室,随着我们在一台机器上运行,流量逐渐增大。我们已经使用ws库对套接字进行了基准测试,发现它们的性能要好得多,这将更好地利用我们的硬件。但是这需要重写我们的应用程序。

我们的socket.io应用程序允许用户创建私人聊天室,通过使用命名空间实现。例如

localhost:8080/room/1
localhost:8080/room/2
localhost:8080/room/3

当所有东西都在一个实例中时,这是相当容易的,但现在我们正在考虑将这种能力扩展到多个节点。

我们在亚马逊云中运行此实例。以前,使用 ELB 扩展 websockets 是一个问题。我们注意到,亚马逊现在支持应用程序负载均衡器,它支持 websockets。听起来很棒,但是阅读文档后,我必须承认我不知道它是什么意思。如果我正在使用数千个命名空间的 socket.io,我只需将实例放在 ALB 后面,一切都会正常工作吗?我的主要问题是:

如果 x 个用户加入命名空间,ALB 是否会自动将我的消息重定向到正确的用户?假设我有 5 个基本的 socket.io 实例在 ALB 后面运行。用户1创建了一个命名空间。几个小时后,用户99999来了,并想加入该命名空间,是否需要编写任何其他代码来完成此操作,还是 alb 会将所有内容重定向到应该去的地方?发送和接收消息也是一样的吗?


只是为了确认,您想要实现的是:每个实例一个房间,还是一个房间可以由多个实例处理? - Marcos Casagrande
@MarcosCasagrande 房间可以由多个实例处理。 - user2924127
1个回答

53

虽然ALB会正确地负载均衡用户,但您需要稍微调整代码,因为加入特定房间的用户将分散在不同的服务器上。

在他们的文档中,socket.io提供了一种方法来解决这个问题:

现在您有多个Socket.IO节点接受连接,如果您想向所有人(甚至是某个房间中的所有人)广播事件,则需要一些方法在进程或计算机之间传递消息。

负责路由消息的接口是我们所谓的Adapter。您可以在socket.io-adapter之上实现自己的Adapter(通过继承它),也可以使用我们在Redis之上提供的Adapter:socket.io-redis:

var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));

ALB设置

我建议在您的ALB中启用粘性会话,否则,在使用非WebSocket传输(例如长轮询)时,Socket.io握手将失败,因为使用这些传输进行握手任务需要多个请求,而且您需要执行所有这些请求针对同一服务器。

enter image description here enter image description here


使用ALB路由的替代方案,无需socket.io适配器。

如果我想避免使用redis数据库。例如,如果我的房间是由用户创建的,如果用户A在实例4上创建了一个房间,如果另一个用户想要加入这个房间,他们怎么知道它在哪个实例上?我也需要适配器吗?

这种替代方案的目标是将每个房间分配给特定的EC2实例。我们将使用ALB路由来实现这一目标。

N个房间 > 1个实例。

步骤1:

您需要将房间URL更改为类似以下内容的URL:

/i1/room/550
/i1/room/20
/i2/room/5
/i5/room/492

存在:

/{instance-number}/room/{room-id}

这是必需的,这样ALB才能将每个房间路由到特定的实例。

步骤2:

创建N个目标组(N是当前拥有的实例数量)

Image

步骤3:

将每个实例注册到每个目标组

目标组 > 实例X目标组> 目标选项卡 > 编辑 > 选择实例X > 添加到已注册列表

Target group X > EC2 Instance X
Target group Y > EC2 Instance Y

图片描述

第四步:

编辑ALB目标规则

负载均衡器>您的ALB>侦听器>查看/编辑规则

图片描述

第五步:

为每个目标组/实例创建一个规则,具有以下设置:

  • IF > Path: /iX/room/*
  • THEN > forward to: instanceX

图片描述


图片描述

一旦您完成了这个设置,当您输入:

  • /i1/room/550 您将使用EC2实例1
  • /i2/room/200 将使用EC2实例2

等等。

现在,您需要自己制定逻辑,以便在您的实例之间平衡房间。 您不希望一个实例承载几乎所有的群组。

我建议使用第一个方法,因为它可以轻松地进行自动缩放。


感谢您的详细解释!所以,如果我采用这种方法,我需要运行一个Redis数据库吗?这回答了我的问题,但我还有一个附加问题。如果我想避免使用Redis数据库,您建议的第一种方法是否可行?例如,如果我的房间是由用户创建的,如果用户A在实例4上创建了一个房间,如果另一个用户想要加入此房间,他们如何知道它在哪个实例上?我也需要适配器吗? - user2924127
我会添加一个关于如何将房间分配给特定实例的说明。但请给我几个小时,因为我现在不在电脑旁边。而且你不需要适配器,ALB会处理到正确实例-房间的路由。 - Marcos Casagrande
房间将由服务器上的某些逻辑强制实施用户限制,因此这不是我关心的事情。请慢慢来,听起来很棒,谢谢! - user2924127
@user2924127 你看过我的更新答案了吗?我解决了“我想避免使用redis数据库,你建议的第一种方法可行吗?” - Marcos Casagrande
感谢@MarcosCasagrande的回答。我正在使用第一种socket.io适配器方法,这种情况下我需要一些帮助,关于单个m4 aws实例上socket.io将处理多少连接以及如何在代码中实现socket.io redis并进行测试?目前我正在使用Node.js和ASP.NETC# websocket在M4.Large实例上,并且由于服务器上的高流量(CPU利用率为90到95%),一些消息未能到达客户端应用程序,遇到了问题。请指导我。谢谢! - Manjunath Bilwar
连接数是你需要自己检查的,可以进行一些测试,看看你的应用程序可以处理多少个连接。我无法给出答案,因为这取决于你的代码、操作系统设置等。关于如何实现套接字redis,你应该阅读文档,但你所需要的只是一个redis服务器。AWS提供了带有redis集群的elasticache服务。 - Marcos Casagrande

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