抛出自定义idempotentexception是推荐做法,需继承runtimeexception、携带idempotencykey等结构化字段,配合全局处理器返回409并记录链路追踪。

在 Java 中,幂等性校验失败时抛出自定义的 IdempotentException 是一种清晰表达业务语义、便于统一处理和日志追踪的做法。关键不是简单 throw 一个异常,而是让这个异常可识别、可捕获、可记录,并与幂等上下文(如请求 ID、操作类型、时间戳)关联。
定义 IdempotentException 类
继承 RuntimeException(非检查异常,符合幂等校验通常发生在业务逻辑入口或拦截器中的场景),并提供带业务上下文的构造方法:
- 包含请求唯一标识(如
idempotencyKey)、操作类型(如createOrder)、已存在记录 ID 等字段 - 重写
toString()或提供 getter 方法,方便日志打印和监控提取 - 避免仅用 message 字符串承载关键信息,结构化数据更利于排查
public class IdempotentException extends RuntimeException {
private final String idempotencyKey;
private final String operationType;
private final Long existingRecordId;
public IdempotentException(String idempotencyKey, String operationType) {
this(idempotencyKey, operationType, null);
}
public IdempotentException(String idempotencyKey, String operationType, Long existingRecordId) {
super("Idempotent check failed for key: " + idempotencyKey + ", operation: " + operationType);
this.idempotencyKey = idempotencyKey;
this.operationType = operationType;
this.existingRecordId = existingRecordId;
}
// getter 方法略
}
在幂等校验逻辑中主动抛出
校验通常发生在 Controller 入口、Service 方法开头或自定义 AOP 切面中。一旦发现重复请求(如 Redis 中已存在对应 key,或数据库已存在相同幂等标识的记录),立即抛出:
- 校验失败时,应先完成必要清理(如释放临时锁)再抛异常,避免状态不一致
- 建议将幂等 key 和操作类型作为参数传入校验方法,方便异常构造时复用
- 不要吞掉原始校验失败原因(如 Redis 连接异常),那是另一类问题;幂等异常只表示“该请求已被成功处理过”
public void createOrder(OrderRequest request) {
String idempotencyKey = request.getIdempotencyKey();
String opType = "createOrder";
if (idempotentChecker.isDuplicate(idempotencyKey, opType)) {
Long existedId = idempotentChecker.getExistingRecordId(idempotencyKey);
throw new IdempotentException(idempotencyKey, opType, existedId);
}
// 执行实际业务...
}
全局异常处理器统一响应
使用 @ControllerAdvice 捕获 IdempotentException,返回标准 HTTP 状态码(如 409 Conflict)和结构化 JSON:
- 状态码选
409 Conflict更准确(表示请求与当前资源状态冲突),比400 Bad Request更具语义 - 响应体中显式携带
idempotencyKey和existingRecordId,前端或调用方可据此直接获取结果 - 记录完整异常堆栈 + 关键字段到日志系统(如 ELK),便于按 key 快速检索历史请求
配合中间件增强可观测性
单纯抛异常还不够。建议在抛出前或异常处理器中做两件事:
- 向分布式链路追踪系统(如 SkyWalking、Pinpoint)打点,标记该请求为“幂等拒绝”,便于大盘统计重复率
- 触发告警(如企业微信/钉钉机器人)仅当单位时间内某 key 频繁触发幂等异常,可能暴露客户端重试逻辑缺陷
- 对高频幂等 key 做采样日志(如每千次记录一次),避免日志爆炸
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











