Node.js 环境中的水平缩放和 GraphQL



我正在尝试构建一个包含即时消息模块的应用程序,主要挑战之一是无论用户数量或交换的消息如何,都要保持应用程序的可扩展性。

在一篇文章中,我读到可以使用带有"订阅"的 GraphQL 构建实时应用程序,除此之外,它是一种简单易用的协议,并且具有最大限度地减少往返对象检索的优点,因此减少了资源使用。

但是,如果我们需要向系统添加新的服务器/节点以便水平扩展,该怎么办?使用 GraphQL 可以做到这一点吗?

以允许水平扩展的websockets实现为例,有SocketCluster。我想知道单独由 GraphQL 开发的应用程序是否可以跨多个节点/机器进行扩展,或者必须与另一个框架(如 SocketCluster(一起使用才能实现这一目标。

很快 - 是的。我们已经做到了,而且效果很好。

诀窍是,在水平扩展方面,您必须更深入地考虑不仅仅是 API worker 应用程序。如果你想要推送架构,它需要从一开始就是异步的。

为了实现它,我们使用了队列系统,即RabbitMQ。

想象一下生成报告的场景,最多可能需要 10 分钟:

  1. 客户端通过 WebSocket 连接到我们的 GraphQL API(实例 1(
  2. 客户端发送命令以通过 WebSocket 生成报告
  3. API 为命令生成令牌,并将用于
  4. 生成报告的命令放入 CommandQueue(在 RabbitMQ 中(,并将令牌返回给客户端。
  5. 客户端使用令牌订阅其命令结果的事件
  6. 某些后端工作线程选取命令并执行报告生成过程
  7. 在此期间,GraphQL API(实例 1(死亡
  8. 客户端自动重新连接到 GraphQL API(实例 2(
  9. 客户端使用以前获取的令牌续订订阅
  10. 工作线程已完成,结果在事件队列 (RabbitMQ( 上
  11. 我们所有的 GraphQL 实例都会接收有关ReportGenerationDoneEvent的信息,并检查是否有人在监听其令牌。
  12. GraphQL API(实例 2(看到客户端正在等待结果。通过网络套接字推送结果。
  13. GraphQL API(实例 3-100(忽略ReportGenerationDoneEvent

它相当广泛,但是通过简单的抽象,您不必考虑所有这些复杂性,也不必为此路由的新进程跨多个服务编写~30行代码。

它的精彩之处在于,您最终会获得良好的水平缩放、事件可重放性(重试(、关注点分离(客户端、api、worker(,尽快将数据推送到客户端,并且正如您提到的,您不会在are we done yet?请求上浪费带宽。

另一个很酷的事情是,每当用户在我们的面板中打开报告列表时,他都会看到当前生成的报告,并且可以订阅他们的更改,因此他们不必手动刷新列表。

对套接字集群的良好思考。它将优化上述场景中的步骤 10,但目前,我们没有看到将ReportGenerationDoneEvent广播到整个 API 集群的任何性能问题。对于更多的实例或多区域架构,这将是必须的,因为它将允许更好的扩展和分片。

重要的是要了解 SocketCluster 在通信层 (WebSockets( 上运行,但逻辑 API 层 (GraphQL( 高于此层。要进行 GraphQL 订阅,您只需要使用允许您向用户推送信息的通信协议,而 WebSockets 允许这样做。

我认为使用 SocketCluster 是一个不错的设计选择,但请记住迭代实现。仅当您计划在任何单个时间点打开多个套接字时,才使用 SocketCluster。此外,应仅在必要时进行订阅,因为 WebSocket 是有状态的,需要管理和检测信号。

如果你对我上面使用的异步后端体系结构进一步感兴趣,请阅读 CQRS 和事件溯源模式。

最新更新