基于SpringBoot+Redis实现接口限流

前言

业务中需要对一些接口进行限流处理,防止机器人调用或者保证服务质量;

实现方式

  • 基于redis的lua脚本

引入依赖

<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>

redis配置

package com.qiangesoft.wechat.config;import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** redis配置** @author qiangesoft* @date 2024-03-19*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {@Bean@SuppressWarnings(value = {"unchecked", "rawtypes"})public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}@Beanpublic DefaultRedisScript<Long> limitScript() {DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();redisScript.setScriptText(limitScriptText());redisScript.setResultType(Long.class);return redisScript;}/*** 限流脚本*/private String limitScriptText() {return "local key = KEYS[1]\n" +"local count = tonumber(ARGV[1])\n" +"local time = tonumber(ARGV[2])\n" +"local current = redis.call('get', key);\n" +"if current and tonumber(current) > count then\n" +"    return tonumber(current);\n" +"end\n" +"current = redis.call('incr', key)\n" +"if tonumber(current) == 1 then\n" +"    redis.call('expire', key, time)\n" +"end\n" +"return tonumber(current);";}
}

限流注解

package com.qiangesoft.wechat.config;import java.lang.annotation.*;/*** 限流注解** @author qiangesoft* @date 2024-03-19*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {/*** 限流类型*/LimitType limitType() default LimitType.IP;/*** 限流时间,单位秒*/int time() default 60;/*** 限流次数*/int count() default 10;}

限流实现

package com.qiangesoft.wechat.config;import com.qiangesoft.wechat.utils.IpUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;/*** 限流处理** @author qiangesoft* @date 2024-03-19*/
@RequiredArgsConstructor
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {private final RedisTemplate<Object, Object> redisTemplate;private final RedisScript<Long> limitScript;@Before("@annotation(rateLimiter)")public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {int time = rateLimiter.time();int count = rateLimiter.count();String key = this.buildKey(rateLimiter, point);List<Object> keys = Collections.singletonList(key);try {Long number = redisTemplate.execute(limitScript, keys, count, time);if (number == null || number.intValue() > count) {throw new RuntimeException("访问过于频繁,请稍候再试");}} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new RuntimeException("服务器限流异常,请稍候再试");}}/*** 缓存key** @param rateLimiter* @param point* @return*/public String buildKey(RateLimiter rateLimiter, JoinPoint point) {String limitId = "";if (rateLimiter.limitType() == LimitType.IP) {limitId = IpUtil.getIpAddr();}MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();Class<?> targetClass = method.getDeclaringClass();String key = "rate_limit:" + limitId + "-" + targetClass.getName() + "-" + method.getName();return key;}
}

测试代码

package com.qiangesoft.wechat.controller;import com.qiangesoft.wechat.config.RateLimiter;
import com.qiangesoft.wechat.utils.ResultVO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** 测试接口** @author qiangesoft* @date 2024-03-19*/
@RestController
public class TestController {@RateLimiter@GetMapping("/test")public ResultVO test() {return ResultVO.ok("调用成功!");}}

在这里插入图片描述
继续频繁点击
在这里插入图片描述

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

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

相关文章

stm32之GPIO电路介绍

文章目录 1 GPIO介绍2 GPIO的工作模式2.1 浮空输入2.2 上拉输入2.3 下拉输入2.4 模拟输入2.5 开漏输出2.6 推挽输出2.7 复用开漏输出2.8 复用推挽输出2.9 其他 3 应用方式4 常用库函数 1 GPIO介绍 保护二极管&#xff1a;保护引脚&#xff0c;让引脚的电压位于正常的范围施密特…

Java-设计模式-单例模式

单例模式 从单例加载的时机区分&#xff0c;有懒汉模式/饥饿模式。 从实现方式区分有双重检查模式&#xff0c;内部类模式/Enum模式/Map模式等。在《Effective Java》中&#xff0c;作者提出利用Enum时实现单例模式的最佳实践。 内容概要 实现单例模式的几个关键点 利用Enu…

【Linux(1)】Linux的一些基本指令(补充上一篇)

思维导图 学习内容 通过上面的学习目标&#xff0c;我们可以列出要学习的内容&#xff1a; linux的一些指令&#xff1a;cd mkdir cp touch which rm cat alias 一些基本的概念&#xff1a;指令的概念&#xff0c;用户家目录是什么...... 一、Linux的一些指令 1.1 重新认识…

【机器学习】无监督学习算法之:自编码器

自编码器 1、引言2、自编码器2.1 定义2.2 原理2.3 实现方式2.4 算法公式2.5 代码示例 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c; 今天可以讲一讲 自编码器嘛 小鱼&#xff1a;请说清楚&#xff0c;是什么编码器&#xff1f; 小屌丝&#xff1a;自编码器 小鱼&#…

除了大众点评,中国未来还会产生多少家这样的人工智能公司? - 学习Yelp公司的软件工程-评价和推荐系统

原文作者&#xff1a;Jason Sleight&#xff0c;ML&#xff08;Machine Learning&#xff09;平台集团技术负责人 翻译&#xff1a;数字化营销工兵 了解数据是Yelp成功的重要组成部分。为了将我们的消费者与当地优秀的企业联系起来&#xff0c;我们每天为各种任务提供数百万条建…

前端面试02(JS)

文章目录 前端面试02&#xff08;JS&#xff09;1、js的组成2、js内置对象3、操作数组的方法4、数据类型的检测方法5、闭包是什么6、前端内存泄漏7、事件委托8、基本数据类型和引用数据类型9、原型链10、JS如何实现继承 &#x1f389;写在最后 前端面试02&#xff08;JS&#x…

百度交易中台之系统对账篇

作者 | 天空 导读 introduction 百度交易中台作为集团移动生态战略的基础设施&#xff0c;面向收银交易与清分结算场景&#xff0c;赋能业务、提供高效交易生态搭建。目前支持百度体系内多个产品线&#xff0c;主要包括&#xff1a;度小店、小程序、地图打车、文心一言等。本文…

从零开始搭建游戏服务器 第四节 MongoDB引入并实现注册登录

这里写目录标题 前言正文添加依赖安装MongoDB添加MongoDB相关配置创建MongoContext类尝试初始化DB连接实现注册功能测试注册功能实现登录逻辑测试登录流程 结语下节预告 前言 游戏服务器中, 很重要的一点就是如何保存玩家的游戏数据. 当一个服务端架构趋于稳定且功能全面, 开发…

qt-pdf-viewer-library 编译过程记录

1.qtpdfviewerinitializer.h 中 类模板问题需要修改为下面代码: https://github.com/develtar/qt-pdf-viewer-library 下载代码&#xff1a; 编译出现错误 修改代码&#xff0c;如下: 2.无法触发onViewerLoaded 事件&#xff0c;就是界面无法显示PDF文件 修改下面代码&#…

使用JNDIExploit-1.2-SNAPSHOT.jar复现log4j2详细流程

1.进入到改工具所在的目录&#xff0c;然后cmd打开命令行 查看一下帮助信息 -l 指定开启ladp服务的端口 -p 指定开启http服务的端口 -i 指定开启服务的ip&#xff0c;也就是攻击者的ip&#xff0c;也可以是黑客的公网服务器 因为这里的靶场是部署在kali当中的&#xf…

MySQL数据库操作学习(5)函数和储存过程

文章目录 一、简介二、函数1.格式2.带参数的函数3.定义变量3.如何查询变量值 三、储存过程1.格式2.参数3.in的接入参数4.out的接入参数5.in和out区别 四、应用场景 一、简介 在 SQL 中&#xff0c;函数&#xff08;Function&#xff09;和存储过程&#xff08;Stored Procedur…

web渗透测试漏洞复现:未授权访问漏洞合集

web渗透测试漏洞复现 Active MQ 未授权访问 Atlassian Crowd 未授权访问 CouchDB 未授权访问 Docker 未授权访问 Dubbo 未授权访问 Druid 未授权访问 Elasticsearch 未授权访问 FTP 未授权访问 Hadoop 未授权访问 JBoss 未授权访问 Jenkins 未授权访问 Jupyter Notebook 未授权…

竞争优势:大型语言模型 (LLM) 如何重新定义业务策略

人工智能在内容创作中的突破 在当今快节奏的商业环境中&#xff0c;像 GPT-4 这样的大型语言模型 (LLM) 不再只是一种技术新颖性&#xff1b; 它们已成为重新定义跨行业业务战略的基石。 从增强客户服务到推动创新&#xff0c;法学硕士提供了企业不容忽视的竞争优势。 1. 加强…

设计模式中的UML基础

设计模式中的UML基础 目录 1、UML概述 2、UML的用途 3、UML的构成 4、UML图 5、UML类图 5.1、类的构成 5.2、类与类之间的关系 6、绘制UML图的软件工具 在讲解设计模式时&#xff0c;会使用到UML建模中的类图去讲解类与类之间的关系&#xff0c;所以这里需要给大家普…

蓝桥杯day4刷题日记

P8605 [蓝桥杯 2013 国 AC] 网络寻路 思路来源于https://www.luogu.com.cn/article/iat8irsf #include <iostream> using namespace std; int n,m; int q[10010]; int v[100010],u[100010]; long long res;int main() {cin>>n>>m;for(int i0;i<m;i){cin…

【SpringSecurity】十三、基于Session实现授权认证

文章目录 1、基于session的认证2、Demosession实现认证session实现授权 1、基于session的认证 流程&#xff1a; 用户认证成功后&#xff0c;服务端生成用户数据保存在session中服务端返回给客户端session id (sid&#xff09;&#xff0c;被客户端存到自己的cookie中客户端下…

Android Studio实现内容丰富的安卓校园二手交易平台(带聊天功能)

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 项目编号083 1.开发环境android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.查看二手商品列表 3.发布二手商品 4.商品详情 5.聊天功能…

NodeJS 中的设计模式

Node.js 是一个流行的 JavaScript 运行时&#xff0c;允许开发者使用事件驱动、非阻塞 I/O 模型构建可扩展的网络应用程序。和任何复杂的框架一样&#xff0c;Node.js 应用程序可以从使用成熟的设计模式中受益&#xff0c;以促进代码重用、可维护性和健壮性。本文将概述一些对 …

在微信小程序中或UniApp中自定义tabbar实现毛玻璃高斯模糊效果

backdrop-filter: blur(10px); 这一行代码表示将背景进行模糊处理&#xff0c;模糊程度为10像素。这会导致背景内容在这个元素后面呈现模糊效果。 background-color: rgb(255 255 255 / .32); 这一行代码表示设置元素的背景颜色为白色&#xff08;RGB值为0, 0, 0&#xff09;&a…

docker批量删除容器或镜像

删除容器 停止所有容器 删除所有容器&#xff0c;需要先停止所有运行中的容器 docker stop docker ps -a -q docker ps -a -q&#xff0c;意思是列出所有容器(包括未运行的)&#xff0c;只显示容器编号&#xff0c;其中 -a : 显示所有的容器&#xff0c;包括未运行的。 …