Node.js 环境中的水平扩展和 GraphQL
Horizontal scaling and GraphQL within a Node.js environment
我正在尝试构建一个包含即时消息模块的应用程序,主要挑战之一是无论用户数量或交换的消息数量如何,都要保持应用程序的可扩展性。
在 an article 中,我读到可以使用带有“订阅”的 GraphQL 构建实时应用程序,除此之外,它是一个易于使用的协议,并且具有最小化往返对象的优点检索,因此减少了资源使用。
但是如果我们需要向系统添加一个新的 server/node 以便水平扩展怎么办?这可能使用 GraphQL 吗?
以允许水平缩放的websockets实现为例,有SocketCluster。我想知道单独由 GraphQL 开发的应用程序是否可以跨多个扩展 nodes/machines 还是必须与 SocketCluster 等其他框架一起使用才能实现此目的。
不久 - 是的。我们已经做到了,而且效果很好。
诀窍是,在横向扩展方面,您必须比 API 工作应用程序更深入地思考。如果要push架构,一开始就需要异步
为了实现它,我们使用了队列系统,即RabbitMQ。
想象一下生成报告的这种情况,最多可能需要 10 分钟:
- 客户端通过 WebSocket
连接到我们的 GraphQL API(实例 1)
- 客户端通过WebSocket发送命令生成报告
- API为命令生成token,并将生成报告的命令放入CommandQueue(在RabbitMQ中),返回token给Client。
- 客户端使用令牌订阅其命令结果的事件
- 某后端Worker拿起指令执行报表生成程序
- 在此期间 GraphQL API(实例 1)死亡
- 客户端自动重新连接到 GraphQL API(实例 2)
- 客户端使用先前获取的令牌续订订阅
- Worker 完成,结果在 EventsQueue (RabbitMQ)
- 我们所有的 GraphQL 实例都会收到关于
ReportGenerationDoneEvent
的信息,并检查是否有人在监听它的令牌。
- GraphQL API(实例 2)看到客户端正在等待结果。通过 websockets 推送结果。
- GraphQL API(实例 3-100)忽略
ReportGenerationDoneEvent
.
它有点广泛,但是通过简单的抽象,您不必考虑所有这些复杂性,也不必为使用此路由的新流程跨多个服务编写约 30 行代码。
它的绝妙之处在于,您最终获得了很好的水平缩放、事件可重播性(重试)、关注点分离(客户端、api、工作人员),尽快将数据推送到客户端,正如您提到的,您不会在 are we done yet?
请求上浪费带宽。
另一件很酷的事情是,每当用户在我们的面板中打开报告列表时,他都会看到当前正在生成的报告,并且可以订阅他们的更改,因此他们不必手动刷新列表。
关于 SocketCluster 的好想法。它将优化上述场景中的第 10 步,但目前,我们没有看到将 ReportGenerationDoneEvent
广播到整个 API 集群的任何性能问题。对于更多实例或多区域架构,这将是必须的,因为它可以实现更好的扩展和分片。
了解 SocketCluster 在通信层 (WebSockets) 上运行很重要,但逻辑 API 层 (GraphQL) 在其之上。要进行 GraphQL 订阅,您只需使用允许您将信息推送给用户的通信协议,而 WebSockets 允许这样做。
我认为使用 SocketCluster 是一个不错的设计选择,但请记住迭代实现。仅当您计划在任何一个时间点打开多个套接字时才使用 SocketCluster。此外,您应该只在必要时订阅,因为 WebSocket 是有状态的并且需要管理和心跳。
如果您对我上面使用的异步后端架构更感兴趣,请阅读 CQRS 和事件溯源模式。
我正在尝试构建一个包含即时消息模块的应用程序,主要挑战之一是无论用户数量或交换的消息数量如何,都要保持应用程序的可扩展性。
在 an article 中,我读到可以使用带有“订阅”的 GraphQL 构建实时应用程序,除此之外,它是一个易于使用的协议,并且具有最小化往返对象的优点检索,因此减少了资源使用。
但是如果我们需要向系统添加一个新的 server/node 以便水平扩展怎么办?这可能使用 GraphQL 吗?
以允许水平缩放的websockets实现为例,有SocketCluster。我想知道单独由 GraphQL 开发的应用程序是否可以跨多个扩展 nodes/machines 还是必须与 SocketCluster 等其他框架一起使用才能实现此目的。
不久 - 是的。我们已经做到了,而且效果很好。
诀窍是,在横向扩展方面,您必须比 API 工作应用程序更深入地思考。如果要push架构,一开始就需要异步
为了实现它,我们使用了队列系统,即RabbitMQ。
想象一下生成报告的这种情况,最多可能需要 10 分钟:
- 客户端通过 WebSocket 连接到我们的 GraphQL API(实例 1)
- 客户端通过WebSocket发送命令生成报告
- API为命令生成token,并将生成报告的命令放入CommandQueue(在RabbitMQ中),返回token给Client。
- 客户端使用令牌订阅其命令结果的事件
- 某后端Worker拿起指令执行报表生成程序
- 在此期间 GraphQL API(实例 1)死亡
- 客户端自动重新连接到 GraphQL API(实例 2)
- 客户端使用先前获取的令牌续订订阅
- Worker 完成,结果在 EventsQueue (RabbitMQ)
- 我们所有的 GraphQL 实例都会收到关于
ReportGenerationDoneEvent
的信息,并检查是否有人在监听它的令牌。 - GraphQL API(实例 2)看到客户端正在等待结果。通过 websockets 推送结果。
- GraphQL API(实例 3-100)忽略
ReportGenerationDoneEvent
.
它有点广泛,但是通过简单的抽象,您不必考虑所有这些复杂性,也不必为使用此路由的新流程跨多个服务编写约 30 行代码。
它的绝妙之处在于,您最终获得了很好的水平缩放、事件可重播性(重试)、关注点分离(客户端、api、工作人员),尽快将数据推送到客户端,正如您提到的,您不会在 are we done yet?
请求上浪费带宽。
另一件很酷的事情是,每当用户在我们的面板中打开报告列表时,他都会看到当前正在生成的报告,并且可以订阅他们的更改,因此他们不必手动刷新列表。
关于 SocketCluster 的好想法。它将优化上述场景中的第 10 步,但目前,我们没有看到将 ReportGenerationDoneEvent
广播到整个 API 集群的任何性能问题。对于更多实例或多区域架构,这将是必须的,因为它可以实现更好的扩展和分片。
了解 SocketCluster 在通信层 (WebSockets) 上运行很重要,但逻辑 API 层 (GraphQL) 在其之上。要进行 GraphQL 订阅,您只需使用允许您将信息推送给用户的通信协议,而 WebSockets 允许这样做。
我认为使用 SocketCluster 是一个不错的设计选择,但请记住迭代实现。仅当您计划在任何一个时间点打开多个套接字时才使用 SocketCluster。此外,您应该只在必要时订阅,因为 WebSocket 是有状态的并且需要管理和心跳。
如果您对我上面使用的异步后端架构更感兴趣,请阅读 CQRS 和事件溯源模式。