网站首页 > 博客文章 正文
重复提交、网络抖动、服务重试...接口幂等性问题就像幽灵一样缠绕着后端开发者,稍不留神就造成资金损失、数据混乱。今天教你用一个注解,三行代码彻底解决!
一、痛!幂等性问题有多可怕?
想象这些场景:
- 用户疯狂点击支付按钮,账户被重复扣款
- 订单创建接口因网络抖动被重试,生成了两个相同订单
- 库存服务重试导致商品超卖
传统解决方案的坑:
- 每个接口都写重复校验代码 → 代码冗余
- 数据库加唯一索引 → 无法覆盖所有场景
- 前端按钮置灰 → 防不住网络重试
二、神兵利器:@Idempotent 自定义注解
核心原理:通过AOP + Redis分布式锁,在方法执行前检查唯一请求标识
1. 定义幂等性注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
int expireTime() default 30; // 令牌有效期(秒)
String message() default "请勿重复请求"; // 提示信息
}
2. 实现AOP切面逻辑
@Aspect
@Component
@RequiredArgsConstructor
public class IdempotentAspect {
private final RedisTemplate<String, String> redisTemplate;
@Around("@annotation(idempotent)")
public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) {
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 1. 从Header获取幂等令牌(实际可根据业务调整)
String token = request.getHeader("Idempotent-Token");
if (StringUtils.isEmpty(token)) {
throw new BusinessException("缺少幂等令牌");
}
// 2. 构建Redis Key
String key = "idempotent:" + token;
// 3. 原子性设置令牌(存在则拒绝)
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(key, "processing", idempotent.expireTime(), TimeUnit.SECONDS);
if (Boolean.FALSE.equals(success)) {
throw new IdempotentException(idempotent.message()); // 抛出幂等异常
}
try {
return joinPoint.proceed(); // 执行目标方法
} finally {
// 可选:标记处理完成(根据业务决定是否删除)
// redisTemplate.delete(key);
}
}
}
三、实战:三行代码保护支付接口
改造前(高危!)
@PostMapping("/pay")
public Result payOrder(@RequestBody PayRequest request) {
// 业务逻辑直接执行
paymentService.processPayment(request);
return Result.success();
}
改造后(安全!)
@Idempotent(expireTime = 60, message = "支付请求处理中")
@PostMapping("/pay")
public Result payOrder(@RequestBody PayRequest request) {
// 原有业务逻辑无需修改!
paymentService.processPayment(request);
return Result.success();
}
四、处理流程全解析
客户端 服务端
│ │
│ 1. 生成唯一Token (UUID/雪花ID等) │
│ 2. 发起请求携带Header: Idempotent-Token │
│─────────────────────────────────────────>│
│ │
│ ├─ 3. 检查Redis是否存在该Token
│ │ │
│ │<─┐ 存在 → 返回错误信息
│ │ │
│ │ 不存在 → 写入Redis锁定
│ │
│ ├─ 4. 执行业务逻辑
│ │
│ 5. 收到响应 │
│<─────────────────────────────────────────│
五、为什么选择注解方案?
- 零侵入:不改动业务代码,加注解立竿见影
- 高可用:基于Redis分布式锁,适合集群环境
- 灵活配置:支持自定义过期时间、提示信息
- 强隔离性:不同接口可使用独立Token策略
六、高级扩展技巧
- 自定义Token生成器:
public interface TokenGenerator {
String generate(HttpServletRequest request);
}
- 组合式Key(防不同参数重复):
String key = "idempotent:" + userId + ":" + md5(params);
- 自定义异常处理:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IdempotentException.class)
public Result handleIdempotent(IdempotentException ex) {
return Result.fail(400, ex.getMessage());
}
}
结语
幂等性设计是分布式系统的基石要求。通过@Idempotent注解,我们实现了:
- 业务代码零污染
- 防重覆盖率达100%
- 30秒快速接入
从此再也不用在Controller里写重复的checkDuplicateRequest()!
技术讨论:你的系统中有哪些被幂等性问题坑过的经历?欢迎评论区分享避坑经验!
猜你喜欢
- 2025-07-28 告别频繁登录!Nuxt3 + TS + Vue3实战:双Token无感刷新方案全解析
- 2025-07-28 SpringBoot实现单点登录(SSO)的4种方案
- 2025-07-28 随机密聊 匿名聊天室程序源码(随机匿名聊天在线)
- 2025-07-28 SpringBoot大文件上传卡死?分块切割术搞定GB级传输,速度飙升!
- 2025-07-28 Java 微服务从源码实战开始 | Gitee 项目推荐
- 2025-07-28 轻量级埋点sdk搭建,便捷更全面(埋点sdk是什么)
- 2025-07-28 Spring Boot 实现文件秒传功能(springboot上传文件到指定文件夹)
- 2025-07-28 项目中不用redis分布式锁,怎么防止用户重复提交?
- 2025-07-28 SpringBoot项目日志打印traceId生成
- 2025-07-28 如何实现PC端网站扫码登录操作?(网页 扫码)
你 发表评论:
欢迎- 08-06nginx 反向代理
- 08-06跨表插入连续的日期,sheetsname函数#excel技巧
- 08-06初中生也能学的编程,不走弯路,先用后学
- 08-06find命令的“七种武器”:远不止-name和-type
- 08-06恶意代码常见的编程方式
- 08-06kali2021ping 外网不通
- 08-06因为一个函数strtok踩坑,我被老工程师无情嘲笑了
- 08-06hadoop集群搭建详细方法
- 23℃nginx 反向代理
- 最近发表
- 标签列表
-
- ifneq (61)
- 字符串长度在线 (61)
- googlecloud (64)
- powershellfor (73)
- messagesource (71)
- plsql64位 (73)
- vueproxytable (64)
- npminstallsave (63)
- #NAME? (61)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- nacos启动失败 (64)
- ssh-add (70)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- qcombobox样式表 (68)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)