【问题标题】:Pubsub, push to all Cloud Run containersPubsub,推送到所有 Cloud Run 容器
【发布时间】:2021-02-13 23:55:37
【问题描述】:

我正在使用 Pubsub(push) 和 Cloud Run,我将在其中部署一个使用 Spring Boot 构建的 Java 应用程序。

我有两个案例。假设由于高负载,我有 Service A 在 Cloud Run 中运行 10 个容器/实例。我想:

  • 所有(广播)Service A 的容器推送消息(来自 Cloud Function)
  • 将消息(来自云函数)推送到Service A单个任意容器

背景:My Cloud Run 服务将使用 server-sent-events 将数据直接推送到客户端/浏览器。这当然意味着容器/实例将保持状态。在某些情况下,我需要将消息推送到所有容器上的所有 sse/ws 连接(想象一个带有公共聊天室的聊天应用程序,每个人都可以看到发布的消息)。由于 Cloud Run 中的容器无法相互了解或查看(我假设),因此我认为解决此问题的正确方法是使用 pubsub。

如果有更适合这种情况的工具,请指出正确的方向。

【问题讨论】:

    标签: java spring-boot google-cloud-platform google-cloud-pubsub google-cloud-run


    【解决方案1】:

    您只能推送到服务端点 (URL),而不是该服务的单个实例。

    每个 Cloud Run 服务只有一个容器。您可以通过每个实例的最大请求数(并发)来控制创建的实例数。

    Cloud Run 实例是根据流量动态创建和销毁的。 Pub/Sub 是一种基于订阅的服务。每个订阅者都会收到一份消息副本。您在某个时间点查看同一消息的 X 个副本,而在另一个时间点查看 Y 个副本。这违反了消息传递的 Pub/Sub 模型。

    【讨论】:

      【解决方案2】:

      Cloud Run 实例是独立的,正如您所说,它们彼此无法看到和了解。另外,Cloud Run 合约是无状态的,所以不能有状态并在推送消息中更新。

      实例可以处于活动状态和非活动状态(处理或不处理请求),如果您有 10 个当前活动实例,则可能会提前(启动)预置 20 或 30 个实例以吸收流量增加(如果发生)。

      所有这些都说明你的设计是错误的。您无需依赖 Cloud Run 实例上的状态并考虑通过推送对其进行更新。

      您需要将状态存储在外部,例如 Memorystore 或 firestore,并在每次请求时获取数据。

      【讨论】:

        【解决方案3】:

        如果您想为此使用 pub-sub,那么您必须为每个云运行实例创建一个订阅者,您可以在程序加载/启动时执行此操作,但随后您将不得不对同一订阅者进行垃圾收集您的程序退出或需要定期清理有大量未确认消息的订阅者。

        我建议您查看https://docs.nats.io/nats-concepts/subjects,这正是您想要做的。

        此外,由于您已经提到这将是一个有状态的服务,因此如果您使用应用引擎比云运行更好,因为云运行实例仅在它处理活动连接时才处于活动状态。如果任何连接由于某种原因中断,那么您可能会丢失容器的状态。

        【讨论】:

        • Cloud Run 容器实例启动和停止。它们不是程序,但可以看起来是一个。实例从 HTTP 请求开始,并在 HTTP 请求返回响应时结束(想想 HTTP GET)。
        【解决方案4】:

        Cloud Run 支持接受 WebSocket 连接。虽然这些连接不是永久的长期连接(它们在 GA 中有 15min timeout,在 beta 中有长达 60 分钟的超时),但只要在给定容器中至少有一个 WebSocket 连接处于活动状态,它们就会阻止 google 终止容器实例.每个容器最多可以有 250 WebSocket connections(或通常在任何给定时间 250 个 HTTP 连接)。

        这意味着您可以让您的 Java 应用程序在启动时立即订阅来自 Google Pubsub 的主题,并等待 Pubsub 消息,然后这些消息将被中继到任何(或所有)连接到给定的 WebSocket 客户端特定的 Cloud Run 实例。

        Google Cloud Pubsub 支持one-to-many subscription pattern,因此您可以将发布到 Pubsub 主题的一条消息发布给所有订阅者,在这种情况下,这些订阅者将是具有活动 WebSocket 连接的每个单独的 Google Cloud Run 容器实例。

        1. Java 应用程序将在启动时连接到 Pubsub 主题。
        2. Java 应用程序将接受 WebSocket 连接。
        3. Java 应用将根据消息正文中的内容以及您的过滤逻辑将消息从 Pubsub 订阅中继到相应的客户端。

        因此,您的设计对于 Google Cloud Run(现在支持 WebSocket)和 Google Cloud Pubsub 是可行的。我确实有一些顾虑,所以我把它们放在这里。

        我首先担心的是 Google 对 Google Cloud Run 施加的 15 分钟(测试版中为 60 分钟)HTTP 超时,这意味着您的客户端的 websocket 连接将在该时间阈值之后被丢弃,您将需要处理重新连接。在重新连接的那一瞬间,一些消息可能会丢失,因此很难实现 100% 保证的消息传递。

        我的第二个担忧(您可能会担心很远)是由于 Pubsub 的一对多扇出架构的性质,一条发布的消息将被转发到 PubSub 主题发给所有订阅者,这意味着所有 Cloud Run 容器实例都将收到该消息。 如果该消息打算只为多个容器之一中的一个 WebSocket 传递,则可能会浪费 cpu/网络资源(成本),并且当有多个 Cloud Run 容器同时运行时,这个问题只会变得更大时间和消息量很大。当然,您可以为每个容器或每个“聊天室”创建一个主题,但这会增加复杂性,而且我相信 google 对您可以拥有的主题数量以及管理操作的 TPS 限制设置了一些限制。

        您可能还想看看 Redis Pubsub,它允许您订阅特定主题(并且没有主题创建/销毁开销)。从技术上讲,您可以为每个用户或每个“聊天室”创建一个主题,并让您的 Java 应用程序根据连接的 WebSockets 的兴趣订阅该主题。这可能会解决我上面提到的第二个问题,因为每个容器实例只会接收与它们相关的消息......但这种方法的权衡是你的 Redis 实例可能是一个瓶颈。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-29
          • 2020-07-01
          • 1970-01-01
          • 2021-02-13
          • 2019-11-16
          • 2021-12-28
          • 1970-01-01
          • 2020-04-16
          相关资源
          最近更新 更多