Spring Boot中幂等性的应用

Spring Boot 中,幂等性是实现分布式系统设计和接口调用的一个重要概念,尤其在高并发、分布式环境下,确保接口重复调用不会引发系统数据异常至关重要。

幂等性概念

幂等性(Idempotence)是指一次请求和重复多次请求对系统的影响完全相同。在接口调用中,如果一个接口满足幂等性,那么无论调用多少次,最终结果是一样的。

场景分析

  1. 支付系统
    防止重复支付。例如用户多次点击支付按钮,导致重复扣款。
  2. 订单创建
    防止用户重复下单,产生多个相同订单。
  3. 短信发送
    防止重复发送短信,避免浪费资源。
  4. 库存扣减
    防止并发扣减库存,导致库存不足或超卖。
  5. 分布式任务处理
    防止任务重复执行,保证最终一致性。

如何实现幂等性

在 Spring Boot 中,常用以下几种方法实现幂等性:

1.基于数据库唯一约束

原理:

利用数据库的唯一约束机制,确保同一请求只能操作一次。

实现:

  • 在数据库表中增加一个唯一字段(如订单号、请求 ID)。
  • 插入数据时,利用唯一约束防止重复写入。

代码示例:

java 复制代码
@Entity
@Table(name = "orders", uniqueConstraints = {@UniqueConstraint(columnNames = "orderId")})
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String orderId; // 唯一订单号
    
    @Column(nullable = false)
    private BigDecimal amount;

}

当重复提交时,数据库会抛出 DuplicateKeyException,可以捕获并返回提示。

2.基于唯一 Token 实现

原理:

  • 每次请求都需要携带唯一的 token,服务器校验 token 是否已使用。
  • 若已使用,则拒绝请求。

实现步骤:

1.客户端向服务器申请唯一 token(如 UUID)。

2.在请求时携带 token。

3.服务端验证 token:

  • 若 token 未使用,处理业务并标记 token 为已使用。
  • 若 token 已使用,直接返回提示。

代码示例:

java 复制代码
@RestController
@RequestMapping("/api/order")
public class OrderController {

    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @PostMapping("/create")
    public String createOrder(@RequestParam String token) {
        // 校验 token 是否已存在
        Boolean isTokenExists = redisTemplate.opsForValue().setIfAbsent(token, "1", 10, TimeUnit.MINUTES);
        if (Boolean.FALSE.equals(isTokenExists)) {
            return "重复请求,请勿再次提交";
        }
    
        // 执行业务逻辑
        // ...
    
        return "订单创建成功";
    }

}

优点:

  • 无需修改数据库结构。
  • 使用 Redis 提高性能,适用于高并发场景。

3.基于幂等字段校验

原理:

  • 接口请求体中包含幂等字段(如订单号、请求 ID)。
  • 服务端通过幂等字段判断请求是否已处理。

实现步骤:

  • 在业务表中增加 requestId 字段,标记唯一请求。
  • 每次请求前查询是否存在相同的 requestId。
  • 若存在,直接返回处理结果。

代码示例:

java 复制代码
@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;
    
    public String createOrder(String requestId, Order order) {
        // 校验幂等字段
        if (orderRepository.existsByRequestId(requestId)) {
            return "订单已创建,请勿重复提交";
        }
    
        // 保存订单
        order.setRequestId(requestId);
        orderRepository.save(order);
        return "订单创建成功";
    }

}

4.基于分布式锁

原理:

  • 利用分布式锁(如 Redis 的 SETNX)对关键资源加锁,确保同一时刻只有一个请求处理。

实现步骤:

  1. 请求时加锁,锁的唯一标识为幂等字段(如订单号)。
  2. 若加锁成功,执行业务逻辑。
  3. 业务执行完成后释放锁。

代码示例:

java 复制代码
@Service
public class SmsService {

    @Autowired
    private StringRedisTemplate redisTemplate;
    
    public String sendSms(String phoneNumber) {
        String lockKey = "sms:lock:" + phoneNumber;
    
        // 加锁
        Boolean isLockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 2, TimeUnit.MINUTES);
        if (Boolean.FALSE.equals(isLockAcquired)) {
            return "短信发送过于频繁,请稍后再试";
        }
    
        try {
            // 执行业务逻辑
            // ...
            return "短信发送成功";
        } finally {
            // 释放锁
            redisTemplate.delete(lockKey);
        }
    }

}

5.基于状态校验

原理:

  • 根据业务状态判断请求是否重复。
  • 常用于支付、库存等有明确状态的场景。

实现步骤:

  • 增加状态字段(如订单状态、支付状态)。
  • 请求前校验状态是否已完成。

代码示例:

java 复制代码
@Service
public class PaymentService {

    @Autowired
    private OrderRepository orderRepository;
    
    public String payOrder(Long orderId) {
        Order order = orderRepository.findById(orderId).orElseThrow(() -> new RuntimeException("订单不存在"));
    
        // 校验状态
        if (order.getStatus().equals("PAID")) {
            return "订单已支付,请勿重复操作";
        }
    
        // 修改订单状态
        order.setStatus("PAID");
        orderRepository.save(order);
    
        return "支付成功";
    }

}

幂等性设计注意事项

1.选择合适的幂等方案

  • 数据库唯一约束适合低并发场景。
  • Redis 分布式锁适合高并发场景。
  • 幂等字段校验适合需要记录请求 ID 的场景。

2.幂等字段的设计

  • 幂等字段应具有唯一性,如订单号、请求 ID。
  • 客户端生成或服务端分配均可。

3.幂等性与事务

  • 确保幂等校验与业务逻辑在同一事务中执行,避免校验通过但业务未执行完成的情况。

4.性能优化

  • 使用缓存(如 Redis)提高幂等校验性能,减少数据库压力。

总结

Spring Boot 中的幂等性实现,是确保接口安全性和数据一致性的关键。根据业务场景的不同,选择合适的幂等方案至关重要:

  • 数据库唯一约束:简单场景,直接使用。
  • Redis 分布式锁:高并发场景,提升性能。
  • 幂等字段校验:需要记录唯一请求的场景。

幂等性设计不仅是接口安全的保障,更是系统稳定性的核心体现。

相关推荐
小李不想输啦2 小时前
什么是微服务、微服务如何实现Eureka,网关是什么,nacos是什么
java·spring boot·微服务·eureka·架构
张铁铁是个小胖子2 小时前
微服务学习
java·学习·微服务
ggs_and_ddu2 小时前
Android--java实现手机亮度控制
android·java·智能手机
敲代码娶不了六花3 小时前
jsp | servlet | spring forEach读取不了对象List
java·spring·servlet·tomcat·list·jsp
Yhame.3 小时前
深入理解 Java 中的 ArrayList 和 List:泛型与动态数组
java·开发语言
是小崔啊4 小时前
开源轮子 - EasyExcel02(深入实践)
java·开源·excel
myNameGL5 小时前
linux安装idea
java·ide·intellij-idea
青春男大5 小时前
java栈--数据结构
java·开发语言·数据结构·学习·eclipse
HaiFan.6 小时前
SpringBoot 事务
java·数据库·spring boot·sql·mysql
2401_882727576 小时前
低代码配置式组态软件-BY组态
前端·后端·物联网·低代码·前端框架