AWS 应用程序负载均衡器和 socket.io
AWS application load balancer and socket.io
我有一个 socket.io 聊天室 运行ning,因为我们 运行ning 在一台机器上,所以流量越来越大。我们有 运行 个使用 ws 套接字库的基准测试,它们确实表现得更好,可以更好地利用我们的硬件。不过,这将以不得不重写我们的应用程序为代价。
我们的 socket.io 应用程序允许用户创建使用命名空间实现的私人聊天室。例如
localhost:8080/room/1
localhost:8080/room/2
localhost:8080/room/3
当一切都在一个实例中时,这很容易,但现在我们正在寻求将这种能力扩展到多个节点。
我们运行亚马逊云中的这个实例。以前看起来扩展 websockets 是 ELB 的问题。我们注意到亚马逊现在支持和支持 websockets 的应用程序负载均衡器。这听起来不错,但在阅读文档后我必须承认我真的不知道它是什么意思。如果我将 socket.io 与数千个名称空间一起使用,我是否只需将实例放在这个 ALB 后面,一切都会正常工作?我的主要问题是:
如果 x 个用户加入命名空间,ALB 是否会自动将我的消息重定向到正确的用户或从正确的用户重定向?所以假设我在 ALB 后面有 5 个 vanilla socket.io 实例 运行ning。用户 1 创建一个命名空间。几个小时后,用户 99999 来了,想加入这个命名空间,是否需要编写任何额外的代码来执行此操作,或者 alb 是否会将所有内容重定向到它应该去的地方?发送和接收消息也是一样吗?
虽然 ALB 会正确地对用户进行负载平衡,但您需要稍微调整您的代码,因为加入特定房间的用户将分散在不同的服务器中。
在他们的 documentation socket.io 中提供了一种方法来做到这一点:
Now that you have multiple Socket.IO nodes accepting connections, if
you want to broadcast events to everyone (or even everyone in a
certain room) you’ll need some way of passing messages between
processes or computers.
The interface in charge of routing messages is what we call the
Adapter. You can implement your own on top of the socket.io-adapter
(by inheriting from it) or you can use the one we provide on top of
Redis: socket.io-redis:
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
ALB 设置
我建议在您的 ALB 中启用 sticky session,否则在使用 non-websocket 传输时 socket.io 握手会失败,例如长轮询,因为使用此传输的握手任务需要多个请求,并且您需要对同一台服务器执行所有这些请求。
使用不带 socket.io 适配器的 ALB 路由选择。
If I wanted to avoid having a redis database. For example, if my rooms are created by users, if userA creates a room
on instance 4, if another user wants to join this room, how would they
know which instance it is on? Would I need the adapter here too?
此替代方案的目标是将每个房间分配给特定的 EC2 实例。我们将使用 ALB Routing
来实现这一点
N 个房间 > 1 个实例。
第 1 步:
您需要将房间 URL 更改为:
/i1/room/550
/i1/room/20
/i2/room/5
/i5/room/492
存在:
/{instance-number}/room/{room-id}
这是必需的,以便 ALB 可以将每个房间路由到特定实例。
第 2 步:
创建 N 个目标组(N 是您当前拥有的实例数)
第 3 步:
将每个实例注册到每个目标组
目标组 > 实例 X 目标组 > 目标选项卡 > 编辑 > 选择实例 X > 添加到已注册
Target group X > EC2 Instance X
Target group Y > EC2 Instance Y
第 4 步:
编辑 ALB 目标规则
负载均衡器 > 您的 ALB > 侦听器 > View/Edit 规则
第 5 步:
使用以下设置为每个目标 group/instance 创建一个规则:
- IF > 路径:
/iX/room/*
- 然后 > 转发到:
instanceX
当您输入以下内容时进行此设置:
/i1/room/550
您将使用 EC2 实例 1。
/i2/room/200
将使用 EC2 实例 2
等等。
现在您必须制定自己的逻辑才能使房间在您的实例之间保持平衡。 您不希望一个实例托管几乎所有组。
我推荐第一种方法,因为它可以轻松自动缩放。
我有一个 socket.io 聊天室 运行ning,因为我们 运行ning 在一台机器上,所以流量越来越大。我们有 运行 个使用 ws 套接字库的基准测试,它们确实表现得更好,可以更好地利用我们的硬件。不过,这将以不得不重写我们的应用程序为代价。
我们的 socket.io 应用程序允许用户创建使用命名空间实现的私人聊天室。例如
localhost:8080/room/1
localhost:8080/room/2
localhost:8080/room/3
当一切都在一个实例中时,这很容易,但现在我们正在寻求将这种能力扩展到多个节点。
我们运行亚马逊云中的这个实例。以前看起来扩展 websockets 是 ELB 的问题。我们注意到亚马逊现在支持和支持 websockets 的应用程序负载均衡器。这听起来不错,但在阅读文档后我必须承认我真的不知道它是什么意思。如果我将 socket.io 与数千个名称空间一起使用,我是否只需将实例放在这个 ALB 后面,一切都会正常工作?我的主要问题是:
如果 x 个用户加入命名空间,ALB 是否会自动将我的消息重定向到正确的用户或从正确的用户重定向?所以假设我在 ALB 后面有 5 个 vanilla socket.io 实例 运行ning。用户 1 创建一个命名空间。几个小时后,用户 99999 来了,想加入这个命名空间,是否需要编写任何额外的代码来执行此操作,或者 alb 是否会将所有内容重定向到它应该去的地方?发送和接收消息也是一样吗?
虽然 ALB 会正确地对用户进行负载平衡,但您需要稍微调整您的代码,因为加入特定房间的用户将分散在不同的服务器中。
在他们的 documentation socket.io 中提供了一种方法来做到这一点:
Now that you have multiple Socket.IO nodes accepting connections, if you want to broadcast events to everyone (or even everyone in a certain room) you’ll need some way of passing messages between processes or computers.
The interface in charge of routing messages is what we call the Adapter. You can implement your own on top of the socket.io-adapter (by inheriting from it) or you can use the one we provide on top of Redis: socket.io-redis:
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));
ALB 设置
我建议在您的 ALB 中启用 sticky session,否则在使用 non-websocket 传输时 socket.io 握手会失败,例如长轮询,因为使用此传输的握手任务需要多个请求,并且您需要对同一台服务器执行所有这些请求。
使用不带 socket.io 适配器的 ALB 路由选择。
If I wanted to avoid having a redis database. For example, if my rooms are created by users, if userA creates a room on instance 4, if another user wants to join this room, how would they know which instance it is on? Would I need the adapter here too?
此替代方案的目标是将每个房间分配给特定的 EC2 实例。我们将使用 ALB Routing
来实现这一点N 个房间 > 1 个实例。
第 1 步:
您需要将房间 URL 更改为:
/i1/room/550
/i1/room/20
/i2/room/5
/i5/room/492
存在:
/{instance-number}/room/{room-id}
这是必需的,以便 ALB 可以将每个房间路由到特定实例。
第 2 步:
创建 N 个目标组(N 是您当前拥有的实例数)
第 3 步:
将每个实例注册到每个目标组
目标组 > 实例 X 目标组 > 目标选项卡 > 编辑 > 选择实例 X > 添加到已注册
Target group X > EC2 Instance X
Target group Y > EC2 Instance Y
第 4 步:
编辑 ALB 目标规则
负载均衡器 > 您的 ALB > 侦听器 > View/Edit 规则
第 5 步:
使用以下设置为每个目标 group/instance 创建一个规则:
- IF > 路径:
/iX/room/*
- 然后 > 转发到:
instanceX
当您输入以下内容时进行此设置:
/i1/room/550
您将使用 EC2 实例 1。/i2/room/200
将使用 EC2 实例 2
等等。
现在您必须制定自己的逻辑才能使房间在您的实例之间保持平衡。 您不希望一个实例托管几乎所有组。
我推荐第一种方法,因为它可以轻松自动缩放。