AWS SQS队列触发Lambda重复执行问题排查求助
SQS触发Lambda重复执行问题排查与解决
我有一个服务调用AWS SQS队列触发Lambda执行,但偶尔会出现同一队列消息触发Lambda两次的异常情况。Lambda日志显示首次调用带有MessageDeduplicationId字段,第二次调用时该字段消失,问题并非每次发生,但必须彻底解决重复执行的问题。
消息发送代码
import aws from 'aws-sdk'; import { v4 as uuidv4 } from 'uuid'; import * as awsConfig from '../config/aws'; const accessKeyId = awsConfig.accessKeyId; const secretAccessKey = awsConfig.secretAccessKey; const region = awsConfig.generalRegion; class SqsMessenger { sqs: aws.SQS; constructor() { this.sqs = new aws.SQS(); } send(body: any, queueUrl: string, groupId?: string) { const params: aws.SQS.Types.SendMessageRequest = { MessageBody: JSON.stringify(body), MessageGroupId: groupId || uuidv4(), MessageDeduplicationId: uuidv4(), QueueUrl: queueUrl, }; return new Promise((resolve, reject) => { this.sqs.sendMessage(params, (error: aws.AWSError, data: aws.SQS.SendMessageResult) => { if (error) { return reject(error); } resolve(data); }); }); } } export const sqs = new SqsMessenger();
问题根源分析
- 队列类型与去重字段不匹配:
MessageDeduplicationId仅对SQS FIFO队列生效,如果你的队列是标准队列,该字段不会产生任何去重效果。SQS标准队列因分布式架构特性,可能出现消息重复存储的情况,进而触发Lambda多次执行。 - 消息未正确确认:Lambda执行超时、抛出未捕获异常时,SQS会将消息重新放回队列,导致二次触发。日志中第二次调用无
MessageDeduplicationId,大概率是消息被重新投递时,原去重字段未被保留(标准队列不存储该字段)。 - 随机去重ID的局限性:即使是FIFO队列,用随机UUID生成
MessageDeduplicationId无法针对同一业务请求的重复发送进行去重,若服务端因重试重复发送同一业务消息,SQS会认为是新消息而接收。
解决方案
1. 匹配队列类型配置去重规则
- 使用FIFO队列:
- 确保队列开启FIFO模式,同时可开启
Content-Based Deduplication(自动基于消息内容生成去重ID),或改用业务唯一标识生成MessageDeduplicationId(比如订单ID+时间戳),确保同一业务请求的重复发送在5分钟去重窗口内被拦截。 - 避免随机生成
MessageGroupId,同一业务流的消息使用固定分组ID,保证顺序性的同时避免不必要的分组分散。
- 确保队列开启FIFO模式,同时可开启
- 使用标准队列:
- 标准队列不支持服务端去重,必须在Lambda端实现幂等性:
- 为每个业务消息分配唯一业务ID(如请求ID、订单ID);
- Lambda执行前,先查询数据库/Redis确认该ID是否已处理;
- 已处理则直接返回成功,未处理则执行业务逻辑并标记ID为已处理。
- 标准队列不支持服务端去重,必须在Lambda端实现幂等性:
2. 确保消息处理的可靠性
- 调整Lambda超时时间:设置为小于SQS的
VisibilityTimeout(建议Lambda超时=VisibilityTimeout-5秒),给Lambda足够的处理时间,避免因超时导致消息重投。 - 捕获Lambda所有异常:在代码中添加全局异常处理逻辑,处理失败时可将消息转发至死信队列(DLQ),避免无意义的重复重试。
3. 优化去重ID生成逻辑(FIFO队列适用)
将随机UUID替换为基于业务标识的去重ID,示例:
// 假设body中包含唯一业务ID字段businessId MessageDeduplicationId: `${body.businessId}-${Date.now()}`,
这样能确保同一业务请求的重复发送会被SQS识别并丢弃,从源头避免重复消息。
内容的提问来源于stack exchange,提问作者Ezequiel Jaramillo
相关产品推荐
相关产品推荐

