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 分钟:

  1. 客户端通过 WebSocket
  2. 连接到我们的 GraphQL API(实例 1)
  3. 客户端通过WebSocket发送命令生成报告
  4. API为命令生成token,并将生成报告的命令放入CommandQueue(在RabbitMQ中),返回token给Client。
  5. 客户端使用令牌订阅其命令结果的事件
  6. 某后端Worker拿起指令执行报表生成程序
  7. 在此期间 GraphQL API(实例 1)死亡
  8. 客户端自动重新连接到 GraphQL API(实例 2)
  9. 客户端使用先前获取的令牌续订订阅
  10. Worker 完成,结果在 EventsQueue (RabbitMQ)
  11. 我们所有的 GraphQL 实例都会收到关于 ReportGenerationDoneEvent 的信息,并检查是否有人在监听它的令牌。
  12. GraphQL API(实例 2)看到客户端正在等待结果。通过 websockets 推送结果。
  13. 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 和事件溯源模式。