Redis+Lua脚本+AOP+反射+自定义注解,打造我司内部基础架构限流组件

定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface RedisLimitAnnotation
{/*** 资源的key,唯一* 作用:不同的接口,不同的流量控制*/String key() default "";/*** 最多的访问限制次数*/long permitsPerSecond() default 3;/*** 过期时间(计算窗口时间),单位秒默认30*/long expire() default 30;/*** 默认温馨提示语*/String msg() default "default message:系统繁忙or你点击太快,请稍后再试,谢谢";
}

定义AOP

import com.atguigu.interview2.annotations.RedisLimitAnnotation;
import com.atguigu.interview2.exception.RedisLimitException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import org.springframework.core.io.ClassPathResource;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;@Slf4j
@Aspect
@Component
public class RedisLimitAop
{Object result = null;@Resourceprivate StringRedisTemplate stringRedisTemplate;private DefaultRedisScript<Long> redisLuaScript;@PostConstructpublic void init(){redisLuaScript = new DefaultRedisScript<>();redisLuaScript.setResultType(Long.class);redisLuaScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua")));}@Around("@annotation(com.atguigu.interview2.annotations.RedisLimitAnnotation)")public Object around(ProceedingJoinPoint joinPoint){System.out.println("---------环绕通知1111111");MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();//拿到RedisLimitAnnotation注解,如果存在则说明需要限流,容器捞鱼思想RedisLimitAnnotation redisLimitAnnotation = method.getAnnotation(RedisLimitAnnotation.class);if (redisLimitAnnotation != null){//获取redis的keyString key = redisLimitAnnotation.key();String className = method.getDeclaringClass().getName();String methodName = method.getName();String limitKey = key +"\t"+ className+"\t" + methodName;log.info(limitKey);if (null == key){throw new RedisLimitException("it's danger,limitKey cannot be null");}long limit = redisLimitAnnotation.permitsPerSecond();long expire = redisLimitAnnotation.expire();List<String> keys = new ArrayList<>();keys.add(key);Long count = stringRedisTemplate.execute(redisLuaScript,keys,String.valueOf(limit),String.valueOf(expire));System.out.println("Access try count is "+count+" \t key= "+key);if (count != null && count == 0){System.out.println("启动限流功能key: "+key);return redisLimitAnnotation.msg();}}try {result = joinPoint.proceed();//放行} catch (Throwable e) {throw new RuntimeException(e);}System.out.println("---------环绕通知2222222");System.out.println();System.out.println();return result;}}

lua脚本

rateLimiter.lua放在resource目录下

--获取KEY,针对那个接口进行限流,Lua脚本中的数组索引默认是从1开始的而不是从零开始。
local key = KEYS[1]
--获取注解上标注的限流次数
local limit = tonumber(ARGV[1])local curentLimit = tonumber(redis.call('get', key) or "0")--超过限流次数直接返回零,否则再走else分支
if curentLimit + 1 > limit
then return 0
-- 首次直接进入
else-- 自增长 1redis.call('INCRBY', key, 1)-- 设置过期时间redis.call('EXPIRE', key, ARGV[2])return curentLimit + 1
end

接口使用

@Slf4j
@RestController
public class RedisLimitController
{/*** Redis+Lua脚本+AOP+反射+自定义注解,打造我司内部基础架构限流组件* 在redis中,假定一秒钟只能有3次访问,超过3次报错* key = redisLimit* Value = permitsPerSecond设置的具体值* 过期时间 = expire设置的具体值,* permitsPerSecond = 3, expire = 10* 表示本次10秒内最多支持3次访问,到了3次后开启限流,过完本次10秒钟后才解封放开,可以重新访问*/@GetMapping("/redis/limit/test")@RedisLimitAnnotation(key = "redisLimit", permitsPerSecond = 3, expire = 10, msg = "当前访问人数较多,请稍后再试,自定义提示!")public String redisLimit(){return "正常业务返回,订单流水:"+ IdUtil.fastUUID();}
}

测试效果

连续刷新几次调接口,第四次就会限流
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于公司不能用第三方组件来限流的时候,这个方法很哇塞,下课!

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA毕业设计152—基于Java+Springboot+vue+小程序的个人健康管理系统小程序(源代码+数据库+15000字论文)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringbootvue小程序的个人健康管理系统小程序(源代码数据库15000字论文)152 一、系统介绍 本项目前后端分离带小程序(可以改为ssm版本)&#xff0c;分为用户、管理员两种…

ARM功耗管理之功耗和安全

安全之安全(security)博客目录导读 思考&#xff1a;功耗与安全&#xff1f;超频攻击&#xff1f;欠压攻击&#xff1f;低功耗流程中的安全&#xff1f; 睡眠唤醒流程中&#xff0c;安全相关寄存器的备份恢复 举例&#xff1a;比如某DMA通道&#xff0c;芯片逻辑默认为安全通…

深入了解jdbc-02-CRUD

文章目录 操作和访问数据库Statement操作数据表的弊端sql注入问题PreparedStatement类ResultSet类与ResultSetMetaData类资源的释放批量插入 操作和访问数据库 数据库的调用的不同方式: Statement&#xff1a;用于执行静态 SQL 语句并返回它所生成结果的对象。PreparedStatem…

《0基础》学习Python——第二十四讲__爬虫/<7>深度爬取

一、深度爬取 深度爬取是指在网络爬虫中&#xff0c;获取网页上的所有链接并递归地访问这些链接&#xff0c;以获取更深层次的页面数据。 通常&#xff0c;一个简单的爬虫只会获取到初始页面上的链接&#xff0c;并不会进一步访问这些链接上的其他页面。而深度爬取则会不断地获…

Java:115-Spring Boot的底层原理(下篇)

这里续写上一章博客&#xff08;115章博客&#xff09; SpringBoot视图技术&#xff1a; 支持的视图技术 &#xff1a; 前端模板引擎技术的出现&#xff08;jsp也是&#xff09;&#xff0c;使前端开发人员无需关注后端业务的具体实现&#xff08;jsp中&#xff0c;具体的…

IPython魔法命令的深入应用

目录 IPython魔法命令的深入应用 一、魔法命令基础 1. 魔法命令的分类 2. 基本使用 二、高级应用技巧 1. 数据交互与处理 2. 交互式编程与调试 三、魔法命令的进阶操作 1. 自定义魔法命令 2. 利用魔法命令优化工作流程 四、总结与展望 IPython魔法命令的深入应用 IP…

ICP配准两个obj三维物体+关键点处形成立体小球球 +TRF算法(含有在ICP配准情境下的算法对应代码)

import os import shutil import numpy as np import cv2 import face_alignment import vtk from scipy.spatial import cKDTree from scipy.optimize import least_squaresdef load_obj(file_path):vertices = []faces = []with open

[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-24 基于FPGA简易示波器显示驱动设计

软件版本&#xff1a;Anlogic -TD5.9.1-DR1_ES1.1 操作系统&#xff1a;WIN10 64bit 硬件平台&#xff1a;适用安路(Anlogic)FPGA 实验平台&#xff1a;米联客-MLK-L1-CZ06-DR1M90G开发板 板卡获取平台&#xff1a;https://milianke.tmall.com/ 登录“米联客”FPGA社区 ht…

解析Type-C母座与Type-C公头:特点与区别

解析Type-C母座与Type-C公头&#xff1a;特点与区别 在数字连接领域&#xff0c;Type-C接口因其高速、多功能等特点备受瞩目。然而&#xff0c;在Type-C连接器中&#xff0c;母座和公头作为两个重要组成部分&#xff0c;却有着各自独特的特点和用途。本文将深入探讨Type-C母座…

编程中的智慧四:设计模式总览

前面三篇我们通过从一些零散的例子&#xff0c;和简单应用来模糊的感受了下设计模式在编程中的智慧&#xff0c;从现在开始正式进入设计模式介绍&#xff0c;本篇将从设计模式的7大原则、设计模式的三大类型、与23种设计模式的进行总结&#xff0c;和描述具体意义。 设计模式体…

新形势下职业教育人工智能人才培养策略

一、引言 随着人工智能技术的飞速发展&#xff0c;社会对人工智能人才的需求日益增长。职业教育作为培养实用型人才的重要基地&#xff0c;应积极响应市场需求&#xff0c;加强人工智能人才的培养。然而&#xff0c;当前职业教育在人工智能人才培养方面仍存在一些问题&#xf…

C++树形结构(总)

目录 一.基础&#xff1a; 1.概念&#xff1a; 2.定义&#xff1a; Ⅰ.树的相关基础术语&#xff1a; Ⅱ.树的层次&#xff1a; 3.树的性质&#xff1a; 二.存储思路&#xff1a; 1.结构体存储&#xff1a; 2.数组存储&#xff1a; 三.树的遍历模板&#xff1a; 四.…

App用户从哪来?Xinstall全渠道数据统计告诉你答案!

在移动互联网时代&#xff0c;App已经成为了我们日常生活中不可或缺的一部分。然而&#xff0c;对于App运营者来说&#xff0c;如何了解用户的来源&#xff0c;从而优化推广策略&#xff0c;一直是一个难题。今天&#xff0c;我们就来科普一下App用户来源分析的重要性&#xff…

使用idea创建Javaweb项目(步骤)

第一步创建Javaweb项目 File>New>Project 第二步 勾选Web Application >Next 然后就是进行起名&#xff0c;完成。 完成创建项目&#xff0c;检查是否文件齐全 配置tomcat 配置好&#xff0c;就能启动tomcat&#xff0c;显示首页 导入jar包。导入进项目&#xf…

博客建站4 - ssh远程连接服务器

1. 什么是SSH?2. 下载shh客户端3. 配置ssh密钥4. 连接服务器5. 常见问题 5.1. IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! 1. 什么是SSH? SSH&#xff08;Secure Shell&#xff09;是一种加密的网络协议&#xff0c;用于在不安全的网络中安全地远程登录到其他…

LC617-合并二叉树

文章目录 1 题目描述2 思路优化代码完整输入输出 参考 1 题目描述 https://leetcode.cn/problems/merge-two-binary-trees/description/ 给你两棵二叉树&#xff1a; root1 和 root2 。 将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另…

解决 elementUI 组件在 WebStorm 中显示为未知标签的问题

解决 elementUI 组件在 WebStorm 中显示为未知标签的问题 一、问题 自从转到 ts 之后&#xff0c;编辑器就一直提示用到的 elementUI 标签未知&#xff0c;一直显示一溜黄色警示&#xff0c;很烦&#xff1a; 二、解决 把它改成大写就可以了。 如下&#xff1a; 把整个项目…

视频编码中算术编码原理详解

介绍 最近研究 CABAC 熵编码原理&#xff0c;因此在剖析 CABAC 熵编码原理之前&#xff0c;先复习下算术编码的原理。算术编码是图像压缩的主要算法之一。 是一种无损数据压缩方法&#xff0c;也是一种熵编码的方法。和其它熵编码方法不同的地方在于&#xff0c;其他的熵编码方…

2024视频改字祝福 豪车装X系统源码uniapp前端源码

源码介绍 uniapp视频改字祝福 豪车装X系统源码 全开源,只有uniapp前端&#xff0c;API接口需要寻找对应的。 创意无限&#xff01;AI视频改字祝福&#xff0c;豪车装X系统源码开源&#xff0c;打造个性化祝福视频不再难&#xff01; 想要为你的朋友或家人送上一份特别的祝福…

使用 Nginx 统计固定源 IP 访问项目的时间

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…