SNS重试投递耗尽后服务端错误消息未投递至DLQ问题排查
问题根因
你的配置核心问题是SQS死信队列的访问策略不符合AWS默认安全规则,同时你对SNS的错误分类、DLQ触发逻辑存在认知偏差,导致服务端错误场景下消息无法正常投递到DLQ。
修复步骤
- 重写DLQ访问策略,移除公共授权
你当前给DLQ配置的策略将Principal设置为*(允许所有主体访问),属于公共访问策略。目前AWS新账号默认开启SQS公共访问阻止功能,会直接拦截这类无限制的公共授权,导致SNS服务在服务端错误重试耗尽后,无法向DLQ写入消息。
你观察到的「客户端错误时消息可正常入DLQ」是场景误判:4xx类客户端错误(比如Lambda权限被删除、Lambda不存在)发生时,SNS不会重试,会直接通过账号内的服务关联角色投递DLQ,不受公共访问阻止规则影响;而5xx类服务端错误(Lambda代码抛异常、Lambda超时、Lambda服务临时故障)重试耗尽后,SNS会通过公开服务主体投递,就会被安全规则拦截。
直接将模板中的EmailDLQPolicy替换为以下最小权限配置,仅给SNS服务开放DLQ的发送消息权限,同时通过条件限定仅允许你的SNS主题访问,彻底避免公共授权问题:
EmailDLQPolicy: Type: AWS::SQS::QueuePolicy Properties: Queues: - !Ref EmailDLQ PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: sns.amazonaws.com Action: sqs:SendMessage Resource: !GetAtt EmailDLQ.Arn Condition: ArnEquals: aws:SourceArn: !Ref EmailsTopic StringEquals: aws:SourceAccount: !Ref AWS::AccountId
原策略中开放的sqs:GetQueueUrl、sqs:PurgeQueue等权限完全多余,还会带来极高的安全风险,全部移除即可。
- 确认重试策略等待窗口,避免误判
你当前使用SNS默认投递策略,针对Lambda端点的服务端错误默认重试3次,采用1-20秒的指数退避,整个重试窗口约40秒。如果你在测试时(比如发送reject消息触发代码异常),发送后立刻查看DLQ,会因为还在重试流程中看不到消息,属于正常现象,等待重试窗口结束后消息才会投递到DLQ。
如果需要自定义重试逻辑,可以给SNS订阅添加DeliveryPolicy调整重试次数、退避规则,示例配置如下:
EmailsSubscription: Type: AWS::SNS::Subscription Properties: TopicArn: !Ref EmailsTopic Protocol: lambda Endpoint: !GetAtt EmailFunction.Arn RedrivePolicy: deadLetterTargetArn: !GetAtt EmailDLQ.Arn # 可选:自定义重试策略 DeliveryPolicy: healthyRetryPolicy: minDelayTarget: 1 maxDelayTarget: 20 numRetries: 5 numNoDelayRetries: 0 numMinDelayRetries: 0 numMaxDelayRetries: 0 backoffFunction: exponential
额外排查点
- 部署修正后的模板后,到SQS控制台查看DLQ的「访问权限」标签页,确认没有公共访问被拦截的告警。
- 如果你后续给DLQ开启了KMS客户管理密钥加密,需要在密钥策略中给
sns.amazonaws.com服务主体添加kms:GenerateDataKey、kms:Decrypt权限,当前模板未开启KMS加密可忽略。 - 确保SNS订阅和DLQ在同一个AWS区域,跨区域配置会导致DLQ投递失败。
内容的提问来源于stack exchange,提问作者kiritsuku
相关产品推荐
相关产品推荐

