分布式锁实现限流

1.限流锁的应用场景

同一时间接口访问量巨大,如秒杀,需要进行限流。

2.实现思路

用 CURRENT_LIMIT_+ 类名+方法名作为redis的key, value作为访问秒杀接口的人数。
用redis的计数器统计访问人数,每新增一个访问请求,计数器+1,当人数超过上限,提示服务忙,秒杀接口处理完之后,计数器-1。

3.主要组成

Dcl:限流注解,自定义锁注解,然后给需要限流的方法加上此注解
DistributedCurrentLimit:限流接口
RedisDistributedCurrentLimit:限流实现类
DistributedCurrentLimitAspect:切面类【核心】

自定义锁注解,利用切面给所有加注解的方法加分布式锁进行限流。

4.关键代码分析:

DistributedCurrentLimitAspect:用redis计时器算人头,人头达到上限就返回服务正忙

@Around("@annotation(com.nascent.ecrp.mall.common.distribute.Dcl)")public Object distributedLock(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {if (request == null) {return proceedingJoinPoint.proceed();}MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();Method method = methodSignature.getMethod();// 用CURRENT_LIMIT_+ 类名+方法名作为redis的key,统计调用此方法的用户数String lockKey = "CURRENT_LIMIT_" + method.getDeclaringClass().getName() + "." + method.getName();Dcl distributedKey = method.getAnnotation(Dcl.class);// 解析缓存key的后缀int keyIndex = distributedKey.keyIndex();String suffixKey = "";if (keyIndex > -1) {Object[] args = proceedingJoinPoint.getArgs();suffixKey += "_" + args[keyIndex];}lockKey += suffixKey;try {// 每进来一个,redis的计数器+1Long incr = currentLimit.incr(lockKey);// 计数器数 > MAX_LIMIT,抛出异常,用户就不能继续访问接口了if (incr > MAX_LIMIT) {// currentLimit.decr(lockKey);throw new WmDefinitelyRuntimeException("前方道路拥挤,请稍后再试");}return proceedingJoinPoint.proceed();} catch (Throwable e) {throw new WmDefinitelyRuntimeException(e);} finally {currentLimit.decr(lockKey);}}

3.全部代码

Dcl

/*** 限流*/
@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dcl {/*** 缓存key后缀参数所在位置** @return 位置*/int keyIndex();
}

DistributedCurrentLimit

/*** 分布式限流*/
public interface DistributedCurrentLimit{/*** 队列加1*/Long incr(String lockKey);/*** 队列减一*/Long decr(String lockKey);

RedisDistributedCurrentLimit

/*** redis 分布式锁*/
@SuppressWarnings({"UnusedReturnValue", "NullableProblems", "unused", "RedundantThrows"})
@Component
public class RedisDistributedCurrentLimit implements DistributedCurrentLimit {/*** 缓存*/@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Overridepublic Long incr(String lockKey) {return redisTemplate.opsForValue().increment(lockKey);}@Overridepublic Long decr(String lockKey) {return redisTemplate.opsForValue().decrement(lockKey);}}

DistributedCurrentLimitAspect

/*** 用户操作锁Aspect*/
@Aspect
@Component
@Order(-1)
public class DistributedCurrentLimitAspect {/*** 默认最大队列,暂时写死*/private static final Long MAX_LIMIT = 50L;/*** 分布式锁*/@Autowiredprivate RedisDistributedCurrentLimit currentLimit;@Autowiredprivate HttpServletRequest request;/*** @param proceedingJoinPoint proceedingJoinPoint*/@Around("@annotation(com.nascent.ecrp.mall.common.distribute.Dcl)")public Object distributedLock(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {if (request == null) {return proceedingJoinPoint.proceed();}MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();Method method = methodSignature.getMethod();String lockKey = "CURRENT_LIMIT_" + method.getDeclaringClass().getName() + "." + method.getName();Dcl distributedKey = method.getAnnotation(Dcl.class);// 解析缓存key的后缀int keyIndex = distributedKey.keyIndex();String suffixKey = "";if (keyIndex > -1) {Object[] args = proceedingJoinPoint.getArgs();suffixKey += "_" + args[keyIndex];}lockKey += suffixKey;try {Long incr = currentLimit.incr(lockKey);if (incr > MAX_LIMIT) {// currentLimit.decr(lockKey);throw new WmDefinitelyRuntimeException("前方道路拥挤,请稍后再试");}return proceedingJoinPoint.proceed();} catch (Throwable e) {throw new WmDefinitelyRuntimeException(e);} finally {currentLimit.decr(lockKey);}}
}

秒杀接口添加锁注解

/*** 参与秒杀** @return CommonResult*/@Dul@Dcl(keyIndex = 0)@Transactional(rollbackFor = Exception.class)@Overridepublic CommonResult doSeckill(WmMarketingSeckillPostVo seckillPostVo) {String checkResult = checkGoodsSeckillInfo(seckillPostVo.getSeckillOrderInfoList(),seckillPostVo.getShopId());if(checkResult.equals(ErrorCode.SECKILL_ACTIVITY_UPDATE.getCode())){return new CommonResult().setFailed().setCode(ErrorCode.SECKILL_ACTIVITY_UPDATE.getCode()).setMsg("活动状态已变更,请重新下单");}List<JSONObject> seckillOrderList = new ArrayList<>();//下单校验List<SecKillBuyInfo> buyInfos = new ArrayList<>();SeckillOrderCheckRequest seckillOrderCheckRequest = new SeckillOrderCheckRequest();seckillOrderCheckRequest.setCustomerId(getCustomerId());seckillOrderCheckRequest.setShopId(seckillPostVo.getShopId());for(WmMarketingSeckillPostVo.SeckillOrderInfo seckillOrderInfo : seckillPostVo.getSeckillOrderInfoList()){SecKillBuyInfo secKillBuyInfo = new SecKillBuyInfo();secKillBuyInfo.setActivityId(seckillOrderInfo.getMarketingGuid());List<SeckillBuyGoodsInfo> seckillBuyGoodsInfos = new ArrayList<>();SeckillBuyGoodsInfo seckillBuyGoodsInfo = new SeckillBuyGoodsInfo();seckillBuyGoodsInfo.setGoodsId(seckillOrderInfo.getGoodsId());seckillBuyGoodsInfo.setGoodsLibId(seckillOrderInfo.getGoodsLibId());List<SeckillBuySkuInfo> seckillBuySkuInfos = new ArrayList<>();SeckillBuySkuInfo seckillBuySkuInfo = new SeckillBuySkuInfo();seckillBuySkuInfo.setGoodsSkuId(StringUtil.isBlank(seckillOrderInfo.getGoodsSkuId()) ? "0" : seckillOrderInfo.getGoodsSkuId());seckillBuySkuInfo.setBuyCount(seckillOrderInfo.getGoodsNum());seckillBuySkuInfos.add(seckillBuySkuInfo);seckillBuyGoodsInfo.setSkuInfos(seckillBuySkuInfos);seckillBuyGoodsInfos.add(seckillBuyGoodsInfo);secKillBuyInfo.setGoodsList(seckillBuyGoodsInfos);buyInfos.add(secKillBuyInfo);}seckillOrderCheckRequest.setBuyInfos(buyInfos);seckillOrderCheckRequest.setGroupId(getGroupId());SeckillOrderCheckResponse seckillOrderCheckResponse = OpenPlatformClient.exec(getGroupId(), seckillOrderCheckRequest);log.info("【调用中台下单校验接口响应结果】seckillOrderCheckResponse="+JSON.toJSONString(seckillOrderCheckResponse));if(!seckillOrderCheckResponse.success&&seckillOrderCheckResponse.getCode().equals("50402")){log.info("【秒杀下单已达限购上限】");return new CommonResult().setFailed().setCode("50402").setMsg("已达限购上限");}if(!seckillOrderCheckResponse.success&&seckillOrderCheckResponse.getCode().equals("50404")){log.info("【秒杀剩余库存不足】");return new CommonResult().setFailed().setCode("50404").setMsg("剩余库存不足");}if(!seckillOrderCheckResponse.success&&seckillOrderCheckResponse.getCode().equals("50005")){log.info("【秒杀活动已结束】");return new CommonResult().setFailed().setCode("50005").setMsg("活动已结束");}AssertUtil.assertTrue(seckillOrderCheckResponse.getSuccess()&&seckillOrderCheckResponse.getResult().isSeckillSuccess(),"秒杀活动校验失败");Map<String,ActivitySeckillCheckInfo> activityCheckInfoMap = new HashMap<>();for(ActivitySeckillCheckInfo activitySeckillCheckInfo : seckillOrderCheckResponse.getResult().getCheckInfos()){Long goodsId = activitySeckillCheckInfo.getItemInfos().get(0).getGoodsId();Long goodsLibId = activitySeckillCheckInfo.getItemInfos().get(0).getGoodsLibId();String skuId = activitySeckillCheckInfo.getItemInfos().get(0).getSkuInfos().get(0).getSkuId();//无sku也会返回到sku级,skuid为0activityCheckInfoMap.put(activitySeckillCheckInfo.getActivityId()+"_"+goodsLibId+"_"+goodsId+"_"+skuId,activitySeckillCheckInfo);}Map<String, ItemDetailsInfo> itemInfoMap = getGoodsInfos(seckillPostVo.getShopId(),seckillPostVo.getSeckillOrderInfoList());Integer expireMinute = seckillOrderCheckResponse.getResult().getExpireMinute();for(WmMarketingSeckillPostVo.SeckillOrderInfo seckillOrderInfo :seckillPostVo.getSeckillOrderInfoList()){ItemDetailsInfo itemInfo = itemInfoMap.get(seckillOrderInfo.getGoodsId()+"_"+seckillOrderInfo.getGoodsLibId());Long marketGuid = seckillOrderInfo.getMarketingGuid();Long goodsId = seckillOrderInfo.getGoodsId();Long goodsLibId = seckillOrderInfo.getGoodsLibId();String goodsSkuId = seckillOrderInfo.getGoodsSkuId()==null?"0":seckillOrderInfo.getGoodsSkuId();//无sku也会返回到sku级,skuid为0ActivitySeckillCheckInfo activitySeckillCheckInfo = activityCheckInfoMap.get(marketGuid+"_"+goodsLibId+"_"+goodsId+"_"+goodsSkuId);String activityName = activitySeckillCheckInfo.getActivityName();//秒杀价BigDecimal price = itemInfo.getPrice();for(ActivitySeckillCheckItemInfo activitySeckillCheckItemInfo : activitySeckillCheckInfo.getItemInfos()){if(activitySeckillCheckItemInfo.getGoodsId().longValue() == seckillOrderInfo.getGoodsId().longValue() && activitySeckillCheckItemInfo.getGoodsLibId().longValue() == seckillOrderInfo.getGoodsLibId().longValue()){for(ActivitySeckillCheckSkuInfo seckillCheckSkuInfo : activitySeckillCheckItemInfo.getSkuInfos()){if(seckillCheckSkuInfo.getSkuId().equals("0")||seckillCheckSkuInfo.getSkuId().equals(seckillOrderInfo.getGoodsSkuId())){price = seckillCheckSkuInfo.getPrice();}}}}JSONObject orderJson = createOrderDetail(marketGuid,activityName,expireMinute,seckillOrderInfo.getGoodsNum(), goodsId, goodsSkuId, price, itemInfo);seckillOrderList.add(orderJson);}return new CommonResult(seckillOrderList);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/626706.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构学习 jz45 把数组排成最小的数

关键词&#xff1a;贪心 排序 快速排序 题目&#xff1a;破解闯关密码 思路&#xff1a; 快排自己制定快排规则 开辟一个vector存string&#xff0c;自己制定排序规则&#xff0c;然后用快排比较string大小。 排序规则&#xff1a; sort(strs.begin(),strs.end(),[](string…

建造者模式深入理解:演示建造单个和多个产品的实践,结合模板模式,通俗易懂

首先呢看下建造者的定义是什么样的&#xff0c;先读一遍 建造者模式 建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它主要用于将一个复杂对象的构建过程与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表现形式。这种模式…

编程笔记 html5cssjs 040 CSS盒子模型

编程笔记 html5&css&js 040 CSS盒子模型 一、CSS 盒子模型二、元素的宽度和高度三、最终元素的总宽度四、元素的总高度五、练习小结 网页是靠分成不同的块&#xff0c;再赋予这些块各不相同的属性来布局的。所以这个“块”是一个基础。先看块本身的构造。 一、CSS 盒子…

深度学习笔记(四)——使用TF2构建基础网络的常用函数+简单ML分类实现

文中程序以Tensorflow-2.6.0为例 部分概念包含笔者个人理解&#xff0c;如有遗漏或错误&#xff0c;欢迎评论或私信指正。 截图和程序部分引用自北京大学机器学习公开课 TF2基础常用函数 1、张量处理类 强制数据类型转换&#xff1a; a1 tf.constant([1,2,3], dtypetf.floa…

网络传输文件软件哪个好?企业该如何选择?

随着互联网技术的飞速发展&#xff0c;网络传输文件软件已经成为企业日常工作中不可或缺的一部分。然而&#xff0c;在市场上众多的网络传输文件软件中&#xff0c;很多企业对于如何选择合适的方案感到困惑。那么&#xff0c;究竟什么是网络传输文件软件&#xff1f;它有哪些作…

怎么注册微商城?开启微商城之旅

在这个数字化时代&#xff0c;微商城的出现为商家提供了一个全新的机会&#xff0c;商家企业可以通过微商城来展示和销售自己的产品。而对于一些商家而言&#xff0c;不知道怎么注册微商城。下面给大家做一个简单的分享。 第一步&#xff1a;选择合适的微商城搭建工具 在注册…

计算机毕业设计 | SpringBoot宠物店管理系统(附源码)

1&#xff0c;绪论 项目背景 我国已经成为世界第二大经济体&#xff0c;经济实力高速发展以及百姓生活水平的普遍提高&#xff0c;不断地要求企业提供更加多元化的娱乐方式&#xff0c;更加快速和方便的服务&#xff0c;因此对宠物行业也提出了更加严格的要求&#xff0c;如管…

洛谷 P1523 旅行商简化版【线性dp+npc问题简化版】

原题链接&#xff1a;https://www.luogu.com.cn/problem/P1523 题目背景 欧几里德旅行商(Euclidean Traveling Salesman)问题也就是货郎担问题一直是困扰全世界数学家、计算机学家的著名问题。现有的算法都没有办法在确定型机器上在多项式时间内求出最优解&#xff0c;但是有…

二叉树(完全二叉树,满二叉树,二叉树的特性,遍历方式,根据遍历方式画出完整的二叉树图相关例题)

目录 基本概念 一、二叉树&#xff08;满二叉树&#xff0c;完全二叉树&#xff09; 二、二叉树的特性 1、若规定根节点的层数为1&#xff0c;则一棵非空二叉树的第i层最多有2^(i-1) 个节点&#xff08;i>0&#xff09; 2、若规定只有根节点的二叉树的深度为1&#xff0…

人力资源智能化管理项目(day01:基础架构拆解)

学习源码可以看我的个人前端学习笔记 (github.com):qdxzw/frontlearningNotes 觉得有帮助的同学&#xff0c;可以点心心支持一下哈 一、基础架构拆解 1.拉取模板代码 git clone GitHub - PanJiaChen/vue-admin-template: a vue2.0 minimal admin template 项目名 2.core-js…

git将一个远程分支的部分修改提交到另一个远程分支

将一个远程分支的部分修改提交到另一个远程分支 将一个远程分支的部分修改提交到另一个远程分支&#xff0c;可以使用 git cherry-pick 命令。这个命令可以选择特定的提交&#xff08;commit&#xff09;从一个分支应用到另一个分支。 切换到目标本地分支&#xff1a; 首先&am…

js删除cookie

要删除一个cookie&#xff0c;你可以使用JavaScript中的document.cookie属性。这个属性包含当前页面上所有的cookie。要删除一个cookie&#xff0c;你可以将其设置为已过期&#xff0c;如下所示&#xff1a; document.cookie "cookieName; expiresThu, 01 Jan 1970 00:0…

使用WAF防御网络上的隐蔽威胁之SQL注入攻击

SQL注入攻击是一种普遍存在且危害巨大的网络安全威胁&#xff0c;它允许攻击者通过执行恶意的SQL语句来操纵或破坏数据库。 这种攻击不仅能够读取敏感数据&#xff0c;还可能用于添加、修改或删除数据库中的记录。因此&#xff0c;了解SQL注入攻击的机制及其防御策略对于保护网…

6.3.1认识Camtasia4(1)

6.3.1认识Camtasia4 安装完Camtasia4(本书使用Camtasia4.0.1版本)后&#xff0c;单击【开始】|【程序】|【Camtasia Studio 4】|【Camtasia Studio】&#xff0c;启动Camtasia Studio&#xff0c;启动后界面如图6-3-1所示。 图6-3-1 Camtasia Studio界面 Camtasia Studio窗口中…

智能时代的语言巨人:ChatGPT 与文心一言哪个更强?

想象一下&#xff0c;如果 AI 语言助手成为了我们生活中的超级英雄&#xff0c;那么 ChatGPT 和文心一言将是怎样的角色&#xff1f;一个是擅长英文、思维敏捷、能够随机应变的全能战士&#xff1b;另一个则是精通中文、文化深厚、在本土语境中无往不利的智者。当这两位超级英雄…

打印的前后顺序

面试题经常会有 <script>console.log(1)setTimeout(function(){console.log(2)})console.log(3)let pnew Promise((resolve,reject) >{console.log(4)resloved(hhhhhh)})p.then(res >{console.log(res)console.log(5)},res >{console.log(7)})console.log(6)&l…

小程序用户头像昵称获取规则调整

在小程序内&#xff0c;开发者可以通过 wx.login 接口直接获取用户的 openId 与 unionId 信息&#xff0c;实现微信身份登录&#xff0c;支持开发者在多个小程序或其它应用间匿名关联同一用户。 同时&#xff0c;为了满足部分小程序业务中需要创建用户的昵称与头像的诉求&…

Git版本控制——分支

分支 几乎所有的版本控制系统都以某种形式支持分支。 使用分支意味着可以把工作从开发主线上分离开来进行重大的Bug修改、开发新的功能&#xff0c;以免影响开发主线。 查看本地分支 git branch创建本地分支 git branch 分支名切换分支(checkout) git checkout 分支名创建…

Python源码26:海龟画图turtle画向日葵

---------------turtle源码集合--------------- Python教程43&#xff1a;海龟画图turtle画小樱魔法阵 Python教程42&#xff1a;海龟画图turtle画海绵宝宝 Python教程41&#xff1a;海龟画图turtle画蜡笔小新 Python教程40&#xff1a;使用turtle画一只杰瑞 Python教程39…

Pandas实战100例 | 案例 53: 处理缺失值

案例 53: 处理缺失值 知识点讲解 在数据分析中&#xff0c;处理缺失值是一个常见且重要的步骤。Pandas 提供了多种方法来处理 DataFrame 中的缺失值&#xff0c;包括填充缺失值和删除含有缺失值的行或列。 填充缺失值: 使用 fillna 方法可以将缺失值替换为指定的值。删除缺失…