以较短的启动时间运行大型作业并自动缩放以应对突发流量

问题描述

对于我在 AWS 上开发的网站,用户可以提交大型作业(例如,选择大量项目并要求以某种方式更新所有项目)。我们不想限制这些用户提交的作业的大小,因此该作业理论上可以运行很长时间并需要大量内存(这排除了 AWS Lambda 作为计算引擎选项) .我们希望作业尽可能相互独立,因此我们选择在 Amazon ECS 的自己的容器中运行每个作业。当用户提交作业请求时,我们当前所做的是向 SQS 队列发送带有作业 ID/引用的消息,让 AWS lambda 轮询该队列,并在收到消息后,lambda 启动 ECS 任务(SQS -> Lambda -> ECS)。这有一个问题,即每个请求都会启动一个新的 ECS 任务,因此必须启动一个新容器,这可能需要几分钟时间。这种延迟对用户来说是直接可见的,如果用户的作业甚至不是特别大但他们仍然等待几分钟以等待容器启动,则这种延迟尤其不可接受。此外,持续运行一两个容器的成本不会太成问题。

我一直在琢磨更新这个流程的一些想法。

尝试 1: 在这个更新的流程中,我们将创建一个如下所示的 ECS 任务:

message = null; 
while (message == null) { 
    message = pollForMessages(); 
} 
processMessage(message); 
// task finishes,and container can be brought down 

我们从流中删除了 lambda,只有 SQS -> ECS 而不是 SQS -> Lambda -> ECS。在这种情况下,假设容器正在为消息旋转,则不会有冷启动。我们可以将我们想要运行的最小任务数设置为 > 0 的数字,以确保在某个时刻处理所有消息。然而,这存在一个问题,即随着队列中消息数量增加,它不会自动扩展。因此,当流量增加时,需要生成更多容器。

尝试 2: 在这个更新的流程中,我们将创建一个如下所示的 ECS 任务:

message = null; 
while (message == null) { 
    message = pollForMessages(); 
} 
If (number of running tasks < number of messages in queue) {
    spawnMoreContainers();
}
processMessage(message); 
// task finishes,and container can be brought down

随之而来的问题是,如果多个容器看到队列中的消息多于正在运行的任务,我们最终可能会过度配置容器。由于这些任务永远运行直到消息被处理,这可能会导致大量不必要的成本。它也可以在配置容器下 - 如果任务看到正在运行的任务数 >= 消息数,但这些正在运行的任务已经忙于处理消息,这些任务最终不会从队列中取出这些消息之一,我们可能会最终需要等待很长时间才能处理的消息。

尝试 3:

message = null; 
while (message == null) { 
    message = pollForMessages(); 
    If (# of containers > min provisioned && this particular container has been running longer than some timeout) {
        // finish this task so this container can be brought down
        return;
    }
} 
If (number of running tasks < number of messages in queue) {
    spawnMoreContainers();
}
processMessage(message); 
// task finishes,and container can be brought down

虽然与尝试 2 相比,这可能会为我们节省一些成本,因此过度配置不会是一个太大的问题,但仍有可能我们可能会配置容器不足,在这种情况下,某些作业请求需要等待可能需要很长时间才能被处理。

尝试 4: 我们可以引入锁定(例如 https://aws.amazon.com/blogs/database/building-distributed-locks-with-the-dynamodb-lock-client/)来缓解一些竞争条件,但是我们总会遇到这样一个问题,即正在运行的任务并不一定意味着可以接收消息的任务,而 Fargate 为我们提供了无法区分这些,这使得很难确定要配置多少个容器(例如,我们看到有 5 个正在运行的容器和 5 个消息,但我们不知道是否要配置更多容器,因为我们没有知道这些容器是否已经在处理消息或者它们是否正在等待)。或者,我们可以引入一些机制,例如外部编排器或容器内的一些逻辑和一些数据存储,以管理这些容器的状态。

从本质上来说,要处理这些问题中的每一个,架构会变得越来越复杂,而且实现起来会很困难且容易出错。 在我看来,这些解决方案也在重新发明轮子,我觉得一定有一些服务已经解决了这个问题,但我似乎找不到。

我看到的解决这个问题的建议是:

  • 也许 AWS 批处理更适合此用例 - 实际上,对于此类工作负载,AWS 批处理可能是更推荐的方法,但是,我们不会通过以下方式消除任何冷启动问题交换。 AWS 批处理仍会为每个作业创建一个新容器。
  • 在 EC2 而不是 Fargate 上运行 ECS 任务,然后在主机上缓存容器映像 - 这样,我们将管理自己的基础架构,理想情况下,我们希望它是无服务器的.
  • 对队列中的消息数量发出警报,并让该警报触发一个 lambda,然后启动更多容器 - /AWS 日志组上的警报的最短周期为 1 分钟。这意味着在我们收到的请求超出了我们配置的容器可以处理的请求一分钟后,才会​​触发警报。此外,我们还必须设置许多警报,以适应不同数量的消息。

我想知道是否有人知道可以使这样做更可行的潜在服务/框架?或者是否有人对替代架构有任何建议?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...