接口幂等性怎么设计?举一个你项目中的实际做法

2025年 阅读约 10 分钟 面试指南 · 系统设计

接口幂等性设计全方案,包含Token机制、数据库唯一索引、状态机、版本号等实战案例。

一句话总结

幂等性核心:同一请求多次调用对系统影响与一次相同。方案:Token机制防重复提交、唯一索引防重复插入、状态机防重复操作、版本号防并发更新。

初级理解

幂等性定义:同一请求多次调用,对系统产生的影响与调用一次完全相同。

常见设计方案:

1. Token机制:服务端生成Token存入Redis,客户端提交时携带,验证后删除 2. 数据库唯一索引:利用订单号等唯一约束,重复插入直接报错 3. 状态机:如订单状态只能单向流转,重复请求被拒绝 4. 版本号:UPDATE SET version=version+1 WHERE id=? AND version=?
一句话总结:Token防重复提交,唯一索引防重复插入,状态机防重复操作。

中级深入

Token机制实现:

// 1. 生成Token @GetMapping("/token") public String generateToken() { String token = UUID.randomUUID().toString(); redisTemplate.opsForValue().set("token:" + token, "1", 5, TimeUnit.MINUTES); return token; } // 2. 验证Token @PostMapping("/submit") public Result submit(@RequestParam String token, @RequestBody OrderDTO order) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; Long result = redisTemplate.execute(script, List.of("token:" + token), token); if (result == 1) { return orderService.createOrder(order); } else { return Result.fail("请勿重复提交"); } }
注意:Token必须设置过期时间,防止用户长期不提交导致Token失效。

高级拓展

分布式环境下的幂等性:

public Result createOrder(OrderDTO order) { String lockKey = "order:lock:" + order.getOrderNo(); boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS); if (!locked) return Result.fail("请勿重复提交"); // 数据库唯一索引兜底 Order existing = orderMapper.selectByOrderNo(order.getOrderNo()); if (existing != null) return Result.fail("订单已存在"); orderMapper.insert(order); redisTemplate.delete(lockKey); return Result.success("创建成功"); }

消息队列的幂等性:

@RocketMQMessageListener(topic = "order-topic", consumerGroup = "group") public void onMessage(OrderMessage message) { String key = "processed:" + message.getMessageId(); if (redisTemplate.hasKey(key)) return; // 已消费 orderService.processOrder(message); redisTemplate.opsForValue().set(key, "1", 24, TimeUnit.HOURS); }
面试加分项:能说出分布式环境下的幂等性方案、消息队列的幂等性处理,说明你对分布式系统有深入理解。

实战场景

场景:下单接口Token幂等

// 流程: // 1. 用户进入下单页面,前端请求/api/token获取Token // 2. 用户点击下单,请求体携带该Token // 3. 后端通过Lua脚本原子性地检查并删除Redis中的Token // 4. 若删除成功,执行业务逻辑;若Token不存在,返回"请勿重复提交" // 5. 即使网络重试,第二次请求因Token已被消费,保证订单只创建一次

场景:支付回调幂等

@Transactional public void handlePaymentCallback(PaymentDTO payment) { Order order = orderMapper.selectByOrderNo(payment.getOrderNo()); // 状态机校验 if (!"PENDING".equals(order.getStatus())) { log.info("订单{}状态为{},忽略重复支付回调", payment.getOrderNo(), order.getStatus()); return; } int updated = orderMapper.updateStatus(order.getId(), "PENDING", "PAID"); if (updated == 0) throw new RuntimeException("状态更新失败"); orderService.afterPayment(order); }

面试模拟

Q:如何保证分布式系统中的幂等性?

A:综合方案:1. 唯一请求ID:每个请求携带唯一ID,服务端检查是否已处理;2. 分布式锁:防止并发重复处理;3. 数据库唯一索引:数据库层面兜底;4. 状态机:业务状态单向流转。

Q:Token机制有什么缺点?

A:缺点:1. 需要额外存储Token;2. Token有有效期;3. 不能防止恶意重复提交(需配合频率限制);4. 分布式环境下需要保证Token的原子性操作。