基于Redis实现幂等判断

news/2024/9/22 18:16:53/

核心思路:

当用户发出提交请求时,在 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 {@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("重复提交");}}}
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 {@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本地缓存

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.ppmy.cn/news/1528948.html

相关文章

【Elasticsearch系列十九】评分机制详解

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【运维项目经历|044】云迁移与CI/CD管道优化项目

🍁博主简介: 🏅云计算领域优质创作者 🏅2022年CSDN新星计划python赛道第一名 🏅2022年CSDN原力计划优质作者 🏅阿里云ACE认证高级工程师 🏅阿里云开发者社区专家博主 💊交流社区:CSDN云计算交流社区欢迎您的加入! 文章目录 项目名称项目背景项目目标项目成果我…

HTTP中的301、302实现重定向

HTTP状态码301和302描述 ‌HTTP状态码301和302用于实现重定向‌&#xff0c;其中301代表永久重定向&#xff0c;而302代表临时重定向。这两种重定向方式在网页开发、搜索引擎优化&#xff08;SEO&#xff09;以及用户体验方面扮演着重要的角色。 301 301永久重定向‌意味着原…

力扣718-最长重复子数组(Java详细题解)

题目链接&#xff1a;718. 最长重复子数组 - 力扣&#xff08;LeetCode&#xff09; 前情提要&#xff1a; 因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。 dp五部曲。 1.确定dp数组和i下标的含义。 2.确定递推公式。 3.dp初始化。 4.确定dp的遍历顺序。 5…

硬件(驱动开发概念)

驱动程序开发 裸机驱动&#xff08;无操作系统&#xff09; Linux驱动 以计算机技术为基础&#xff0c;在软件和硬件层间可以被剪裁的专业硬件计算机系统 SOC&#xff1a;片上系统 Kernel&#xff1a;内核 x86 &#xff08;CISC:complex instruction set computer 复杂指令…

【Proteus仿真】基于51单片机的L298N电机电速调节

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;L298N电机驱动连接电机&#xff0c;采用调节PWM占空比来控制电机速度转动。 仿真图&#xff1a; 编辑 二、硬件资源 基于KEIL5编写C代码&#xff0c;PROTEUS8.15进行…

图卷积网络(GCN)与图注意力网络(GAT)基础实现及其应用

创作不易&#xff0c;您的打赏、关注、点赞、收藏和转发是我坚持下去的动力&#xff01; 图卷积网络&#xff08;Graph Convolutional Networks, GCN&#xff09;是一种能够直接在图结构数据上进行操作的神经网络模型。它能够处理不规则的数据结构&#xff0c;捕获节点之间的依…

负载均衡服务由几部分组成?分别是什么

负载均衡服务由几部分组成?分别是什么&#xff1f;均衡服务通常由六部分组成&#xff0c;分别是客户端、负载均衡器、后端服务器、负载均衡算法、监控和健康检查及会话保持。这六者互相协同工作&#xff0c;实现了流量的有效分发和系统的高可用性。这种结构不仅提高了系统的容…