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.mzph.cn/pingmian/48855.shtml

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

相关文章

算法日记day 17(二叉树的最大、最小深度)

一、二叉树的最大深度 题目&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1…

STM32智能机器人控制系统教程

目录 引言环境准备智能机器人控制系统基础代码实现&#xff1a;实现智能机器人控制系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与导航系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;机器人控制与优化问题解决方案与优化收尾与总结 1. 引言 智能机器人控…

qt中charts图表的使用方法

折线图 #include "widget.h" #include "ui_widget.h" #include <QtCharts/QChart> #include <QtCharts/QChartView> #include <QtCharts/QLineSeries> #include<QVBoxLayout>Widget::Widget(QWidget *parent): QWidget(parent), …

Windows图形界面(GUI)-MFC-C/C++ - MFC项目工程框架解析

公开视频 -> 链接点击跳转公开课程博客首页 -> e​​​​​​链接点击跳转博客主页 目录 MFC项目 项目选择 配置安装 程序引导 MFC框架 环境设置 程序框架 代码编写 MFC解析 程序入口 执行流程 代码结构 应用程序类 窗口框架类 消息处理 消息类型 消息…

ML.Net 学习之使用经过训练的模型进行预测

什么是ML.Net&#xff1a;&#xff08;学习文档上摘的一段&#xff1a;ML.NET 文档 - 教程和 API 参考 | Microsoft Learn 【学习入口】&#xff09; 它使你能够在联机或脱机场景中将机器学习添加到 .NET 应用程序中。 借助此功能&#xff0c;可以使用应用程序的可用数据进行自…

一个简单好用安全的开源交互审计系统,支持SSH,Telnet,Kubernetes协议(带私活)

前言 在当今的企业网络环境中&#xff0c;远程访问和交互审计成为了保障网络安-全的重要组成部分。然而&#xff0c;现有的解-决方案往往存在一些痛点&#xff0c;如复杂的配置、有限的协议支持、以及审计功能的不足。这些问题不仅增加了IT管理员的负担&#xff0c;也为企业的…

基于R语言复杂数据回归与混合效应模型【多水平/分层/嵌套】技术与代码

回归分析是科学研究特别是生态学领域科学研究和数据分析十分重要的统计工具&#xff0c;可以回答众多科学问题&#xff0c;如环境因素对物种、种群、群落及生态系统或气候变化的影响&#xff1b;物种属性和系统发育对物种分布&#xff08;多度&#xff09;的影响等。纵观涉及数…

HarmonyOS NEXT零基础入门到实战-第四部分

自定义组件: 概念: 由框架直接提供的称为 系统组件&#xff0c; 由开发者定义的称为 自定义组件。 源代码&#xff1a; Component struct MyCom { build() { Column() { Text(我是一个自定义组件) } } } Component struct MyHeader { build() { Row(…

路由器ip地址脱机是什么意思?怎么应对

在数字化时代&#xff0c;路由器作为家庭或企业网络连接的核心设备&#xff0c;其稳定性和连通性对于我们的网络体验至关重要。然而&#xff0c;有时我们可能会遇到路由器IP地址显示脱机的情况&#xff0c;这不仅影响了我们的网络访问&#xff0c;还可能对工作和娱乐造成不便。…

【C语言】 约瑟夫环,循环链表实现

1、循环链表实现约瑟夫环&#xff0c;每次经过特定步数删除一个元素 //looplist.h #ifndef LOOPLIST_H #define LOOPLIST_H #include<stdio.h> #include<string.h> #include<stdlib.h>typedef int datatype;typedef struct Node {union {int len;datatype d…

Elasticsearch:Java ECS 日志记录 - log4j2

ECS 记录器是你最喜欢的日志库的格式化程序/编码器插件。它们可让你轻松将日志格式化为与 ECS 兼容的 JSON。ECS 兼容的 JSON 日志记录可以帮我们简化很多分析&#xff0c;可视化及解析的工作。在今天的文章里&#xff0c;我来详述如何在 Java 应用里生成 ECS 相兼容的日志。 …

tensorflow keras Model.fit returning: ValueError: Unrecognized data type

题意&#xff1a;TensorFlow Keras 的 Model.fit 方法返回了一个 ValueError&#xff0c;提示数据类型无法识别 问题背景&#xff1a; Im trying to train a keras model with 2 inputs: an image part thats a tf.data.Dataset and a nor mal part represented by a pd.DataF…

【中项】系统集成项目管理工程师-第4章 信息系统架构-4.5技术架构

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

数据结构之判断平衡二叉树详解与示例(C,C++)

文章目录 AVL树定义节点定义计算高度获取平衡因子判断是否为平衡二叉树完整示例代码结论 在计算机科学中&#xff0c;二叉树是一种非常重要的数据结构。它们被广泛用于多种算法中&#xff0c;如排序、查找等。然而&#xff0c;普通的二叉树在极端情况下可能退化成链表&#xff…

C#基于SkiaSharp实现印章管理(4)

前几篇文章实现了绘制不同外形印章的功能&#xff0c;印章内部一般包含圆形、线条等形状&#xff0c;有些印章内部还有五角星&#xff0c;然后就是各种样式的文字。本文实现在印章内部绘制圆形、线条、矩形、椭圆等四种形状。   定义FigureType枚举记录印章内部形状&#xff…

pcie数据传输

一 数据传输通道总体设计 在上传数据时首先将 FPGA 中数据缓存到 DDR3 存储器&#xff0c;然后上位机请求后把数据从DDR3 存储器中取出并通过 PCIE 总线将数据传输到上位机&#xff1b;在下传数据时上位机中的数据首先通过 PCIE 总线下传至 FPGA&#xff0c;FPGA 读取这些数据并…

小程序内嵌uniapp页面跳转回小程序指定页面方式

使用微信小程序提供的Api&#xff1a;wx.miniProgram.navigateTo 在小程序中嵌套uniapp的H5页面&#xff0c;并使用wx.miniProgram.navigateTo进行页面跳转&#xff0c;需要确保满足以下条件&#xff1a; 你的小程序必须是通过uniapp构建的&#xff0c;并且支持小程序嵌套。 你…

系统架构设计师教程 第3章 信息系统基础知识-3.8 典型信息系统架构模型-解读

系统架构设计师教程 第3章 信息系统基础知识-3.8 典型信息系统架构模型-解读 3.8.1 政府信息化与电子政务3.8.1.1 电子政务的概念3.8.1.2 电子政务的内容3.8.1.2.1 政府与政府3.8.1.2.2 政府对企/事业单位3.8.1.2.3 政府对居民3.8.1.2.4 企业对政府3.8.1.2.5 居民对政府 3.8.1…

【HarmonyOS】HarmonyOS NEXT学习日记:六、渲染控制、样式结构重用

【HarmonyOS】HarmonyOS NEXT学习日记&#xff1a;六、渲染控制、样式&结构重用 渲染控制包含了条件渲染和循环渲染&#xff0c;所谓条件渲染&#xff0c;即更具状态不同&#xff0c;选择性的渲染不同的组件。 而循环渲染则是用于列表之内的、多个重复元素组成的结构中。 …

RabbitMQ的学习和模拟实现|GTest测试框架的介绍和简单使用

GTest 项目仓库&#xff1a;https://github.com/ffengc/HareMQ GTest GTest是什么我们需要学习的GTest功能宏断言事件机制 全局测试套件独立测试套件 GTest是什么 GTest是一个跨平台的 C单元测试框架&#xff0c;由google公司发布。gtest是为了在不同平台上为编写C单元测…