必须先用GetQueueUrlAsync获取真实队列URL,而非手拼;接收后须用ReceiptHandle调用DeleteMessageAsync删除,不可用MessageId;轮询应设WaitTimeSeconds节制,Lambda需用SQSEvent参数正确解析。

用 AwsSdkNet 发送 SQS 消息前,必须确认队列 URL 是否有效
很多人卡在第一步:调用 SendMessageRequest 报错 InvalidAddress 或 NotFound。这不是代码问题,而是没拿到真实队列 URL——它不是控制台里看到的“队列名称”,而是形如 https://sqs.us-east-1.amazonaws.com/123456789012/my-queue 的完整地址。你得先用 CreateQueue 或 GetQueueUrl 获取它,不能手写拼接。
- 推荐用
sqsClient.GetQueueUrlAsync(new GetQueueUrlRequest { QueueName = "my-queue" })动态获取,避免硬编码区域和账号 ID - 若跨区域访问,
AmazonSQSClient初始化时必须指定对应RegionEndpoint,否则会默认走 us-east-1,导致 404 - 消息体大小超 256KB?别硬塞——要么启用 S3 托管(用
AmazonSQSExtendedClient),要么提前拆分或压缩
ReceiveMessage 后不删消息,等于没处理
收到消息只是“暂取”,不是“消费完成”。SQS 默认保留消息(VisibilityTimeout 决定多久不可见),如果没显式调用 DeleteMessage,消息会在超时后重新入队,造成重复处理。这是生产环境最常被忽略的逻辑断点。
- 务必在业务逻辑执行成功后,用
message.ReceiptHandle调用DeleteMessageAsync;失败则跳过删除,让消息重试 - 不要用
message.MessageId删除——它无效,只有ReceiptHandle是本次接收的唯一凭证 - 批量接收(
MaxNumberOfMessages > 1)时,每个消息要单独删,不能共用一个 ReceiptHandle
用 Polling + Delay 实现可靠轮询,别用死循环
官方 SDK 不提供“监听式”长连接 API,C# 端只能靠轮询。但直接 while(true) + Thread.Sleep(100) 会浪费 CPU、压垮队列、触发限流。正确做法是结合 VisibilityTimeout 和 WaitTimeSeconds 做节制拉取。
- 设置
WaitTimeSeconds = 20(最长支持 20 秒),让请求挂起等待新消息,降低空轮询频次 - 每次拉取后检查
Messages.Count == 0,再 Sleep 1–5 秒,避免密集探测 - 建议封装成
async Task PollAndProcessAsync(),配合CancellationToken支持优雅退出
Lambda 触发 SQS 时,C# 函数签名必须匹配事件结构
如果你把 C# 函数部署为 Lambda 并配置 SQS 触发器,SDK 自动生成的事件类型不是原始 Message,而是 SQSEvent。直接反序列化原始 Body 会丢掉 ReceiptHandle 和批处理元信息,导致无法删除。
- 函数入口应为
public async Task FunctionHandler(SQSEvent ev, ILambdaContext context) - 每条
ev.Records[i].Sns.Message或ev.Records[i].Body是字符串,需手动 JSON 反序列化业务数据 - 删除动作由 Lambda 自动完成(只要没抛异常),但若需自定义重试策略,得改用
EventSourceMapping并禁用自动删除
C# 处理 SQS 最容易栽在“以为收了就是完了”,其实从 URL 获取、ReceiptHandle 生命周期、轮询节奏到 Lambda 事件包装,每层都有隐性契约。漏掉任意一环,轻则重复消费,重则消息堆积雪崩。










