Pubsub,推送到所有 Cloud Run 容器

如何解决Pubsub,推送到所有 Cloud Run 容器

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

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

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

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

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

解决方法

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

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

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

,

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

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

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

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

,

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

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

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

,

Cloud Run 支持接受 WebSocket 连接。虽然这些连接不是永久的长期连接(它们在 GA 中有 15min timeout 并且在 beta 中有长达 60 分钟的超时),但它们确实会阻止 google 终止容器实例,只要至少有一个 WebSocket 连接在给定的容器。每个容器最多可以有 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 应用程序根据连接的 WebSocket 的兴趣订阅该主题。这可能会解决我上面提到的第二个问题,因为每个容器实例只会接收与它们相关的消息……但这种方法的权衡是您的 Redis 实例可能成为瓶颈。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-