当前位置: 首页 > news >正文

基于Redis限流(aop切面+redis实现“令牌桶算法”)

令牌桶算法属于流量控制算法,在一定时间内保证一个键(key)的访问量不超过某个阈值。这里的关键是设置一个令牌桶,在某个时间段内生成一定数量的令牌,然后每次访问时从桶中获取令牌,如果桶中没有令牌,就拒绝访问。
参考网上一个博主写的:https://blog.csdn.net/xdx_dili/article/details/133683315
注意:我这边只是学习实践加上修改对应的代码记录下而已

第一步:记得要下载redis并配置好

第二步:创建springboot项目并引入maven,配置好配置文件
(注意我这边使用的springboot版本是2.6.x,因为2.7开始博主的部分代码不可用了)

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>21.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.78</version></dependency>
</dependencies>
#application.properties
#redis
spring.redis.host=127.0.0.1
spring.redis.port=6379server.port=8081

第三步:代码部分

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.io.Serializable;/*** RedisTemplate调用实例*/
@Configuration
public class RedisLimiterHelper {//spring会帮助我们注入LettuceConnectionFactory@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;}
}
/*** 限流类型枚举类*/
public enum LimitType {/*** 自定义key*/CUSTOMER,/*** 请求者IP*/IP;
}
import java.lang.annotation.*;/*** 自定义限流注解*/
//@Target({ElementType.METHOD, ElementType.TYPE}) 表示这个注解可以应用到方法和类上。
//ElementType.METHOD 表示这个注解可以应用到方法上,即可以在方法上添加这个注解。
//ElementType.TYPE 表示这个注解可以应用到类上,即可以在类上添加这个注解。
//@Retention(RetentionPolicy.RUNTIME) 表示这个注解的元数据信息(metadata)在运行时可用。
//@Inherited 表示这个注解可以被继承。
//@Documented 表示这个注解的文档信息(documentation)会被自动生成
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RedisLimit {/*** 名字*/String name() default "";/*** key*/String key() default "";/*** Key的前缀*/String prefix() default "";/*** 给定的时间范围 单位(秒)*/int period();/*** 一定时间内最多访问次数*/int count();/*** 限流的类型(用户自定义key 或者 请求ip)*/LimitType limitType() default LimitType.CUSTOMER;}
import org.apache.commons.lang3.StringUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;/*** 限流切面实现*/
@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;}//切面(使用了该RedisLimit注解时触发)@Around("execution(public * *(..)) && @annotation(com.zhangximing.redis_springboot.annotate.RedisLimit)")public Object interceptor(ProceedingJoinPoint pjp) {MethodSignature signature = (MethodSignature) pjp.getSignature();Method method = signature.getMethod();RedisLimit limitAnnotation = method.getAnnotation(RedisLimit.class);LimitType limitType = limitAnnotation.limitType();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());}List<String> keys = Arrays.asList(StringUtils.join(limitAnnotation.prefix(), key));try {//lua脚本String luaScript = buildLuaScript();//获取已调用数量RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);Long count = limitRedisTemplate.execute(redisScript, keys, limitCount, limitPeriod);//判断是否已超过限制if (count != null && count <= limitCount) {return pjp.proceed();} else {throw new RuntimeException("服务忙,请稍后再试");}} catch (Throwable e) {if (e instanceof RuntimeException) {
//                throw new RuntimeException(e.getLocalizedMessage());return e.getLocalizedMessage();}
//            throw new RuntimeException("server exception");return "服务异常";}}//编写 redis Lua 限流脚本public String buildLuaScript() {StringBuilder lua = new StringBuilder();lua.append("local c");//KEYS[1]表示keylua.append("\nc = redis.call('get',KEYS[1])");// 调用不超过最大值,则直接返回 (ARGV[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;");return lua.toString();}//获取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;}
}
import com.zhangximing.redis_springboot.annotate.LimitType;
import com.zhangximing.redis_springboot.annotate.RedisLimit;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicInteger;/*** @Author: zhangximing* @Email: 530659058@qq.com* @Date: 2023/12/20 10:59* @Description: 限流测试类 参考 https://blog.csdn.net/xdx_dili/article/details/133683315*/
@RestController
@RequestMapping("/limit")
public class LimitController {private static final AtomicInteger ATOMIC_INTEGER_1 = new AtomicInteger();//十秒同一IP限制访问5次@RedisLimit(period = 10, count = 5, name = "测试接口", limitType = LimitType.IP)@RequestMapping("/test")public String testLimit(){return "SUCCESS:"+ATOMIC_INTEGER_1.incrementAndGet();}}

效果展示:
在这里插入图片描述

相关文章:

基于Redis限流(aop切面+redis实现“令牌桶算法”)

令牌桶算法属于流量控制算法&#xff0c;在一定时间内保证一个键&#xff08;key&#xff09;的访问量不超过某个阈值。这里的关键是设置一个令牌桶&#xff0c;在某个时间段内生成一定数量的令牌&#xff0c;然后每次访问时从桶中获取令牌&#xff0c;如果桶中没有令牌&#x…...

【小白专用】php pdo方式连接sqlserver 设置方法 更新23.12.21

windows系统的拓展相对来说比较好安装&#xff0c;直接下载对应的dll文件&#xff0c;修改php.ini配置文件即可。 添加PHP对SQL SERVER的支持 1.新建PHP 文件&#xff0c;输入内容&#xff1a; <?php echo phpinfo(); ?> 2.运行后&#xff0c;可以查看到如下数据&…...

打开C#项目时出现“error : 找不到指定的 SDK”的错误解决方法汇总

从github上克隆项目回来&#xff0c;发现编译通过&#xff0c;我是通过一下步骤解决的&#xff1a; 1.到适用于 Visual Studio 的 .NET SDK 下载网址下载对应版本的.NET SDK&#xff0c;关闭当前VS后安装&#xff1b; 2.参考VS2022报错&#xff1a;error : 找不到指定的 SDK“…...

MIT 6.824 练习1

Hi, there! 这是一份根据 MIT 6.824(2021) 课程的第 2 课的课堂示例代码改编的 2 个 go 语言编程练习。像其他的编程作业一样&#xff0c;我去除了核心部分&#xff0c;保留了代码框架&#xff0c;并编写了每一步的提示 练习代码在本文的最后面 爬虫 在第一部分&#xff0c;…...

Git报错x509: certificate signed by unknown authority

下载报错&#xff1a; Error downloading object: model-00001-of-00008.safetensors (ed3ac49): Smudge error: Error downloading model-00001-of-00008.safetensors (ed3ac4983f682a999b0e4b6f072aad294c4fd9a7e968e90835ba5c4b466d3c7c): LFS: Get https://cdn-lfs.huggin…...

OpenCV技术应用(8)— 如何将视频分解

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。本节课就手把手教大家如何将一幅图像转化成热力图&#xff0c;希望大家学习之后能够有所收获~&#xff01;&#x1f308; 目录 &#x1f680;1.技术介绍 &#x1f680;2.实现代码 &#x1f680;1.技术介绍 视频是…...

Swagger2接口测试文档

目录 一、Swagger简介 1.1 Swagger是什么&#xff1f; 1.2 为什么要用Swagger 1.3 Swagger注解 二、Spring集成Swagger 三、测试环境配置 一、Swagger简介 1.1 Swagger是什么&#xff1f; Swagger 是一个开源的 API 设计和文档工具&#xff0c;它可以帮助开发人员更快、…...

【Java】BigDecimal 比较自动化页面获取数据的大小

jwensh2023.12.20 使用背景 对 web3 相关的数据进行计算的时候&#xff0c;需要进行大小加减计算&#xff0c;UI 自动化过程需要将数据转为自然数&#xff1b;页面获取的数据会有千分位 、高精度(18位) /*** Compares this {code BigDecimal} with the specified* {code BigDe…...

开源键盘工程QMK

一、Qmk简介 目录 一、Qmk简介 二、Qmk入门指导文档 三、QMK配置器 四、QMK层的概念 TMK原先是由Jun Wako设计实现...

Elasticsearch的批量bulk 提交 写入的方式会有顺序问题吗?

Elasticsearch的分布式特性可能会导致写入操作的执行顺序与提交顺序稍有不同。在分布式环境中,Elasticsearch将数据分散到不同的节点上进行存储和处理,因此写入操作的执行顺序可能会受到网络延迟、负载均衡等因素的影响。 根源在于ES的分布式架构。如上图所示,客户端的命令首…...

云原生之深入解析如何使用Vcluster Kubernetes加速开发效率

一、背景 为什么一个已经在使用 Kubernetes 本身方面已经很挣扎的开发人员还要处理虚拟集群呢&#xff1f;答案可能会让您感到惊讶&#xff0c;但虚拟集群实际上比单独的物理集群更容易处理&#xff0c;并且与本地 k3d、KinD 或 minikube 部署的集群相比具有相当多的优势。如果…...

PCL 已知同名点对计算旋转矩阵并对点云进行旋转

目录 一、 算法概述二、代码实现三、测试示例一、 算法概述 适用:已知三组及三组以上的同名点对,计算旋转矩阵;然后根据旋转矩阵对点云进行旋转,最后保存旋转后的点云文件。 二、代码实现 #include <Eigen/Core> #include <Eigen/Dense>...

MyBatis ORM映射

MyBatis只能自动维护库表”列名“与”属性名“相同时的对应关系&#xff0c;二者不同时无法自动ORM 因此需要使用到ORM映射。 共有两种解决办法&#xff1a;1.列的别名 2.结果映射 1.列的别名 在SQL中使用 as 为查询字段添加列别名&#xff0c;以匹配属性名 public List<…...

在线客服系统推荐:为何选择Zoho Desk?

客户和企业的关系并不止于一次买卖关系&#xff0c;企业后续持续的服务不仅是对客户的保障&#xff0c;更能收获良好的品牌口碑和持续的良性收益。所以&#xff0c;企业需要在客户旅程的每一个阶段为客户提供优质服务。而在这段服务旅程中&#xff0c;在线客服系统承担了重要的…...

手绘心情树叶,探索情绪世界

人生如同滚雪球&#xff0c;关键在于找到湿润的雪和陡峭的坡。正如巴菲特所言。昨天参与JSTO的经历&#xff0c;让我深有同感。徐老师分享的主题是《手绘心情树叶&#xff0c;探索情绪世界》&#xff0c;发现在JSTO这个平台上&#xff0c;伙伴们的成长速度惊人。从系统的角度看…...

智能优化算法应用:基于水基湍流算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于水基湍流算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于水基湍流算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.水基湍流算法4.实验参数设定5.算法结果6.…...

打开和关闭GBASE南大通用数据库连接

下面的样例代码使用连接字符串通过GBASE南大通用Connection 类创建连接对象、 打开连接、关闭连接GBASE南大通用。 C# 示例&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using Sys…...

Zookeeper 集群搭建过程中常见错误

文章目录 Mode: standalone启动失败 Mode: standalone 这通常表示 Zookeeper 配置为单节点模式&#xff0c;而不是集群模式。需要检查 zoo.cfg 文件中的配置&#xff0c;确保包含了所有集群节点的信息。 启动失败 /usr/bin/java ZooKeeper JMX enabled by default Using con…...

Linux开发工具——vim篇

vim开发工具的使用 文章目录 vim开发工具的使用认识vimvim常用三种模式vim正常模式命令集模式切换移动光标删除文字赋值替换撤销上一次操作更改跳到指定的行 vim末行模式命令集列出行号跳到文件中的某一行&#xff1a;保存文件离开vim查找字符&#xff1a; 总结题外话&#xff…...

基于YOLOv5的吸烟检测系统设计与实现

一、项目背景 吸烟检测作为保障公共健康和环境安全的重要任务之一&#xff0c;一直备受关注。传统的吸烟检测方法往往依赖人工判断&#xff0c;存在准确性低和实时性差的问题。为了解决这些问题&#xff0c;本项目基于深度学习技术进行了吸烟检测系统的设计与实现&#xff0c;…...

FRCRN模型结构解析:频域卷积+循环网络如何协同提升信噪比

FRCRN模型结构解析&#xff1a;频域卷积循环网络如何协同提升信噪比 1. 引言&#xff1a;语音降噪的挑战与突破 语音降噪技术一直面临着"既要又要"的难题&#xff1a;既要彻底消除背景噪声&#xff0c;又要完整保留人声细节。传统的降噪方法往往在这两者之间难以平…...

【英飞凌】TC3XX单片机型号解码:从命名规则看芯片选型

1. 英飞凌TC3XX单片机命名规则解析 第一次接触英飞凌TC3XX系列单片机时&#xff0c;我完全被那一长串型号搞懵了。TC387TP、TC377T、TC397QP...这些看似随机的字母数字组合&#xff0c;其实隐藏着丰富的芯片信息。经过几个项目的实战&#xff0c;我终于摸清了这套命名规则的规律…...

跨境电商多语种支持:SenseVoice-Small ONNX语音识别模型部署与本地化适配

跨境电商多语种支持&#xff1a;SenseVoice-Small ONNX语音识别模型部署与本地化适配 1. 环境准备与快速部署 SenseVoice-Small ONNX模型是一个经过量化处理的高效语音识别解决方案&#xff0c;特别适合跨境电商场景中的多语言语音处理需求。这个模型支持超过50种语言&#x…...

NsEmuTools:开源模拟器管理工具的质量保障与工程实践

NsEmuTools&#xff1a;开源模拟器管理工具的质量保障与工程实践 【免费下载链接】ns-emu-tools 一个用于安装/更新 NS 模拟器的工具 项目地址: https://gitcode.com/gh_mirrors/ns/ns-emu-tools 在开源项目的生命周期中&#xff0c;如何在快速迭代与代码质量之间找到平…...

Wan2.2-T2V-A5B提示词怎么写?新手快速出效果的实用指南

Wan2.2-T2V-A5B提示词怎么写&#xff1f;新手快速出效果的实用指南 1. 认识Wan2.2-T2V-A5B视频生成模型 Wan2.2-T2V-A5B是一款由通义万相开源的轻量级文本到视频生成模型&#xff0c;拥有50亿参数规模。虽然它生成的视频分辨率是480P&#xff0c;但在时序连贯性和运动推理能力…...

Trelby:释放创意生产力的剧本创作解决方案

Trelby&#xff1a;释放创意生产力的剧本创作解决方案 【免费下载链接】trelby The free, multiplatform, feature-rich screenwriting program! 项目地址: https://gitcode.com/gh_mirrors/tr/trelby 当格式规范不再成为创作的枷锁&#xff0c;编剧如何重获灵感自由&am…...

3步告别微信单向好友:WechatRealFriends帮你轻松识别谁删了你

3步告别微信单向好友&#xff1a;WechatRealFriends帮你轻松识别谁删了你 【免费下载链接】WechatRealFriends 微信好友关系一键检测&#xff0c;基于微信ipad协议&#xff0c;看看有没有朋友偷偷删掉或者拉黑你 项目地址: https://gitcode.com/gh_mirrors/we/WechatRealFrie…...

别再拷贝sxs文件夹了!Win10教育版1903安装.NET 3.5最简方案(实测有效)

彻底解决Win10安装.NET 3.5报错0x800F081F的高效方案 每次在Win10上安装.NET Framework 3.5时遇到0x800F081F错误&#xff0c;都让人抓狂。网上那些让你拷贝sxs文件夹的教程&#xff0c;99%都在误导人。作为一位经历过无数次失败的老手&#xff0c;我要分享的是经过上百次验证的…...

OpenPPL之二,优化器里面的算子融合

算子融合的执行时机 完整的时间线 模型加载阶段&#xff08;一次&#xff09; 运行时阶段&#xff08;多次推理&#xff09;↓ ↓ ┌─────────────────────┐ ┌─────────────┐ │ 1. 解析ON…...

利用快马平台快速构建mcporter数据转换工具原型,十分钟验证数据管道设计

最近在做一个数据迁移项目时&#xff0c;遇到了需要频繁转换数据格式的需求。传统方式下&#xff0c;光是搭建开发环境、编写基础代码就要花上大半天时间。这次尝试用InsCode(快马)平台快速构建了一个mcporter数据转换工具原型&#xff0c;整个过程出乎意料地顺畅。 明确核心需…...