无报错但已成功处理的SQS消息仍被移入DLQ问题咨询
SQS成功处理消息被移入DLQ排查思路(基于sqs-consumer实现)
所有排查点优先针对生产环境独有、本地无法复现的场景:
- 先开启sqs-consumer全量debug日志,补抓库层逻辑错误
给生产消费实例加环境变量DEBUG=sqs-consumer:*,同时监听消费实例的所有内置事件打日志,包括error/timeout_error/processing_error/message_processed/message_deleted。绝大多数业务日志无报错的场景,都是库内部执行DeleteMessage(消息删除确认)时出的错,这类错误不会进入业务逻辑日志,常见情况包括:- 消费实例绑定的IAM角色/AKSK缺少源队列的
sqs:DeleteMessage权限,或者被账号下的SCP策略、权限边界拦截了删除请求 - 业务代码或接入的中间件意外修改了消息对象的
receiptHandle字段,导致删除请求传参无效被AWS拒绝 - 配置的
handleMessageTimeout阈值小于生产环境实际任务执行时长,库层面提前判定处理超时触发重投,此时业务逻辑仍在后台执行至成功,不会打印业务错误日志
- 消费实例绑定的IAM角色/AKSK缺少源队列的
- 核对可见性超时与任务执行时长的匹配度
先从DLQ取异常消息,查看自带的ApproximateReceiveCount属性:如果数值刚好等于DLQ配置的maxReceiveCount阈值,说明是消息未被及时删除导致重复投递累计到阈值进的DLQ,优先核查:- 源队列配置的
Visibility Timeout是否小于生产环境任务的99分位执行时长。本地测试数据量小任务执行快不会触发问题,生产环境数据量上涨后任务执行时间变长,超过可见性超时后SQS会自动把消息重新投递给其他消费者,原消费者执行完任务发删除请求时,会因为ReceiptHandle已经失效删除失败,多次重投累计到阈值就会进入DLQ - 如果是长任务场景,是否配置了
heartbeatInterval参数自动续期消息可见性,没开的话哪怕总超时设置足够长,也会因为没定期续期触发重投
- 源队列配置的
- 排查生产环境网络层对删除请求的拦截
检查消费端到SQS服务的链路上有没有代理、WAF、VPC端点策略拦截DeleteMessage请求:比如生产环境为了安全加了出站流量管控,只允许发消息、拉消息的请求,拦截了删除请求;或者网络偶发闪断导致删除请求丢包,sqs-consumer默认不会对失败的删除请求做重试,直接导致消息未被确认。 - 核对DLQ关联与阈值配置
确认生产环境源队列关联的DLQ没有配错,maxReceiveCount阈值不要设得过低(比如设为1的话,只要出现一次网络抖动导致删除失败、消息重投,就会直接进入DLQ),生产环境建议根据任务重试容忍度设为3-5。 - 排查非消费逻辑的消息转移操作
确认有没有其他运维脚本、服务账号有权限调用SendMessage往DLQ写数据,或者配置了错误的队列重定向规则,导致消息被异常移入DLQ。
内容的提问来源于stack exchange,提问作者Subburaj
相关产品推荐
相关产品推荐

