springboot-aop-redis-lua 实现的分布式限流方案
1.自定义限流注解
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Limit {/*** 名字*/String name() default "";/*** key*/String key() default "";/*** Key的前缀*/String prefix() default "";/*** 给定的时间范围 单位(秒)*/int period();/*** 一定时间内最多访问次数*/int count();/*** 限流的类型(用户自定义key 或者 请求ip)*/LimitType limitType() default LimitType.CUSTOMER; }
2. 限流类型
public enum LimitType {/*** 自定义key*/CUSTOMER,/*** 请求者IP*/IP; }
3.redis配置
@Configuration public class RedisLimiterHelper {@Beanpublic RedisTemplate<String, Serializable> limitRedisTemplate(LettuceConnectionFactory redisConnectionFactory) {RedisTemplate<String, Serializable> template = new RedisTemplate<>();template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());template.setConnectionFactory(redisConnectionFactory);return template;} }
4. 限流切面实现
/*** @author* @description 限流切面实现* @date 2020/4/8 13:04*/ @Aspect @Configuration public class LimitInterceptor {private static final Logger logger = LoggerFactory.getLogger(LimitInterceptor.class);private static final String UNKNOWN = "unknown";private final RedisTemplate<String, Serializable> limitRedisTemplate;@Autowiredpublic LimitInterceptor(RedisTemplate<String, Serializable> limitRedisTemplate) {this.limitRedisTemplate = limitRedisTemplate;}/*** @param pjp* @authofuor * @description 切面* @date 2020/4/8 13:04*/@Around("execution(public * *(..)) && @annotation(com..limit.api.Limit)")public Object interceptor(ProceedingJoinPoint pjp) {MethodSignature signature = (MethodSignature) pjp.getSignature();Method method = signature.getMethod();Limit limitAnnotation = method.getAnnotation(Limit.class);LimitType limitType = limitAnnotation.limitType();String name = limitAnnotation.name();String key;int limitPeriod = limitAnnotation.period();int limitCount = limitAnnotation.count();/*** 根据限流类型获取不同的key ,如果不传我们会以方法名作为key*/switch (limitType) {case IP:key = getIpAddress();break;case CUSTOMER:key = limitAnnotation.key();break;default:key = StringUtils.upperCase(method.getName());}ImmutableList<String> keys = ImmutableList.of(StringUtils.join(limitAnnotation.prefix(), key));try {String luaScript = buildLuaScript();RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);Number count = limitRedisTemplate.execute(redisScript, keys, limitCount, limitPeriod);logger.info("Access try count is {} for name={} and key = {}", count, name, key);if (count != null && count.intValue() <= limitCount) {return pjp.proceed();} else {throw new RuntimeException("You have been dragged into the blacklist");}} catch (Throwable e) {if (e instanceof RuntimeException) {throw new RuntimeException(e.getLocalizedMessage());}throw new RuntimeException("server exception");}}/*** @author xiaofu* @description 编写 redis Lua 限流脚本*/public String buildLuaScript() {StringBuilder lua = new StringBuilder();lua.append("local c");lua.append("\nc = redis.call('get',KEYS[1])");// 调用不超过最大值,则直接返回lua.append("\nif c and tonumber(c) > tonumber(ARGV[1]) then");lua.append("\nreturn c;");lua.append("\nend");// 执行计算器自加lua.append("\nc = redis.call('incr',KEYS[1])");lua.append("\nif tonumber(c) == 1 then");// 从第一次调用开始限流,设置对应键值的过期lua.append("\nredis.call('expire',KEYS[1],ARGV[2])");lua.append("\nend");lua.append("\nreturn c;");logger.info("====="+lua.toString());return lua.toString();}/*** @description 获取id地址*/public String getIpAddress() {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String ip = request.getHeader("x-forwarded-for");if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;} }
上述Lua脚本解释:jdk8在书写脚本的时候需要使用字符串拼接的方式,jdk17以及以后可以使用代码片段书写
local c //局部变量
c = redis.call('get',KEYS[1]) //从redis获取key执行的次数
if c and tonumber(c) > tonumber(ARGV[1]) then //获取的key执行的次数是否大于允许的最大值,如果是直接返回,拒绝访问
return c;
end
c = redis.call('incr',KEYS[1]) //key对应的value增加1
if tonumber(c) == 1 then //当前的key是否是第一次
redis.call('expire',KEYS[1],ARGV[2]) //对key设置过期时间
end
return c;
5.测试例子
package com.xiaofu.limit.controller;import com.xiaofu.limit.api.Limit; import com.xiaofu.limit.enmu.LimitType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.atomic.AtomicInteger;@RestController public class LimiterController {private static final AtomicInteger ATOMIC_INTEGER_1 = new AtomicInteger();private static final AtomicInteger ATOMIC_INTEGER_2 = new AtomicInteger();private static final AtomicInteger ATOMIC_INTEGER_3 = new AtomicInteger();@Limit(name = "1111", key = "limitTest", period = 10, count = 3)@GetMapping("/limitTest1")public int testLimiter1() {return ATOMIC_INTEGER_1.incrementAndGet();}@Limit(key = "customer_limit_test", period = 10, count = 3, limitType = LimitType.CUSTOMER)@GetMapping("/limitTest2")public int testLimiter2() {return ATOMIC_INTEGER_2.incrementAndGet();}@Limit(key = "ip_limit_test", period = 10, count = 3, limitType = LimitType.IP)@GetMapping("/limitTest3")public int testLimiter3() {return ATOMIC_INTEGER_3.incrementAndGet();}}
相关文章:
springboot-aop-redis-lua 实现的分布式限流方案
1.自定义限流注解 Target({ElementType.METHOD, ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Inherited Documented public interface Limit {/*** 名字*/String name() default "";/*** key*/String key() default "";/*** Key的前缀*/String…...
C++ realloc()用法及代码示例
C realloc()用法及代码示例 C 中的realloc() 函数重新分配先前分配但尚未释放的内存块。realloc() 函数重新分配先前使用 malloc() 、 calloc() 或 realloc() 函数分配但尚未使用 free() 函数释放的内存。如果新大小为零,则返回的值取决于库的实现。它可能会也可能…...
【Go】gin框架生成压缩包与下载文件
在没有加入下面这串代码之前,下载的压缩包一直为空。遂debug了两个小时。。。 可以在服务端本地创建压缩包。单独将服务端本地的压缩包发送给客户端也是没问题的。但是两个合起来,客户端接收到的压缩包内容就为空了。 期间也尝试了 zipFile.Close() zipW…...

iOS 面试题以及自我理解答案
1、简述push原理,push的证书和其他的有什么不一样? 第 一阶段:BeejiveIM服务器把要发送的消息、目的iPhone的标识打包,发给APNS。 第二阶段:APNS在自身的已注册Push服务 的iPhone列表中,查找有相应标识的iP…...

vue实现自定义滚动条
vue实现自定义滚动条 具体效果如下,这边我用的rem单位,比例是1:40, 先写下页面布局,把原生的滚动条给隐藏掉,给自定义的滑块增加transition: marginLeft 1s linear;可以使左边距过度的更顺滑 .top-box-2::-webkit-scr…...

基于Qt C++的工具箱项目源码,含命令行工具、桌面宠物、文献翻译、文件处理工具、医学图像浏览器、插件市场、设置扩展等工具
一、介绍 1. 基本信息 完整代码下载地址:基于Qt C的工具箱项目源码 TBox是一款基于Qt C的工具箱。用户可以自行选择安装所需的工具(以插件的形式),将TBox打造成专属于自己的效率软件。TBox基本界面展示如下: 2. 使用…...

C# AnimeGANv2 人像动漫化
效果 项目 下载 可执行程序exe下载 源码下载 其他 C# 人像卡通化 Onnx photo2cartoon-CSDN博客...
gateway接口参数加解密
上篇介绍了多种加解密的使用java加密使用 本篇主要介绍在gateway网关中使用对参数解密和返回数据进行加密的操作 原理 下面使用的是AES加密 SHA1withRSA加签 1-用户使用拿到的AES秘钥和RSA私钥。对数据进行加密和加签 2-进行验签和时间的检验 3-将解密的数据返回到具体的调用…...

WorkPlus定制化的局域网会议软件,提供安全稳定的会议体验
在现代商业环境中,迅速而高效的沟通是企业成功的关键要素之一。而在传统的会议模式下,时间成本和地理限制往往给企业带来不小的困扰。针对这一问题,WorkPlus推出了一款创新的局域网会议软件——WorkPlus Meet,旨在为企业创造高效的…...

干货|小白也能自制电子相册赶紧码住~
你是否想拥有一个独一无二的电子相册,却又苦于不知道如何下手?今天教你一个简单的方法,即使你是小白,也能轻松自制电子相册! 一、选择合适的工具 首先,你需要选择一个合适的工具来制作电子相册。有很多工具…...

docker之Harbor私有仓库
目录 一、什么是Harbor 二、Harbor的特性 三、Harbor的构成 1、六个组件 2、七个容器 四、私有镜像仓库的上传与下载 五、部署docker-compose服务 把项目中的镜像数据进行打包持久数据,如镜像,数据库等在宿主机的/data/目录下, 一、什么…...

服务器上部署python脚本
1.查看服务器上的python是否自带,一般都自带 2.将本地脚本上传到服务器 3.直接运行一下脚本看报什么错误 代码错误, 将f删除后报别的错误 上面是未安装依赖的错误。我们安装一下依赖 下面是编码的解决 #!/usr/bin/python # -*- coding: utf-8 -*- 先把…...

【excel技巧】如何在Excel表格中添加选项按钮?
不知道大家是否会9遇到需要勾中选项的情况,我们可以在电子表格中制作出可以勾选、选中的选项按钮,今天我们一起学习一下设置方法。 首先,我们需要先在excel工具栏中添加一个功能模块:开发工具 依次点击excel中的文件 – 选项 –…...

前端 vite+vue3——写一个随机抽奖组件
文章目录 ⭐前言⭐设计布局⭐交互设计⭐整体代码⭐insicode代码 ⭐总结⭐结束 ⭐前言 大家好,我是yma16,本文分享关于前端 vitevue3——写一个抽奖随机组件。 vue3系列相关文章: 前端vue2、vue3去掉url路由“ # ”号——nginx配置 csdn新星计…...

语音芯片基础知识 什么是语音芯 他有什么作用 发展趋势是什么
目录 一、语音芯片的简介 常见的语音芯片有哪些? 语音芯片的种类有很多,大体区分下来也就4个类别而已: 选型的经验说明如下: 推荐使用flash型语音芯片 一、语音芯片的简介 语音芯片基础知识: 什么是语音芯片&…...

设计模式01———简单工厂模式 c#
首先我们打开一个项目 在这个初始界面我们需要做一些准备工作 建基础通用包 创建一个Plane 重置后 缩放100倍 加一个颜色 任务:使用【简单工厂模式】生成四种不同怪物 【按不同路径移动】 首先资源商店下载四个怪物模型 接下来我们选取四个怪物作为预制体并分别起名…...

如何解决MidJourney错过付费后被暂停
问题 假定你已经成功订阅购买了 MidJourney 一段时间,下个月扣费周期到了。 如果你卡里余额不足,卡被封或失效了,或者你想着最近没啥用得上 MidJourney 的地方先省着不续费,等要用的时候就用不了。 如果想要去官网的续费页&…...
考研人考研魂——英语单词篇(20231010)
下一站,上岸 transplanttransportstorestoragestockstridestrikestringstructurestrikingstunprimaryprimeprimitiveprincipalpsychiatryprinciplepsychologyliableliberal transplant n. (器官等的)移植;移植的器官 vt. 移植&a…...

java 版 项目管理工程系统,实现项目全周期管理-源码交付
工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&am…...

TOGAF(企业架构)
TOGAF 核心概念(官方原版) 什么是TOGAF? TOGAF?是一种经验证的企业架构方法和框架,被世界领先的组织用于提高业务效率。它是一个企业架构标准,确保企业架构专业人员之间的标准、方法和通信一致,以便我们…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...