每小时调用外部 API 并单独执行约 10000 行任务的作业

Posted

技术标签:

【中文标题】每小时调用外部 API 并单独执行约 10000 行任务的作业【英文标题】:Job to call external API every hour and perform tasks for ~10000 rows individually 【发布时间】:2021-05-22 13:47:30 【问题描述】:

我目前正在考虑设计一个系统,该系统需要基本上每小时运行一项作业,但大约有 10,000 行。然后,这些行中的每一行都需要调用外部 api 并在分析方面做一些其他的事情。

我目前正在尝试找出实现这一目标的最佳方法,但我之前没有做过类似的事情,因此我会感谢任何人提供的建议或指导。我主要习惯于 GCP,因此我将我的想法集中在可用的工具上(这也很可能在 JS/Node 环境中完成)。

我对设计的初步想法如下。

使用 Cloud Scheduler 创建每小时运行的作业 云调度器触发云函数 Cloud 函数检索所有必要的行并在主题中为每一行创建发布/订阅消息。 Cloud pub sub 然后触发另一个云函数,该函数调用外部 API 并为该行执行其他任务,并将数据写回需要去的地方。 鳍

我让第一个函数将项目添加到队列的理由显然是云函数受执行时间和内存的限制,所以我认为让一个函数尝试自己处理所有行并不谨慎。我的假设是 pub/sub 每次都会触发一个新的函数实例而不是覆盖第一个实例?

我认为理论上我可以批处理一些外部 API 调用,一次最多大约 20 个,所以我不知道这是否会/应该对上述设计产生影响。

我显然也希望这个成本尽可能低,所以我不知道有一个应用引擎实例这样做是否会更好?但是我也不知道我是否会在那里遇到内存和超时问​​题。

我在写这篇文章时想到的一个想法是我是否可以按原样批量处理。来自 JS 背景,我可以创建所有批处理 API 调用并在 Promise.all() 调用中执行它们。再次不确定它对内存和性能的影响,所以我想我需要对其进行测试。

有没有人注意到上面的任何漏洞,或者有更好的解决方案吗?

谢谢

【问题讨论】:

【参考方案1】:

您设计的第一部分是正确的(Cloud Scheduler -> CLoud Functions -> PubSub 中的消息)。

在这里,每条消息都会调用 Cloud Functions。 IMO,这不是最佳选择,因为您一次只能处理 Cloud Functions 实例上的 1 个请求。如果您执行外部 API 调用,您将无所事事地浪费时间(您将等待答案,什么都不做)。

更好的解决方案是使用管理并发请求的产品,例如Cloud Run 或App Engine。使用 Cloud Run,您最多可以有 250 个并发请求,但使用 App Engine 时只能有 80 个。

通过使用这种解决方案,您将节省大量金钱,从而节省时间。


关于批处理,我不确定。

如果您可以在 1 个请求中向外部 API 发送 20 条消息中包含的 20 个值,是的,最好对请求进行批处理(在您的第一个 Cloud Functions 中创建 20 条消息的块) 如果你继续一个一个地发送请求,但你使用语言的并发能力(Node 或 Go 很少这样做),与一个一个地处理消息相比,没有真正的优势。

事实上,您会减少调用次数(但这真的很便宜),相反,会增加代码的复杂性。不确定是否值得。


编辑 1

事实上,PubSub 不会生成任何 Cloud Run 实例。 PubSub 订阅仅将消息推送到 URL。 PubSub 的工作到此结束。

现在,在 Cloud Run 端,服务根据 HTTP 流量进行扩展。因此,平台选择创建 1 个、2 个或更多实例来吸收流量。在您的情况下,该平台将创建大量实例(我认为大约 100 个),但您只需为实例处理流量付费。不处理请求,不计费。

您还可以使用 max instance 参数限制 Cloud Run 上的并行实例数。有了它,您可以限制成本,也可以限制处理能力。

现在,关于延迟,当然有不同的来源。

    当您在 PubSub 中发布消息时,在第一次创建和第 10k 次之间存在“延迟”。 每次Cloud Run平台创建一个新的实例,该实例都需要启动并初始化其运行环境(称为冷启动);根据您的开发语言和设计,它可能需要几毫秒(大约 200 - 500)或几秒钟(例如使用 Spring Boot)。您可以想象使用最小实例功能来保持多个实例的温度,从而限制 clod 启动的数量。但是,对于每小时 1 次运行,此功能对您来说可能过于昂贵(IMO,我不会推荐此功能) 在同一实例上,如果您同时处理 250 个请求,它们必须共享相同的 CPU 资源,并且某些请求将等待获得 CPU 时间来处理它们。您可以增加 CPU 的数量来减少这种延迟(例如设置 4vCPU),但这是任何多线程系统的正常行为。

【讨论】:

感谢您的回复。在我阅读了一些内容之后,我将研究 cloud run 并接受答案。干杯 嘿,我已经接受了这个答案,但我还有一个与之相关的问题,我不知道是否要对该问题进行编辑。你说第一部分是正确的,但理论上云运行不能撤回它需要的所有行吗?这个问题是延迟和发布订阅在需要时产生新实例吗?而不是将所有请求强制到可能运行的“一个”云运行实例上(欣赏它可以处理 250 个请求)。如果您可以更新您的答案以解释为什么这是最好的行动方案的一些原因,那就太好了! 不确定是否理解了您的问题,但我用有关平台行为的一些信息更新了我的答案。

以上是关于每小时调用外部 API 并单独执行约 10000 行任务的作业的主要内容,如果未能解决你的问题,请参考以下文章

无法对没有元素的张量执行约简函数 min,因为该操作在 THCTensorMathReduce.cu:64 处没有标识

如何每 1 小时自动获取一个外部 api?

减少对外部 API 的身份验证调用(Laravel 5.6)

将 Spark Dataframe 中的多个列发送到外部 API 并将结果存储在单独的列中

Celery + Redis - .get()在平稳运行约70个小时后无限期挂起

超过 Instagram API 请求限制