
本文介绍如何在 spring cloud aws 的 `@sqslistener` 中,仅对特定自定义异常(如 `mycustomexception`)触发消息重试,而对其他运行时异常静默处理或记录,避免意外丢消息或无限重试。核心在于结合 `on_success` 删除策略与显式异常捕获控制。
在使用 @SqsListener 消费 SQS 消息时,deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS 表示:仅当监听方法正常返回(无异常抛出)时,消息才被自动从队列中删除;一旦方法抛出任何未捕获的异常,SQS 将保留该消息,并在可见性超时后重新入队,从而触发重试。
但默认行为会导致 所有未捕获异常(包括 NullPointerException、IllegalArgumentException 等编程错误)均触发重试——这不仅违背业务语义(例如空指针不应重试),还可能掩盖真实缺陷,甚至因重复处理导致数据不一致。
✅ 正确做法是:主动捕获所有异常,仅将目标自定义异常重新抛出,其余异常吞掉或记录后静默忽略,确保只有业务明确标识的“可重试失败”才影响消息生命周期。
以下是推荐实现:
@SqsListener(
value = "https://sqs.us-east-1.amazonaws.com/123456789012/MyQueueURL",
deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS
)
public void getMessageFromSqs(MyMessage message) {
try {
log.info("Processing message: {}", message);
if (someCondition(message)) {
throw new MyCustomException("Business rule violated — retry needed");
}
// ✅ 业务逻辑成功执行,方法自然返回 → 消息被自动删除
log.info("Message processed successfully");
} catch (MyCustomException e) {
log.warn("Business-retryable failure occurred, letting SQS handle redelivery", e);
throw e; // ? 关键:仅重抛自定义异常,触发重试
} catch (Exception e) {
// ⚠️ 兜底捕获:记录非预期异常(如 NPE、IOE),但不抛出
log.error("Unexpected error (NOT retryable) — suppressing to avoid unintended redelivery", e);
// 不抛出 → 方法视为“成功完成” → 消息被删除(符合 ON_SUCCESS 语义)
}
}? 关键要点说明:
- ON_SUCCESS 是安全基线:它天然防止“失败消息被误删”,但需配合异常分类逻辑才能实现语义化重试;
- MyCustomException 应继承 RuntimeException(Spring 默认只对 unchecked exception 触发重试),并建议添加 @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) 等语义标记(非必需,便于监控);
- 绝不忽略日志:即使吞掉非自定义异常,也必须 log.error(..., e) 记录完整堆栈,否则将丧失可观测性;
- ⚠️ 风险提示:此模式将“非自定义异常”视为最终失败(fail-fast),消息会被永久删除。因此务必确保 catch (Exception e) 块中不包含本应重试的场景(如临时网络抖动)。如需更精细控制(例如按异常类型配置重试次数),应改用 SqsMessageDeletionPolicy.NEVER + 手动调用 AmazonSQS.deleteMessage(),并配合 @RetryableTopic 或死信队列(DLQ)策略;
- 建议为 MyCustomException 添加唯一错误码和上下文字段(如 messageId, retryCount),便于链路追踪与告警聚合。
综上,通过「显式 try-catch + 精准重抛」,你能在保持 ON_SUCCESS 安全语义的同时,实现真正面向业务的弹性重试设计。








