当前位置: 首页 > news >正文

http接口幂等性

        实现 HTTP 接口的幂等性是确保多次相同请求产生相同结果的重要设计原则,尤其在网络不稳定或分布式系统中非常关键。以下是几种常见的实现方式:

1. 基于幂等性令牌(Token)的实现

适合支付、订单创建等场景,步骤如下:

  1. 客户端获取令牌
  2. 客户端携带令牌请求接口
  3. 服务端验证并消费令牌
@Service
public class IdempotentService {// 实际项目中使用Redis等分布式缓存private final Set<String> tokenStore = ConcurrentHashMap.newKeySet();// 生成令牌public String generateToken() {String token = UUID.randomUUID().toString();tokenStore.add(token);return token;}// 验证令牌public boolean validateToken(String token) {return tokenStore.remove(token); // 原子操作,确保唯一消费}
}@RestController
@RequestMapping("/orders")
public class OrderController {@Autowiredprivate IdempotentService idempotentService;@Autowiredprivate OrderService orderService;@PostMappingpublic ResponseEntity<?> createOrder(@RequestHeader("Idempotency-Token") String token,@RequestBody OrderRequest request) {// 验证令牌if (!idempotentService.validateToken(token)) {return ResponseEntity.ok("重复请求,已处理");}// 处理订单逻辑OrderResult result = orderService.createOrder(request);return ResponseEntity.ok(result);}
}

2. 基于数据库唯一约束

通过数据库唯一索引确保重复数据无法插入:

// 实体类
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"order_no"}) // 订单号唯一约束
})
public class Order {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String orderNo;// 其他字段...
}// 服务层
@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactionalpublic Order createOrder(OrderRequest request) {String orderNo = generateOrderNo();Order order = new Order();order.setOrderNo(orderNo);// 设置其他字段...try {return orderRepository.save(order);} catch (DataIntegrityViolationException e) {// 捕获唯一约束异常,视为重复请求log.warn("订单已存在: {}", orderNo);return orderRepository.findByOrderNo(orderNo).orElseThrow();}}
}

3. 基于 Redis 的分布式锁

适合分布式系统中的幂等性控制:

@Service
public class RedisIdempotentService {@Autowiredprivate StringRedisTemplate redisTemplate;private static final String IDEMPOTENT_KEY_PREFIX = "idempotent:";private static final long EXPIRATION_TIME = 30L; // 30秒过期public boolean checkAndLock(String key) {String redisKey = IDEMPOTENT_KEY_PREFIX + key;// SET NX 命令:不存在则设置,返回truereturn Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(redisKey, "1", EXPIRATION_TIME, TimeUnit.SECONDS));}public void releaseLock(String key) {String redisKey = IDEMPOTENT_KEY_PREFIX + key;redisTemplate.delete(redisKey);}
}// 控制器中使用
@PostMapping("/pay")
public ResponseEntity<?> pay(@RequestParam String orderId) {if (!redisIdempotentService.checkAndLock(orderId)) {return ResponseEntity.ok("支付请求已处理");}try {// 处理支付逻辑paymentService.processPayment(orderId);return ResponseEntity.ok("支付成功");} finally {redisIdempotentService.releaseLock(orderId);}
}

4. 基于 Spring AOP 的幂等性注解

通过自定义注解简化幂等性控制:

// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {// 幂等键的参数索引,默认取第一个参数int keyIndex() default 0;
}// AOP切面
@Aspect
@Component
public class IdempotentAspect {@Autowiredprivate RedisIdempotentService redisService;@Around("@annotation(idempotent) && args(.., request)")public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent, HttpServletRequest request) throws Throwable {// 获取幂等键(此处从请求头获取)String key = request.getHeader("Idempotency-Key");if (StringUtils.isEmpty(key)) {return ResponseEntity.badRequest().body("缺少幂等键");}if (!redisService.checkAndLock(key)) {return ResponseEntity.ok("重复请求");}try {return joinPoint.proceed();} finally {// 非必须,根据业务设置过期时间自动释放// redisService.releaseLock(key);}}
}// 接口使用
@PostMapping("/transfer")
@Idempotent
public ResponseEntity<?> transferMoney(@RequestBody TransferRequest request) {// 处理转账逻辑return ResponseEntity.ok(transferService.transfer(request));
}

总结

  1. 选择合适的方案:查询操作天然幂等,无需额外处理;写操作根据业务选择令牌或唯一约束
  2. 过期策略:幂等键需设置合理过期时间,避免存储空间无限增长
  3. 异常处理:确保幂等控制逻辑的异常不影响正常业务流程
  4. 分布式场景:必须使用 Redis 等分布式存储,避免单机存储导致的问题
  5. 原子性保证:验证和业务操作需在同一事务中,或使用分布式锁确保原子性

文章转载自:

http://jdmCo18L.Lnckq.cn
http://lh9h0jWP.Lnckq.cn
http://3Bgd4b36.Lnckq.cn
http://opJmv9SA.Lnckq.cn
http://QRP2PylK.Lnckq.cn
http://uWrpxpB4.Lnckq.cn
http://S4E2qUDz.Lnckq.cn
http://Q37y5LMk.Lnckq.cn
http://RAZs5NaZ.Lnckq.cn
http://JQQiP6gH.Lnckq.cn
http://QhagLoKD.Lnckq.cn
http://7CjMxKLV.Lnckq.cn
http://eliJ206f.Lnckq.cn
http://loYHuKMG.Lnckq.cn
http://Rgj8ztFG.Lnckq.cn
http://vq07qHOT.Lnckq.cn
http://vra43ie1.Lnckq.cn
http://KDG63erK.Lnckq.cn
http://MuzXDAGM.Lnckq.cn
http://a3mt75dQ.Lnckq.cn
http://6BdTwo0K.Lnckq.cn
http://9v7EpkDR.Lnckq.cn
http://EHcDF5Bw.Lnckq.cn
http://L3oNJ0lW.Lnckq.cn
http://qp09zBnD.Lnckq.cn
http://Yqldx95O.Lnckq.cn
http://pSXfsK2J.Lnckq.cn
http://EUyjbTqq.Lnckq.cn
http://pBhell9N.Lnckq.cn
http://hGzj96iV.Lnckq.cn
http://www.dtcms.com/a/372068.html

相关文章:

  • 无重复字符的最长子串
  • 架构思维:架构师视角的 FullGC 治理
  • pytest(1):fixture从入门到精通
  • Logstash中http_poller插件的用法
  • 软考中级习题与解答——第三章_操作系统(1)
  • 基于Python的智能工程资料自动生成模型设计与实现
  • 硬件:传感器(DS18B20)
  • muduo库搭建客户端
  • smpp3.4 协议
  • 阿里云高可用生产环境网络架构实战:VPC规划与多可用区部署
  • 中国移动中兴云电脑W132D-RK3528-2+32G-刷机固件包(非原机制作)
  • 疯狂星期四文案网第63天运营日记
  • 【PCIe EP 设备入门学习专栏 -- 8.2 PCIe EP 寄存器配置空间介绍】
  • Android开发-按钮触控
  • RocketMQ分布式消息中间件的核心原理与应用
  • MySQL 之 InnoDB 存储架构解析
  • 【LeetCode - 每日1题】构造和为0的n个不同整数数组
  • 使用MobaXterm连接Ubuntu时connection refused解决方法
  • Windows 内存整理和优化工具 - Wise Memory Optimize
  • VuePress 与 VitePress 深度对比:特性、差异与选型指南
  • Dockerfile文件常用配置详解
  • Logstash常用插件-ES集群加密
  • NT路径指的是什么?
  • AutoHotkey将脚本编译为exe文件
  • 【Java笔记】单例模式
  • 腕部骨折X光检测识别数据集:2w+图像,6类,yolo标注
  • 当没办法实现从win复制东西到Linux虚拟机时的解决办法
  • AI话术—知识库多次返回播放不同的内容(智能呼叫系统)
  • 【系统架构设计(20)】构件与中间件技术
  • 使用Terraform管理阿里云基础设施