我正在使用无服务器框架来使用来自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()
语句,甚至不会消耗掉的消息也不会显示在日志中。他们似乎被完全忽略了。
答案 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
或增加消息进入死信队列之前必需的失败次数。
我希望这对某人有帮助。