Redis防止用户并发执行某一操作

2024-05-28 00:18

本文主要是介绍Redis防止用户并发执行某一操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景

在实际的开发过程中,会遇到某些接口因处理过慢或者用户重复点击操作,导致相同的操作执行多遍的情况,那么如何有效的防止同一用户同一时刻执行多次相同的操作,就是本文的重点啦!

处理方法

废话不多说,此处采用Redis Incr实现。

Redis incr 可以实现原子性的递增,可应用于高并发的秒杀活动、分布式序列号生成等场景。这里我使用它来计数实现过滤用户请求,同一时刻只允许同一用户执行一次操作,任务执行超过一分钟可以执行下一次操作。

  • 过滤用户请求,同一时刻只允许同一用户执行一次操作,任务执行超过一分钟可以执行下一次操作。
/*** Redis Incr 过滤用户请求,同一时刻只允许同一用户执行一次操作,任务执行超过一分钟可以执行下一次操作** @return*/@GetMapping("/testRedisIncrement3")public Response testRedisIncrement3(){Integer userId = 10086;String redisKey = "test_redis_increment_" + userId;long count = redisTemplate.opsForValue().increment(redisKey, 1);if (count == 1) {/******设置有效期一分钟******/log.info(Thread.currentThread().getName()+"设置Redis increment值失效时间,count = {}...", count);redisTemplate.expire(redisKey, 60, TimeUnit.SECONDS);}if (count > 1) {/******默认一分钟内只允许操作一次******/log.info(Thread.currentThread().getName()+"请求过于频繁...");return Response.errorResponse("请求过于频繁,请稍后重试");}try {/******具体执行的业务******/Thread.sleep(1000);  //更换睡眠时间查看效果 120*1000log.info(Thread.currentThread().getName()+"正在执行任务计算...");DingDingMsgSendUtils.sendDingDingGroupMsg(DingTokenEnum.SEND_SMS_BY_DEVELOPER_TOKEN.getToken(), "【测试消息】Redis Incr 过滤用户请求,同一用户同一时刻只允许一个操作执行,by:songfayuan...");} catch (Exception e) {log.info(Thread.currentThread().getName()+"请求异常,errMsg = {}", e);return Response.successResponse("请求异常");} finally {/******如果任务一分钟内执行完毕,则允许下一个任务继续执行******/log.info(Thread.currentThread().getName()+"请求操作删除...");redisTemplate.delete(redisKey);}return Response.successResponse("操作成功");}
  • 过滤用户请求,5s内只允许同一用户请求一次
	/*** Redis Incr 过滤用户请求,5s内只允许同一用户请求一次** @return*/@GetMapping("/testRedisIncrement2")public Response testRedisIncrement2(){Integer userId = 1;String redisKey = "test_redis_increment_" + userId;long count = redisTemplate.opsForValue().increment(redisKey, 1);log.info("{}, redis key = {}, count = {}", Thread.currentThread().getName(), redisKey, count);if (count == 1) {/******设置有效期一分钟******/log.info(Thread.currentThread().getName()+"设置Redis increment值失效时间...");redisTemplate.expire(redisKey, 5, TimeUnit.SECONDS);}if (count > 1) {/******一分钟内只允许操作一次******/log.info(Thread.currentThread().getName()+"请求过于频繁...");return Response.errorResponse("请求过于频繁,请稍后重试");}/******具体执行的业务******/log.info(Thread.currentThread().getName()+"正在执行任务计算...");DingDingMsgSendUtils.sendDingDingGroupMsg(DingTokenEnum.SEND_SMS_BY_DEVELOPER_TOKEN.getToken(), "【测试消息】Redis Incr 过滤用户请求,5s内只允许同一用户请求一次测试,by:songfayuan...");return Response.successResponse("操作成功");}
  • 仿多线程版
package com.github.collection.admin.controller.demo;import com.github.collection.common.constant.Constants;
import com.github.collection.common.constant.enums.DingMsgPhoneEnum;
import com.github.collection.common.constant.enums.DingTokenEnum;
import com.github.collection.common.distributelock.DistributeLock;
import com.github.collection.common.distributelock.DistributeLockFactory;
import com.github.collection.common.util.DingDingMsgSendUtils;
import com.github.collection.common.util.Response;
import com.github.collection.common.util.concurrent.Executors;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicInteger;
import org.springframework.web.bind.annotation.*;import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;/*** 描述:demo* @author songfayuan* 2017年8月31日下午2:07:19*/
@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController implements DisposableBean {private ExecutorService executor = Executors.newFixedThreadPool(10, "测试多线程操作");  //newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。@Autowiredprivate RedisTemplate redisTemplate;/*** Redis Incr 实现原子性的递增测试** 		Redis incr 可以实现原子性的递增,可应用于高并发的秒杀活动、分布式序列号生成等场景。这里我使用它来计数实现一分钟内只接受一次请求。** @return*/@GetMapping("/testRedisIncrement")public Response testRedisIncrement(){for (int i = 0; i<=200; i++) {   //模拟200个请求executor.execute(new Runnable() {@Overridepublic void run() {String redisKey = "test_redis_increment";long count = redisTemplate.opsForValue().increment(redisKey, 1);log.info("{}, redis key = {}, count = {}", Thread.currentThread().getName(), redisKey, count);if (count == 1) {/******设置有效期一分钟******/log.info(Thread.currentThread().getName()+"设置Redis increment值失效时间...");redisTemplate.expire(redisKey, 60, TimeUnit.SECONDS);}if (count > 1) {/******一分钟内只允许操作一次******/log.info(Thread.currentThread().getName()+"请求过于频繁...");return;}//具体执行的业务log.info(Thread.currentThread().getName()+"正在执行任务计算...");DingDingMsgSendUtils.sendDingDingGroupMsg(DingTokenEnum.SEND_SMS_BY_DEVELOPER_TOKEN.getToken(), "【测试消息】Redis Incr 实现原子性的递增测试,by:songfayuan...");}});}return Response.successResponse("操作成功");}@Overridepublic void destroy() throws Exception {executor.shutdown();}
}
  • 文中用到的Response类
package com.github.collection.common.util;import com.fasterxml.jackson.annotation.JsonInclude;import java.io.Serializable;/*** 响应信息主体** @param <T>* @author songfayuan*/
//@JsonInclude(JsonInclude.Include.NON_NULL)
public class Response<T> implements Serializable {private static final long serialVersionUID = 1L;private static final int SUCCESS_CODE = 200;private static final String SUCCESS_MSG = "success";private static final int ERROR_CODE = 500;private static final String ERROR_MSG = "服务器内部异常,请联系技术人员"; //将error改成了内容信息public static final int NO_LOGIN = -1;public static final int SUCCESS = 200;public static final int FAIL = 500;public static final int NO_PERMISSION = 2;private String msg = "success";private int code = SUCCESS;private T data;public Response() {super();}public Response(T data) {super();this.data = data;}public Response(T data, String msg) {super();this.data = data;this.msg = msg;}public Response(int code, T data, String msg) {super();this.code = code;this.data = data;this.msg = msg;}public Response(int code, String msg) {super();this.code = code;this.msg = msg;}public Response(Throwable e) {super();this.msg = e.getMessage();this.code = FAIL;}public static Response success() {Response resp = new Response();resp.code = (SUCCESS_CODE);resp.msg = (SUCCESS_MSG);return resp;}public static Response successResponse(String msg) {Response resp = new Response();resp.code = SUCCESS_CODE;resp.msg = msg;return resp;}public static Response error() {Response resp = new Response();resp.code = (ERROR_CODE);resp.msg = (ERROR_MSG);return resp;}public static Response errorResponse(String msg) {Response resp = new Response();resp.code = ERROR_CODE;resp.msg = msg;return resp;}public static Response response(int code, String msg) {Response resp = new Response();resp.code = (code);resp.msg = (msg);return resp;}public static Response response(int code, String msg, Object data) {Response resp = new Response();resp.code = (code);resp.msg = (msg);resp.data = data;return resp;}public static Response success(Object data) {Response resp = new Response();resp.code = (SUCCESS_CODE);resp.msg = (SUCCESS_MSG);resp.data = data;return resp;}public static Response error(Object data) {Response resp = new Response();resp.code = (ERROR_CODE);resp.msg = (ERROR_MSG);resp.data = data;return resp;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public T getData() {return data;}public void setData(T data) {this.data = data;}
}

文中用到的钉钉消息发送方法DingDingMsgSendUtils,移步到教程:https://blog.csdn.net/u011019141/article/details/94222443

这篇关于Redis防止用户并发执行某一操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1008999

相关文章

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

redis群集简单部署过程

《redis群集简单部署过程》文章介绍了Redis,一个高性能的键值存储系统,其支持多种数据结构和命令,它还讨论了Redis的服务器端架构、数据存储和获取、协议和命令、高可用性方案、缓存机制以及监控和... 目录Redis介绍1. 基本概念2. 服务器端3. 存储和获取数据4. 协议和命令5. 高可用性6.

在MySQL执行UPDATE语句时遇到的错误1175的解决方案

《在MySQL执行UPDATE语句时遇到的错误1175的解决方案》MySQL安全更新模式(SafeUpdateMode)限制了UPDATE和DELETE操作,要求使用WHERE子句时必须基于主键或索引... mysql 中遇到的 Error Code: 1175 是由于启用了 安全更新模式(Safe Upd

TP-Link PDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务

《TP-LinkPDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务》近期,路由器制造巨头普联(TP-Link)在用户群体中引发了一系列重要变动,上个月,公司发出了一则通知,明确要求所... 路由器厂商普联(TP-Link)上个月发布公告要求所有用户必须完成实名认证后才能继续使用普联提供的 D

Redis的数据过期策略和数据淘汰策略

《Redis的数据过期策略和数据淘汰策略》本文主要介绍了Redis的数据过期策略和数据淘汰策略,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录一、数据过期策略1、惰性删除2、定期删除二、数据淘汰策略1、数据淘汰策略概念2、8种数据淘汰策略

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表