使用死信队列隔离失败任务

AWSBeginner
立即练习

介绍

一个无效订单任务每次被工作程序处理都会失败。你将限制其尝试次数,将它保留在独立队列中供调查,并确认健康订单仍然能够完成。

请先完成 处理可见性超时与再次投递 及其前置引导实验。这个独立 VM 提供工作程序和空订单表;队列、消息和消费者连接由你完成。

将源队列连接到死信队列

本步骤中,创建两个空的 Standard 队列,并在源队列上配置具有次数上限的 redrive。

死信队列(DLQ)保留超过源队列领取次数上限的任务。使用 Terminal 旁的 AWS View 对比两个队列、实际尝试和存储的订单;保留参考数据。

cd /home/labex/project

创建失败任务的目标队列,并保存其队列地址:

DEAD_URL=$(aws sqs create-queue --queue-name labex-q03-dead --query QueueUrl --output text)

redrive 策略引用目标的 ARN,也就是它的服务资源标识符,而不是队列 URL。选择该 ARN:

DEAD_ARN=$(aws sqs get-queue-attributes --queue-url "$DEAD_URL" --attribute-names QueueArn --query Attributes.QueueArn --output text)

写入一个小型 JSON 策略。shell 会将目标 ARN 插入 $DEAD_ARN;maxReceiveCount 允许两次投递尝试,之后继续领取会将消息移入 DLQ。

cat > redrive-policy.json <<EOF
{
  "deadLetterTargetArn": "$DEAD_ARN",
  "maxReceiveCount": 2
}
EOF

SQS 队列属性将 redrive 策略表示为另一个 JSON 文档中的 JSON 字符串。--rawfile 将策略文件读取为该字符串;> 写入属性文件。

jq -n --rawfile policy redrive-policy.json '{VisibilityTimeout:"30",RedrivePolicy:$policy}' > queue-attributes.json

使用这些属性创建源队列:

QUEUE_URL=$(aws sqs create-queue --queue-name labex-q03-jobs --attributes file://queue-attributes.json --query QueueUrl --output text)
aws sqs get-queue-attributes --queue-url "$QUEUE_URL" --attribute-names QueueArn VisibilityTimeout RedrivePolicy

预期可见性超时为 30,策略指向 labex-q03-dead,领取次数上限为 2。保存源队列 ARN,供消费者连接使用:

QUEUE_ARN=$(aws sqs get-queue-attributes --queue-url "$QUEUE_URL" --attribute-names QueueArn --query Attributes.QueueArn --output text)

AWS View 显示两个空队列,没有工作程序执行记录或订单。redrive 策略本身不会处理消息;必须由消费者领取消息。

连接消费者并证明健康任务正常处理

本步骤中,连接 Lambda 事件源映射,并验证有效任务的实际处理结果。

队列通过映射连接工作程序

映射轮询队列并调用工作程序;处理成功后,它才能确认消息。

事件源映射(event-source mapping)将源队列连接到 Lambda 消费者。它轮询消息,将消息作为 SQS Records 事件传递,并删除成功处理的消息。提供的执行角色只拥有源队列的领取和删除权限、订单表写入权限及日志权限。工作程序拒绝 1–10 范围之外的数量。代码和权限是配套资源;你的任务是连接队列并隔离失败。

创建批大小为一的映射,让每次尝试只有一个任务需要检查:

MAPPING_ID=$(aws lambda create-event-source-mapping --function-name labex-q03-worker --event-source-arn "$QUEUE_ARN" --batch-size 1 --enabled --query UUID --output text)

读取连接:

aws lambda get-event-source-mapping --uuid "$MAPPING_ID" --query '{Source:EventSourceArn,Function:FunctionArn,Batch:BatchSize,State:State}'

预期看到 labex-q03-jobs 的源 ARN、函数 labex-q03-worker、批大小 1 和状态 Enabled。映射存在只是配置证据;实际存储的订单才能证明处理成功。

发送一个健康任务:

aws sqs send-message --queue-url "$QUEUE_URL" --message-body '{"id":"good-order","quantity":2}'

观察 AWS View,直到工作程序返回结果且订单表显示 good-order。处理是异步的;请给消费者短暂的处理时间,不要手动领取这条消息。然后读取订单:

aws dynamodb get-item --table-name labex-q03-orders --key '{"id":{"S":"good-order"}}' --consistent-read --query Item

预期数量为 2,总金额为 600。检查两个队列:

aws sqs get-queue-attributes --queue-url "$QUEUE_URL" --attribute-names ApproximateNumberOfMessages ApproximateNumberOfMessagesNotVisible
aws sqs get-queue-attributes --queue-url "$DEAD_URL" --attribute-names ApproximateNumberOfMessages

两个队列都应为空:工作程序完成了业务写入,消费者确认了成功的任务。健康消息不应进入 DLQ。

观察有限重试与失败隔离

本步骤中,发送一个无效任务,观察实际处理失败的尝试,然后由原生 redrive 将它放入 DLQ。

有限失败后进入 DLQ

本实验的领取次数上限为二,因此之后的领取会将失败任务移入 DLQ,而不是第三次调用工作程序。

毒消息(poison message)因数据或处理逻辑而反复失败。发送一个数量为零的模拟无效任务:

aws sqs send-message --queue-url "$QUEUE_URL" --message-body '{"id":"poison-order","quantity":0}'

AWS View 显示工作程序的失败尝试。消息在可见性到期之前保持处理中,之后消费者可以再次领取。观察尝试记录和队列计数,直到 DLQ 中有一个可用任务。由于窗口为 30 秒、尝试次数为两次,请等待大约一分钟,再加上处理时间。消费者运行期间,不要手动领取或删除任务;那会改变领取次数和实验结果。

两次失败尝试具有相同消息 ID,领取次数分别为 1 和 2。不会出现 poison-order 的存储订单。达到领取次数上限后,后续原生领取会将消息移出源队列,而不是第三次调用函数。

通过 CLI 确认队列状态:

aws sqs get-queue-attributes --queue-url "$QUEUE_URL" --attribute-names ApproximateNumberOfMessages ApproximateNumberOfMessagesNotVisible
aws sqs get-queue-attributes --queue-url "$DEAD_URL" --attribute-names ApproximateNumberOfMessages ApproximateNumberOfMessagesNotVisible

预期源队列中可用任务和处理中的任务均为零,DLQ 中有一个可用任务。AWS View 显示它的原始正文。检查实际工作程序日志中的无效数量与失败信息:

aws logs filter-log-events --log-group-name /aws/lambda/labex-q03-worker --query 'events[].message'

读取全部订单项目:

aws dynamodb scan --table-name labex-q03-orders --query Items

只保留 good-order/2/600。失败已被隔离,没有丢弃消息或写入无效订单。移入 DLQ 不代表修复,也不是成功的业务结果;后续实验与项目挑战将介绍任务恢复和去重。

AWS View 示例:两次失败领取后,毒任务保留在 DLQ 中,只有健康订单被存储。

移除连接和自己创建的资源

本步骤中,在删除队列和结果之前,先停止你的消费者连接。

先移除事件源映射:

aws lambda delete-event-source-mapping --uuid "$MAPPING_ID" --query UUID --output text

删除两个临时队列。这也会丢弃 DLQ 中保留的模拟毒任务:

aws sqs delete-queue --queue-url "$QUEUE_URL"
aws sqs delete-queue --queue-url "$DEAD_URL"

移除健康订单和本实验的执行日志:

aws dynamodb delete-item --table-name labex-q03-orders --key '{"id":{"S":"good-order"}}'
aws logs delete-log-group --log-group-name /aws/lambda/labex-q03-worker

通过成功的 API 响应检查资源不存在且参考资源仍保留:

aws lambda list-event-source-mappings --function-name labex-q03-worker --query EventSourceMappings
aws sqs list-queues
aws dynamodb scan --table-name labex-q03-orders --query Items
aws dynamodb scan --table-name labex-q03-reference --query Items

映射和订单列表为空,不再有队列 URL,参考项目仍包含 keep unchanged。提供的工作程序和表结构仍然保留。AWS View 显示相同的资源状态。身份验证或网络失败不能证明删除成功。

移除普通的本地策略文件:

rm -f redrive-policy.json queue-attributes.json

结束环境之前,运行清理检查。

总结

你通过有限的领取次数上限将 SQS 源队列连接到 DLQ,附加 Lambda 消费者,并验证了健康订单的存储结果。你观察到无效任务失败两次后移入 DLQ,没有产生业务写入,随后移除了自己的映射、队列、结果和日志,同时保留提供的资源。