SpringBoot中防止接口重复提交多种方法(代码示例)

在Spring Boot应用中,防止接口重复提交是一个常见的需求,尤其是在处理订单、支付等关键业务时。

这可以通过在单机环境和分布式环境下采用不同的策略来实现。

单机环境

在单机环境下,可以使用ThreadLocal或者Spring的@SessionAttributes来防止接口重复提交。

1. 使用ThreadLocal

ThreadLocal为每个线程提供了一个单独的变量副本,因此可以用来存储请求级别的数据,以此来判断是否是重复提交。

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    private ThreadLocal<String> orderSubmitFlag = new ThreadLocal<>();

    @PostMapping("/order")
    public String submitOrder() {
        // 检查是否已经提交过
        if (orderSubmitFlag.get() != null) {
            return "请勿重复提交";
        }
        
        orderSubmitFlag.set("提交中");
        
        try {
            // 执行业务逻辑...
            
            // 提交成功,清理标志
            orderSubmitFlag.remove();
            return "提交成功";
        } catch (Exception e) {
            // 处理异常,可根据需要重置标志或不处理
            return "提交失败";
        }
    }
}
2. 使用@SessionAttributes

如果你的应用启用了session,并且希望在用户会话级别防止重复提交,可以使用@SessionAttributes

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

@Controller
@SessionAttributes("submitFlag")
public class OrderController {

    @PostMapping("/order")
    public String submitOrder(Model model, SessionStatus sessionStatus) {
        if (model.getAttribute("submitFlag") != null) {
            return "请勿重复提交";
        }
        
        model.addAttribute("submitFlag", true);
        
        try {
            // 执行业务逻辑...
            
            sessionStatus.setComplete(); // 成功后清除session中的标志
            return "提交成功";
        } catch (Exception e) {
            // 处理异常
            return "提交失败";
        }
    }
}

分布式环境

在分布式环境中,由于请求可能被负载均衡到不同的服务器上,因此需要一个全局的共享存储来记录请求状态,常用的方法有Redis、数据库等。

使用Redis
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String ORDER_SUBMIT_LOCK_PREFIX = "order:submit:";

    @PostMapping("/order")
    public String submitOrder(String orderId) {
        String key = ORDER_SUBMIT_LOCK_PREFIX + orderId;
        Boolean hasKey = redisTemplate.hasKey(key);
        
        if (Boolean.TRUE.equals(hasKey)) {
            return "请勿重复提交";
        }
        
        redisTemplate.opsForValue().set(key, "processing", 5, TimeUnit.MINUTES); // 设置5分钟过期
        
        try {
            // 执行业务逻辑...
            
            redisTemplate.delete(key); // 成功后删除key
            return "提交成功";
        } catch (Exception e) {
            // 处理异常,根据需要决定是否删除key
            return "提交失败";
        }
    }
}

除了上述提到的方法,还有其他一些策略和技术可以用来防止接口重复提交,特别是在处理高并发和分布式场景下。

更多方式

1. 使用Token机制
  • 前端生成Token: 在表单中嵌入一个由前端生成的唯一Token(可以是UUID),每次提交时携带此Token。服务器接收到请求后验证Token的有效性(例如,检查Token是否已使用或是否在有效期内),并处理完请求后标记此Token为已使用,后续相同的Token请求则被拒绝。

  • 后端生成Token: 类似地,服务器可以在首次请求时生成Token返回给客户端,客户端在后续的提交中携带此Token。服务器验证Token并执行幂等性检查。

2. 分布式锁
  • 基于Redis的分布式锁: 已在上述答案中提及,但更进一步,可以利用RedLock算法或者Lua脚本来实现更安全的分布式锁,确保在高并发下的锁的正确获取与释放。

  • Zookeeper分布式锁: 利用Zookeeper的临时节点特性,实现分布式锁。客户端在尝试执行操作前先获取锁,操作完成后释放锁。

3. 幂等性设计
  • 业务逻辑幂等: 设计接口为幂等的,即无论调用多少次,其结果都是一样的。这对于写操作来说可能涉及到业务逻辑的调整,比如在数据库层面使用UPDATE ... WHERE ...语句,确保只有满足特定条件的数据才会被更新。
4. 滑动窗口机制
  • 对于限制一定时间内的重复提交,可以使用滑动窗口算法,记录一段时间窗口内的请求次数,超过设定阈值则拒绝新的请求。
5. AOP(面向切面编程)与自定义注解
  • 防重提交切面: 利用Spring AOP编写一个切面,拦截特定方法的调用,检查是否存在重复提交的情况。可以结合ThreadLocal、Redis等技术来实现检查逻辑,并使用自定义注解标记需要防重复提交的控制器方法。
6. TCC事务(Try-Confirm-Cancel)
  • 在分布式事务场景下,TCC模式可以用来保证操作的幂等性和一致性。Try阶段预留资源,Confirm阶段确认执行,Cancel阶段取消执行。这种方式比较复杂,适用于复杂的业务流程,确保即使在部分失败的情况下也能回滚。
7. 消息队列
  • 将请求放入消息队列中,由后台服务异步处理。消息队列天然支持幂等消费,即相同的消息不会被重复处理。

每种方法都有其适用场景和优缺点,实际应用时应根据系统的具体需求、并发量、可用资源等因素综合考虑。

以上就是在单机环境和分布式环境下防止接口重复提交的几种方法。选择合适的方法取决于你的具体需求和系统架构。

  • 36
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值