当前位置: 首页 > news >正文

基于Redis实现幂等判断

核心思路:

当用户发出提交请求时,在 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>

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

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 {@Resourceprivate 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("重复提交");}}}
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 {@Resourceprivate 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本地缓存

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 块中进行,避免因异常导致锁未被释放。

    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.mrgr.cn/news/29587.html

相关文章:

  • MongoDB
  • 论文阅读--Planning-oriented Autonomous Driving(一)
  • 达梦数据库导入xml迁移到达梦数据库大文件导致中断问题解决方案记录?
  • 多目标优化算法(Multi-Objective Optimization Algorithms, MOOA)介绍
  • CTFShow-反序列化
  • C++面试题
  • GIT基本使用
  • 我写了一套无敌的参数校验组件③ | SpEL Validator 之自定义约束注解
  • 传输层协议——udp/tcp
  • 静态绑定和动态绑定
  • Flutter Android Package调用python
  • SOLIDWORKS® 2025 新增功能 - SIMULATION
  • 函数题 6-12 判断奇偶性【PAT】
  • 【Vue】- 路由及传参
  • 利用WPF绘制轮廓并保存为图片
  • 文件保护措施:防止文件误删、覆盖及恶意篡改的策略探讨
  • c++设计模式
  • 珠海市自闭症寄宿学校,为孩子打造温馨成长环境
  • python队列操作
  • 李沐也看好的方向:多模态预训练!最新成果刷爆SOTA,轻松拿下顶会