基于Redis实现幂等判断

news/2024/9/19 5:38:33 标签: redis, java, 前端

核心思路:

当用户发出提交请求时,在 Redis 中创建一个带有过期时间的唯一标识,表示这个请求已经提交过了。如果 Redis 中已经存在这个标识,则拒绝本次提交,避免重复操作。

基本准备:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.7.10</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.7.10</version>
</dependency>

大体思路:在业务侧进行加锁的幂等判断,在规定时间内操作只能算一次成功的请求

java">import com.sa.config.RedissonManager;
import org.redisson.api.RLock;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
public class RepeatedSubmitService {

    @Resource
    private RedisTemplate redisTemplate;

    private static final long EXPIRE_TIME = 5;  // 过期时间,单位秒


    /**
     * 防止重复提交操作
     * @param userId userId 用户ID
     * @param actionId actionId 操作标识(可以是业务类型或者表单ID等)
     * @return true 表示操作允许,false 表示重复提交
     */
    public boolean check(String userId,String actionId){
        // 生成redisKey,作为唯一标识
        String redisKey = "submitLock:"+userId+":"+actionId;

        // 尝试使用 SETNX 来防止重复提交,返回 true 表示设置成功(没有重复提交)
        Boolean success = redisTemplate.opsForValue()
                .setIfAbsent(redisKey,"LOCKED",EXPIRE_TIME, TimeUnit.SECONDS);

        if (Boolean.TRUE.equals(success)){
            return true;
        }else{
            // Redis 中已经存在锁键,说明是重复提交
            return false;
        }
    }

    public void submit(String userId,String actionId){
        if (check(userId,actionId)){
            System.out.println("业务操作成功");
        }else {
            System.out.println("重复提交");
        }
    }

}
java">package com.sa.controller;


import com.sa.pojo.Order;
import com.sa.service.RepeatedSubmitService;
import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@Log4j2
@RestController
public class OrderController {

    @Resource
    private RepeatedSubmitService repeatedSubmitService;

    @GetMapping("/submit")
    public void submit(@RequestBody Order order){
        String userId = order.getUserId();
        String actionId = order.getActionId();
        log.info("userId:{},actionId:{}",userId,actionId);
        repeatedSubmitService.submit(userId,actionId);
    }
}

在5秒内的重复提交记录,只能是一条生效,剩余的请求在业务侧进行失效处理

改进

此处还可以基于本地缓存实现,这里采用Map模拟,也可以使用Caffine本地缓存

java">import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * 幂等性校验
 */
@Service
public class IdempotencyService {


    private Map<String, Boolean> requestCache = new HashMap<>();

    /**
     * 检查是否是重复的请求
     *
     * @param requestId
     * @return
     */
    private synchronized boolean check(String requestId) {
        if (requestCache.containsKey(requestId)) {
            return false;
        }
        requestCache.put(requestId, true);
        return true;
    }

    /**
     * 模拟业务操作
     */
    public void processRequest(String requestId) {
        if (check(requestId)) {
            // 处理业务逻辑
            System.out.println("处理请求: " + requestId);
        } else {
            System.out.println("请求重复: " + requestId);
        }
    }

}

或是使用Redisson来进行实现:

  • RLock 替代 setIfAbsent:Redisson 的 RLock 封装了 Redis 分布式锁的功能,简化了操作。通过 lock.tryLock 来尝试获取锁,获取成功则继续执行操作,获取失败则表示重复提交。

  • 自动续期和过期时间:Redisson 内置了看门狗机制,会自动续期锁,防止长时间业务执行时锁提前释放。通过 tryLock(100, EXPIRE_TIME, TimeUnit.SECONDS) 可以设置锁的最大等待时间和最大存活时间,超时后锁自动释放。

  • 锁的释放:Redisson 自动确保锁的释放在 finally 块中进行,避免因异常导致锁未被释放。

java">    public boolean checkForRedisson(String userId,String actionId){
        // 生成redisKey,作为唯一标识
        String redisKey = "submitLock:"+userId+":"+actionId;

        // 获取分布式锁对象
        RLock lock = RedissonManager.getClient().getLock(redisKey);

        try{
            Boolean success = lock.tryLock(100,EXPIRE_TIME, TimeUnit.SECONDS);
            if (success){
                return true;
            }else{
                return false;
            }
        }catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }finally {
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }

 


http://www.niftyadmin.cn/n/5665087.html

相关文章

Canvas简历编辑器-Monorepo+Rspack工程实践

Canvas简历编辑器-MonorepoRspack工程实践 在之前我们围绕Canvas聊了很多代码设计层面的东西&#xff0c;在这里我们聊一下工程实践。在之前的文中我也提到过&#xff0c;因为是本着学习的态度以及对技术的好奇心来做的&#xff0c;所以除了一些工具类的库例如 ArcoDesign、Re…

第五部分:3---前后台进程的管理、闹钟机制

目录 前后台进程的管理&#xff1a; 闹钟机制&#xff1a; 闹钟结构体和链表&#xff1a; 设置闹钟&#xff1a; 闹钟超时检测&#xff1a; 前后台进程的管理&#xff1a; 前台进程&#xff1a;只能有一个前台进程&#xff0c;能够接收用户的输入&#xff0c;因为键盘是唯…

宝兰德MCP系列介绍 ①:中间件管理能力全线升级,驱动企业数字化管理效能提升

在企业数字化转型加速与新技术涌现下&#xff0c;中间件作为衔接底层基础设施和上层业务应用的桥梁&#xff0c;应用愈发广泛且关键。但为了有效管理并维护众多类型的中间件&#xff0c;企业需更多专业运维与资源&#xff0c;这大大分散业务焦点并提升成本。因此&#xff0c;优…

SpringBoot:解析excel

解析Excel文件&#xff0c;可以使用Apache POI库 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version> </dependency> 上代码&#xff1a; /*** <b>Functio…

算法-排序算法(冒泡选择插入希尔快速归并堆)

1.算法概述 1.1什么是算法 算法是特定问题的求解步骤的描述&#xff0c;是独立存在的一种解决问题的思想和方法。对于算法而言计算机编程语言并不重要&#xff0c;可以用任何计算机编程语言来编写算法。 程序数据结构算法 1.2数据结构和算法的区别和联系 数据结构只是静态…

1.Seata 1.5.2 seata-server搭建

一&#xff1a;Seata基本介绍 Seata是一款开源的分布式事务解决方案&#xff0c;致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 详见官网链接&#xff1a;https://seata.apache.org/zh-cn/ 1.历史项目里的使用经验&#xff1a; 之前公司里的oem用户对应的App…

基于51单片机的矿井安全检测系统

基于51单片机的矿井安全检测系统使用51单片机作为系统主控&#xff0c;LCD1602进行显示同时系统集成了ADC0808和烟雾传感器、甲烷传感器&#xff0c;二者结合测量环境烟雾值&#xff0c;同时使用DHT11温湿度传感器获取环境温湿度值&#xff0c;使用L298N驱动风扇&#xff0c;利…

iPhone 16系列:摄影艺术的全新演绎,探索影像新境界

在科技的浪潮中&#xff0c;智能手机摄影功能的进化从未停歇。 苹果公司即将推出的iPhone 16系列&#xff0c;以其卓越的相机升级和创新特性&#xff0c;再次站在了手机摄影的前沿。 从硬件到软件&#xff0c;从拍照体验到图像处理&#xff0c;iPhone 16系列都展现了其在移动…