侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

设计高并发场景下的分布式幂等接口

2025-12-9 / 0 评论 / 4 阅读

题目

设计高并发场景下的分布式幂等接口

信息

  • 类型:问答
  • 难度:⭐⭐⭐

考点

Spring MVC请求处理流程,分布式锁实现,接口幂等性设计,高并发优化,防重机制

快速回答

实现高并发幂等接口的核心要点:

  • 使用分布式锁(如Redis RedLock)保证原子性操作
  • 通过唯一请求ID实现幂等性校验(客户端生成或服务端颁发)
  • 采用三级防重策略:请求ID内存缓存 → Redis标记 → 数据库唯一索引
  • 使用异步处理+状态查询分离读写压力
  • 数据库操作使用乐观锁唯一约束
## 解析

1. 问题背景与挑战

在分布式系统中,当用户重复提交、网络重试或服务重发时,需要保证接口的幂等性。典型场景如:支付回调、订单创建等。高并发下需解决:

  • 重复请求导致数据不一致
  • 数据库写入冲突
  • 系统资源竞争

2. 核心实现方案

2.1 幂等性设计原理

幂等性指同一请求多次执行结果一致。关键要素:

  • 唯一请求ID:客户端生成(如前端UUID)或服务端颁发(Token机制)
  • 状态记录:记录请求处理状态(pending/success/fail)

2.2 Spring MVC接口实现

@RestController
public class PaymentController {
    @PostMapping("/pay")
    public ResponseEntity<String> payment(
        @RequestHeader("X-Request-ID") String requestId, 
        @RequestBody PaymentRequest request) {

        // 1. 幂等校验
        IdempotentService.verifyRequestId(requestId);

        // 2. 业务处理(含分布式锁)
        return paymentService.process(requestId, request);
    }
}

2.3 三级防重策略

// 伪代码实现
public class IdempotentService {
    // 一级:本地缓存(Guava Cache)
    private static final Cache<String, Boolean> localCache = 
        CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS).build();

    // 二级:Redis标记
    public static void verifyRequestId(String requestId) {
        // 本地缓存检查
        if (localCache.getIfPresent(requestId) != null) {
            throw new DuplicateRequestException();
        }

        // Redis原子操作(SETNX+EXPIRE)
        boolean locked = redisTemplate.execute((RedisCallback<Boolean>) conn -> 
            conn.set(requestId.getBytes(), "1".getBytes(), 
                Expiration.seconds(30), 
                RedisStringCommands.SetOption.SET_IF_ABSENT)
        );

        if (!locked) throw new DuplicateRequestException();
        localCache.put(requestId, true);
    }
}

2.4 数据库层防护

-- 创建唯一索引
ALTER TABLE payments ADD UNIQUE INDEX idx_request_id (request_id);

3. 高并发优化策略

  • 读写分离
    • 写操作:异步处理(@Async + 线程池)
    • 读操作:返回202 Accepted + 结果查询接口
  • 锁优化
    • 使用Redisson红锁(RedLock)替代简单Redis锁
    • 锁粒度控制到业务ID级别(如用户ID+业务类型)
  • 流量控制
    • Guava RateLimiter限流
    • Nginx层限速

4. 常见错误与规避

  • 错误1:仅用本地缓存 → 分布式系统失效
    规避:必须结合分布式存储
  • 错误2:未设置锁超时 → 死锁风险
    规避:Redis锁必须设置过期时间
  • 错误3:先查DB再操作 → 并发间隙
    规避:用唯一索引兜底

5. 扩展知识

  • Token方案:预生成令牌(防止客户端重复生成ID)
    1. 客户端获取token(GET /token)
    2. 携带token发起请求(POST /pay?token=xxx)
  • 状态机设计:业务状态流转校验(如订单状态:created → paid → fulfilled)
  • 消息队列:Kafka/RocketMQ自带消息去重机制