无服务器 SQS 消费者跳过消息

pko*_*out 2 amazon-sqs serverless

我正在使用无服务器框架来使用来自 SQS 的消息。发送到队列的一些消息不会被消耗。他们直接进入飞行中的 SQS 状态,然后从那里进入我的死信队列。当我查看消费者日志时,我可以看到它消费并成功处理了 9/10 消息。一个总是不会被消耗并最终进入死信队列。我设置reservedConcurrency为 1,以便一次只有一个消费者可以运行。函数消费者timeout设置为 30 秒。这是消费者代码:

module.exports.mySQSConsumer = async (event, context) => {
  context.callbackWaitsForEmptyEventLoop = false;

  console.log(event.Records);

  await new Promise((res, rej) => {
    setTimeout(() => {
      res();
    }, 100);
  });

  console.log('DONE');

  return true;
}
Run Code Online (Sandbox Code Playgroud)

消费者功能配置如下:

functions:
  mySQSConsumer:
    handler: handler.mySQSConsumer
    timeout: 30 # seconds
    reservedConcurrency: 1
    events:
      - sqs:
          arn: arn:aws:sqs:us-east-1:xyz:my-test-queue
          batchSize: 1
          enabled: true
Run Code Online (Sandbox Code Playgroud)

如果我删除该await功能,它将处理所有消息。如果我将超时增加到 200 毫秒,更多的消息将直接进入运行中状态,并从那里进入死信队列。这段代码非常简单。知道为什么它会跳过一些消息吗?使用第一条语句时,未使用的消息甚至不会显示在日志中console.log()。他们似乎完全被忽视了。

pko*_*out 6

我解决了这个问题。SQS队列Lambda函数事件触发的工作方式与我想象的不同。消息被推送到 Lambda 函数中,而不是由它拉取。我认为 AWS 可以对此进行更好的设计,但事实就是如此。

问题是设置Default Visibility Timeout为 30 秒以及Reserved Concurrency设置为 1。当 SQS 队列很快被数千条记录填满时,AWS 开始以比单个函数更快的速度将消息推送到 Lambda 函数。实例可以处理它们。AWS“假设”它可以简单地启动更多 Lambda 实例来跟上背压。但是,并发限制不允许它启动更多实例 - Lambda 函数受到限制。因此,该函数开始向 AWS 后端返回某些消息的失败,从而将失败的消息隐藏 30 秒(默认设置),并在此期限后将它们放回队列中进行重新处理。由于单个实例需要处理的记录太多,30秒后,Lambda函数仍然很忙,无法再次处理这些消息。于是,这种情况又重演了,消息又恢复为不可见状态,持续 30 秒。这总共重复 3 次。第三次尝试后,消息将进入死信队列(我们以这种方式配置了 SQS 队列)。

为了解决这个问题,我们将时间增加到Default Visibility Timeout5 分钟。这足够 Lambda 函数处理队列中的大部分消息,而失败的消息则在不可见的状态下等待。5 分钟后,它们被推回到队列中,并且由于 Lambda 函数不再繁忙,因此它将处理其中的大部分。其中一些必须经过两次隐身才能被成功处理。

因此,解决此问题的方法是像Default Invisibility Timeout我们一样增加或增加消息进入死信队列之前所需的失败次数。

我希望这可以帮助别人。