【问题标题】:Where to store shared cache objects in Cloud Run?在 Cloud Run 中将共享缓存对象存储在哪里?
【发布时间】:2020-04-15 01:49:35
【问题描述】:

我正在使用 Cloud Run 创建数据提取管道。每次通过 Pub Sub 将文件放入 GCS 存储桶时,都会调用我的 Cloud Run api。我需要加载一些元数据,其中包含我正在摄取的数据的文本。此元数据很少更改。我显然不想在每次执行时将它重新加载到内存中。我最好的选择是什么?到目前为止,我能够研究的是:

选项一

如果对象的开销很大,您也可以在内存中缓存它们 在每个服务请求上重新创建。从请求逻辑中移动它 到全局范围会导致更好的性能。 https://cloud.google.com/run/docs/tips#run_tips_global_scope-java

在这个链接给出的例子中,heavyComputation 函数是否只在冷启动时被调用一次?如果我需要在元数据更新时偶尔重新触发此功能怎么办。我还发现以下信息令人不安,因为似乎无法保证其他实例会重用该对象。

在 Cloud Run 中,您不能假设保留了服务状态 请求之间。但是,Cloud Run 确实重用了单个容器 实例来服务持续的流量,因此您可以在 全局范围以允许其值在后续使用 调用。任何个人请求是否受益于 这种重用无法提前知道。

选项 2

使用 Redis 或 Cloud Memory Store 之类的东西,只要有变化,就会由云功能更新。并且所有的云运行 api 实例都从 Redis 中拉取元数据信息。这会比选项 1 性能更低还是更高?这还有其他不利方面吗?

如果有其他更好的方法可以做到这一点,我会很感兴趣。

更新 1:我想了更多,因为我的元数据对于每个租户都会有所不同,而且我的云运行代码的每次调用都会为一个租户摄取一个文件,因此将是一个坏主意在每次执行时加载所有租户元数据,即使它被缓存。不过,我可能会在每个租户的项目中运行单独的云运行。

【问题讨论】:

    标签: shared-memory google-cloud-run data-ingestion google-cloud-memorystore


    【解决方案1】:

    关于第一个选项(选项1):

    heavyComputation() 函数 here 只会在冷启动时调用,每次创建一个新的 Cloud Run container instance 时(当超过可以并行发送到给定容器实例的最大请求数时,因此一个新的实例被创建)。

    为了解决第二个选项(选项2):

    截至目前,Cloud Run(全托管)不支持无服务器 VPC 访问,因此无法连接到 Cloud Memorystore。请关注以下Feature Request 以从 Cloud Run 产品团队获取所有相关信息和更新,以检查此功能何时可用。

    您可以在 post 和已经提到的功能请求上找到一些解决方法。它们基本上包括:

    1. Using a Google Kubernetes Engine cluster with Cloud Run
    2. 在 Compute Engine 上设置 Redis 实例。

    【讨论】:

    • MemoryStore 不就是 BigQuery 或 GCS 之类的另一种服务吗?您能否详细说明为什么 Cloud Run 需要无服务器 VPC 访问才能使用 MemoryStore?
    • 另外,在那个线程上,一群人说 Cloud Run API 是可以公开访问的,但我不相信这是真的。在部署 API 时,它会询问我们是否要允许匿名访问。我错过了什么吗?
    • Cloud Memorystore 有一些非常严格的connectivity requirements,基本上实例必须与其他服务共享相同的 VPC 网络。 Serverless VPC Access 允许在 Google Cloud Platform 上下文中实现无服务器服务(例如 App Engine、Cloud Functions 和即将推出的 Cloud Run)与 VPC 网络之间的连接。
    • 关于 Cloud Run API,您可以选择公开访问的服务或要求身份验证的服务。但这与authentication 有关,这意味着谁能够调用 Cloud Run 服务。他们所指的内容与连接性有关,这意味着由于默认情况下每个服务都有一个外部端点,因此它们通过服务的默认 URL 暴露于公共互联网。需要的是公共互联网无法访问 Cloud Run 服务,而是通过 VPC 网络上的内部 IP 访问。
    • DoS 类型攻击是公开服务的唯一关注点吗?还是有其他顾虑?
    【解决方案2】:

    我们从您的初始前提开始,即“我显然不想在每次执行时将其重新加载到内存中”。对我来说,这并不总是正确的说法。如果我实现了一种缓存技术,那么作为一名程序员,我会花时间让它正确,并引入错误和维护的机会。如果我每次执行节省了 100 毫秒,那么与增加执行时间所节省的成本相比,需要多少数千次执行才能实现收支平衡?我通常会预先采取最简单的方法,并准备好监控未来的运营,并仅在必要时进行改进。

    话虽如此,让我们假设您已确定每秒将发出无数个新文件创建请求并希望进行优化。了解如何最好地使用 Cloud Run 的关键在于它运行一个可以处理并发请求的整个容器。我相信默认是 80。这意味着如果创建了 80 个并发文件,则只会创建一个容器实例并处理 80 个并行事件。如果您使用 Java 进行编码,这意味着 80 个并发线程都在同一个 JVM 中。每个线程将具有对公共全局变量的并发寻址能力。只有当第 81 个请求到达并且之前的 80 个请求都没有完成时,才会生成新的 Cloud Run 容器。

    这告诉我的是,我的第一个“改进”是在第一次使用时在我的 JVM 中填充缓存数据,并保留它以供后续重用。您何时填充缓存数据?这将是您的设计选择。您可以在容器首次启动时预先填充所有内容......如果您知道数据将用于每个请求,这将是明智的。或者,如果您有多个可缓存的值,请考虑创建一个包含您的名称/值对的映射,并具有一个返回缓存值(如果存在)或从慢速存储、缓存和返回值(如果最初不存在)的访问器)。

    【讨论】:

    • 我的元数据位于 bigquery 中。在每次调用以往返 BigQuery 时获取相同的信息(正如我所说的不经常更改)将是相当昂贵的。想象一下加载了数百万个文件,并且在每次调用开始时,我都会去 Bigquery 以不必要地提取相同的信息。感谢您对容器级别的云运行的解释。这有助于清除一些问题。所以你的意思是,只有在第 81 个请求第二个容器启动时,它才会重新运行那个全局变量?
    • 如果 bigquery 中的元数据发生更改,我是否可以强制它重新启动所有云运行实例?
    • 啊哈......所以我们这里有两个故事......一个是使用元数据的“每次请求”成本,第二个是每个容器获取一次的“每次检索”成本。如果我们假设您必须在每次发生“某些”元数据更改时执行 BigQuery 查询,那么如果您可以执行查询并将数据存储为 GCS 对象,则可以在您的容器中廉价且快速地检索。
    • 回复:当第二个容器启动时。仅当要处理的并发请求多于您声明的请求时,才会启动第二个容器。如果默认是 80 个并发请求,并且永远不会超过这个,那么您将永远运行一个容器。但是,如果您的流量激增并且您发现最新的请求将导致 81 个并发请求,那么将生成一个新容器来运行您的最新请求。容器将在一段可配置的空闲时间后减速(归零)。
    • 如果我必须重复执行,为什么 gcs retreival 必然比 bigquery 便宜。我应该添加的一件事是,在我的代码中,一旦我加载元数据,即创建字典对象,在查找信息时给我 O(1) 查找,我正在执行您之前建议的操作。这使我的代码超快,我会保留它。但我想尽可能少地从 bq 和地图创建中提取。
    猜你喜欢
    • 1970-01-01
    • 2011-09-03
    • 1970-01-01
    • 2022-08-18
    • 2015-07-05
    • 1970-01-01
    • 2012-08-25
    • 1970-01-01
    相关资源
    最近更新 更多