无服务器SQS使用者跳过消息

时间:2018-12-27 02:08:44

标签: 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;
}

消费者功能配置如下:

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

如果我删除await函数,它将处理所有消息。如果我将超时时间增加到200ms,则更多消息将直接进入运行状态,并从那里进入死信队列。这段代码很简单。有什么想法为什么它会跳过一些消息吗?使用第一个console.log()语句,甚至不会消耗掉的消息也不会显示在日志中。他们似乎被完全忽略了。

1 个答案:

答案 0 :(得分:2)

我发现了问题所在。 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 Timeout延长至5分钟。 Lambda函数有足够的时间处理队列中的大多数消息,而失败的消息则在看不见的情况下等待。 5分钟后,它们被推回队列,并且由于Lambda函数不再忙碌,它将处理其中的大多数。其中一些必须先进行两次隐身处理才能成功处理。

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

我希望这对某人有帮助。