Lambda与SQS触发器执行15次后停止触发问题排查
问题分析与解决
核心问题
你遇到的现象本质是原始SQS消息因Lambda未正确确认处理完成,被重复重试达10次后移入DLQ,而你观察到的“第16次发送的续跑消息”与原始消息的重试轨迹重叠,导致出现“消息飞行中但无法被拾取”的错觉。
原因拆解
- 消息确认机制缺失:
当Lambda因超时/主动终止退出时,SQS不会自动删除当前处理的原始消息——只有当Lambda正常返回(无异常、无超时),SQS才会删除该批次的消息。你的逻辑是发送续跑消息后终止实例,导致原始消息未被确认删除,在可见性超时后重新回到队列,被Lambda反复拾取。 - 重试次数累积触发DLQ规则:
SQS触发器默认配置了10次重试限制,当原始消息被重复拾取10次仍未被确认删除时,会被自动移入DLQ。你发送的第16次续跑消息,大概率是原始消息第10次重试时生成的,此时原始消息已进入DLQ,续跑消息则因之前的重试逻辑被标记为飞行状态,最终也因重复重试触发DLQ规则。
解决办法
1. 手动确认删除原始消息
在发送续跑消息前,调用SQS的delete_message接口删除当前处理的原始消息,避免其重复进入队列:
import json import boto3 import time from datetime import datetime sqsClient = boto3.client('sqs') SQS_URL = "https://sqs.ap-south-1.amazonaws.com/YOUR_ACCOUNT_NUMBER/test-sqs" def lambda_handler(event, context): receipt_handle = None if ("Records" in event) and (len(event["Records"]) > 0): print("Trigger through SQS.") for record in event["Records"]: event = json.loads(record["body"]) receipt_handle = record['receiptHandle'] # 保存消息的receipt handle else: print("Triggered manually.") print(event) start_time = datetime.utcnow() print("start", start_time) time.sleep(1) segment_number = 1 if "segment_number" in event: segment_number = event["segment_number"] if segment_number <= 20: segment_number += 1 payload = { "segment_number" : segment_number } # 先删除原始消息,再发送续跑消息 if receipt_handle: sqsClient.delete_message(QueueUrl=SQS_URL, ReceiptHandle=receipt_handle) sqsClient.send_message(QueueUrl = SQS_URL, MessageBody=json.dumps(payload)) else: # 任务完成时,也要删除原始消息 if receipt_handle: sqsClient.delete_message(QueueUrl=SQS_URL, ReceiptHandle=receipt_handle) print("COMPLETED") print("end", datetime.utcnow())
2. 优化Lambda终止逻辑
避免强制终止Lambda实例(如调用exit()或抛出异常),确保发送续跑消息和删除原始消息的操作完成后,让Lambda正常返回。强制终止会导致SQS认为消息处理失败,触发不必要的重试。
3. 调整SQS触发器配置(可选)
如果业务需要保留原始消息的重试能力,可以在Lambda的SQS触发器配置中,调整重试尝试次数(默认10次),但这只是临时缓解,无法解决根本的重复消息问题。
复现场景验证
在你的复现代码中,Lambda超时20秒、SQS可见性超时30秒,若Lambda正常返回则消息会被自动删除;但如果Lambda因超时退出,原始消息会在30秒后回到队列,重复触发Lambda发送续跑消息,最终因10次重试进入DLQ。添加手动删除逻辑后,即可避免原始消息重复进入队列。
内容的提问来源于stack exchange,提问作者Ranjith kumar
相关产品推荐
相关产品推荐

