springcloud gateway网关动态配置限流

上一篇记录了gateway网关的基础功能和配置,并且使用了默认的限流功能。

springcloud gateway网关-CSDN博客

这里简单记录一下gateway网关集成mybatisPlus实现动态限流。gateway网关默认的限流方式各项限流参数都是在配置文件中配置,不够灵活,虽然使用阿里的Sentinel组件可以实现从nacos注册中心、配置中心动态读取配置,但是还是有一定的局限性。

有些业务上需要限流功能可以在平台的页面上进行灵活配置,并且实时生效。

大致流程:数据库添加一个流控表,有需要限流的URL,最大限流限制数、时间范围等字段。通过页面维护这个表的数据。gateway中写一个全局过滤器中,收到请求后,用URL去数据库中查询、或者从缓存查询,得到需要限制的参数,再调用写好的限流方法实现限流。限流方法用Redis的Zset数据结构实现的滑动窗口算法,当然,也可以用其他的限流算法。

下面的配置是基于上一篇文章的代码来实现。

一、pom文件中添加依赖

mybatisPlus相关依赖

        <!-- 数据库驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency><!-- druid数据库连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency><!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><!-- druid数据库连接池 需要用到该依赖 ,否则启动报错--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency>

二、配置文件

server:port: 8089spring:application:name: gatewaydatasource:url: jdbc:mysql://127.0.0.1:3306/test-db?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghaiusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid: # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置)# 连接池的配置信息# 初始化大小,最小,最大initial-size: 5min-idle: 5maxActive: 20# 配置获取连接等待超时的时间maxWait: 60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒timeBetweenEvictionRunsMillis: 60000# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1testWhileIdle: truetestOnBorrow: falsetestOnReturn: false# 打开PSCache,并且指定每个连接上PSCache的大小poolPreparedStatements: truemaxPoolPreparedStatementPerConnectionSize: 20# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙#filters: stat,wall,slf4j# 通过connectProperties属性来打开mergeSql功能;慢SQL记录connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000webStatFilter:enabled: true########## Redis ############redis:database: 0host: 127.0.0.1port: 6379password:########## gateway 相关配置 ############cloud:gateway:routes:- id: service-01uri: http://127.0.0.1:8080predicates:- Path=/svs1/**filters:- StripPrefix=1 # 去掉path前缀,1代表去掉第一个- name: RequestRateLimiterargs:redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充数redis-rate-limiter.burstCapacity: 1 #令牌容量key-resolver: "#{@apiKeyResolver}" # 限流策略,对应配置中的Bean- id: service-02uri: http://127.0.0.1:8080predicates:- Path=/svs2/**filters:- StripPrefix=1#mybatis plus 设置
mybatis-plus:mapper-locations: classpath:mapper/*.xmlglobal-config:# 关闭MP3.0自带的bannerbanner: falsedb-config:#主键类型  0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)";id-type: AUTO# 默认数据库表下划线命名table-underline: trueconfiguration:# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 返回类型为Map,显示null对应的字段call-setters-on-nulls: true

三、相关代码

1)redis工具类

package com.zhh.gateway.common.util;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;/*** @Description: Redis缓存* @Author: zhaoheng* @CreateTime: 2024-03-13  21:10*/
@Component
public class RedisCache {public static final String SYS_PREFIX = "gateway:";@Autowiredprivate RedisTemplate redisTemplate;public String getZSetKey(String key) {return SYS_PREFIX + "zset:" + key;}/*** zSet数据结构添加数据* @param key		唯一标识* @param value		值* @param score		分值,用于排序* @param expireTime	过期时间,单位:秒* @param <T>*/public <T> void zSetAdd(String key, T value, double score, long expireTime) {key = getZSetKey(key);ZSetOperations zSetOps = redisTemplate.opsForZSet();zSetOps.add(key, value, score);zSetOps.getOperations().expire(key, expireTime, TimeUnit.SECONDS);}/*** 删除指定范围内的数据* @param key		唯一标识* @param min		最小值* @param max		最大值* @return*/public Long zSetRemoveRangeByScore(String key, double min, double max) {return redisTemplate.opsForZSet().removeRangeByScore(getZSetKey(key),min,max);}/*** 统计数据总量* @param key		唯一标识* @return*/public Long zSetCountAll(String key) {return redisTemplate.opsForZSet().zCard(getZSetKey(key));}
}

2)限流过滤器实现

核心就是Redis实现的滑动窗口的限流算法

package com.zhh.gateway.filter;import com.zhh.gateway.common.util.RedisCache;
import com.zhh.gateway.pojo.ApiLimiterPO;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;/*** @Description: 全局过滤器 限流过滤器* @Author: zhaoheng* @CreateTime: 2024*/
@Slf4j
@Component
public class ApiLimiterFilter implements GlobalFilter, Ordered {@Autowiredprivate RedisCache redisCache;@SneakyThrows@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String url = exchange.getRequest().getPath().value();log.info("request url:{}", url);// TODO 从请求头或cookie中获取签名,解析得到用户唯一标识String userId = "zs";this.apiLimiterByUser(url, userId);return chain.filter(exchange);}/*** IApiLimiterService* 过滤器执行顺序,值越小越靠前** @return*/@Overridepublic int getOrder() {return 0;}/*** 根据用户唯一标识限流** @param reqUrl 请求* @param userId 用户唯一标识* @throws Exception*/public void apiLimiterByUser(String reqUrl, String userId) throws Exception {// TODO 根据URL从数据库中查询限流相关配置//ApiLimiterPO apiLimiterPO = iApiLimiterService.getByUrl(reqUrl);// 模拟从数据库中查询到的数据ApiLimiterPO apiLimiterPO = ApiLimiterPO.builder().apiUrl("/api/v1.0/user/query")// 限流:1秒钟最多2个请求.rangeTime(1).limitMax(2).build();// 没有查询到数据,说明该接口没有配置限流if (null == apiLimiterPO) {log.info("无需限流,url:{}", reqUrl);return;}log.info("apiLimiterPO:{}", apiLimiterPO.toString());// 根据用户id限流String key = "xl:" + userId;// 时间窗口大小 限流:【rangeTime】秒钟最多【limitMax】个请求int rangeTime = apiLimiterPO.getRangeTime();// 流量大小int limitMax = apiLimiterPO.getLimitMax();// 当前时间long now = System.currentTimeMillis();// Redis实现滑动窗口算法  删除【rangeTime】秒之前的数据redisCache.zSetRemoveRangeByScore(key, 0, now - (rangeTime * 1000));// 添加请求数据到Redis,设置过期时间redisCache.zSetAdd(key, now, now, 60 * 60);// 统计总数据量Long sum = redisCache.zSetCountAll(key);if (sum > limitMax) {// TODO 一般都是自定义异常,然后全局异常处理器再统一返回错误信息给调用端throw new Exception("请稍后再试!");}}
}

这就完事!

读取数据库相关的简单业务代码就不做过多展示了,具体细节也是根据业务而定,这里只记录一下实现思路和核心流控代码。

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

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

相关文章

【LLMs+小羊驼】23.03.Vicuna: 类似GPT4的开源聊天机器人( 90%* ChatGPT Quality)

官方在线demo: https://chat.lmsys.org/ Github项目代码&#xff1a;https://github.com/lm-sys/FastChat 官方博客&#xff1a;Vicuna: An Open-Source Chatbot Impressing GPT-4 with 90% ChatGPT Quality 模型下载: https://huggingface.co/lmsys/vicuna-7b-v1.5 | 所有的模…

STM32输入捕获频率和占空比proteus仿真失败

这次用了两天的时间来验证这个功能&#xff0c;虽然实验没有成功&#xff0c;但是也要记录一下&#xff0c;后面能解决了&#xff0c;回来再写上解决的办法&#xff1a; 这个程序最后的实验结果是读取到的CCR1和CCR2的值都是0&#xff0c;所以没有办法算出来频率和占空比。 还…

如何创建Gitflow图表

如何创建Gitflow图表 drawio是一款强大的图表绘制软件&#xff0c;支持在线云端版本以及windows, macOS, linux安装版。 如果想在线直接使用&#xff0c;则直接输入网址drawon.cn或者使用drawon(桌案), drawon.cn内部完整的集成了drawio的所有功能&#xff0c;并实现了云端存储…

排序算法之快速排序算法介绍

目录 快速排序介绍 时间复杂度和稳定性 代码实现 C语言实现 c实现 java实现 快速排序介绍 快速排序(Quick Sort)使用分治法策略。 它的基本思想是&#xff1a;选择一个基准数&#xff0c;通过一趟排序将要排序的数据分割成独立的两部分&#xff1b;其中一部分的所有数据…

确定比赛名次-拓扑排序

题目链接&#xff1a;https://vjudge.net/problem/HDU-1285 邻接矩阵存储版 #include<iostream> #include<queue> #include<cstring> #include<functional> #include<vector> using namespace std; const int N 510; int n, m;int g[N][N], i…

CSS 面试题及答案

CSS 面试题及答案 问题&#xff1a;什么是盒模型&#xff08;Box Model&#xff09;&#xff1f; 答案&#xff1a; 盒模型是指在网页布局时&#xff0c;每个元素都被看作一个矩形盒子&#xff0c;包括内容区域、内边距、边框和外边距。这些部分组合在一起构成了元素的盒模型。…

C++Qt学习——添加资源文件

目录 1、创建好了文件之后&#xff0c;在左边空白处按下CtrlN&#xff0c;创建Qt 以及Qt Resource File 2、写入名称&#xff0c;点击下一步 3、可以发现已经创建好啦。 4、点击Add Prefix 5、写上前缀&#xff0c;最好加上斜杠 6、选择提前放好的图片或者icon 7、发…

安卓Java面试题 91- 100

91. 请描述一下Intent 和 IntentFilter ?Intent是组件的通讯使者,可以在组件间传递消息和数据。 IntentFilter是intent的筛选器,可以对intent的action,data,catgory,uri这些属性进行筛选,确定符合的目标组件🚀🚀🚀🚀🚀🚀92. 阐述什么是IntentService?有何优…

Python异步编程探究:深入理解asyncio的使用和原理【第130篇—asyncio】

Python异步编程探究&#xff1a;深入理解asyncio的使用和原理 随着计算机应用程序的复杂性不断增加&#xff0c;对于高效处理I/O密集型任务的需求也越来越迫切。在Python中&#xff0c;asyncio模块提供了一种强大的异步编程方式&#xff0c;使得开发者能够轻松地处理并发任务&…

Vulnhub - DC-1

希望和各位大佬一起学习&#xff0c;如果文章内容有错请多多指正&#xff0c;谢谢&#xff01; 个人博客链接&#xff1a;CH4SER的个人BLOG – Welcome To Ch4sers Blog DC-1 靶机下载地址&#xff1a;DC: 1 ~ VulnHub 0x01 信息收集 Nmap扫描目标主机&#xff0c;发现开…

Springboot 使用【过滤器】实现在请求到达 Controller 之前修改请求体参数和在结果返回之前修改响应体

文章目录 前情提要解决方案自定义 HttpServletRequest 包装类 RequestWrapper自定义 HttpServletResponse 包装类 ResponseWrapper自定义过滤器 MiddlewareFilter配置过滤器注解配置类 编写 Controller 测试 前情提要 在项目中需要使用过滤器 在请求调用 Controller 方法前修改…

Linux-轻量级数据库sqlite-015

1【sqlite】安装 1.让虚拟机能够上网 2.【apt-get】工具集配置好 3.【sudo apt-get install sqlite3】 4.启动【sqlite3】 【sqlite3】 5.输入【.quit】退出2数据库简介 2.1常见的几种数据结构及数据库 1.数组、链表、文件、数据库&#xff08;1&#xff09;存储位置及存储…

volatile关键字用处和场景?

什么是volatile关键字 在C语言和Java中&#xff0c;它是一种特殊的类型修饰符&#xff0c;用来告诉编译器该变量可能会被意外地改变。这样&#xff0c;每次存取该变量时&#xff0c;编译器都不会对其进行缓存优化&#xff0c;而是直接从内存中读取或写入&#xff0c;提供了对特…

面试题 --- jdbc执行流程、MyBatis执行流程、MyBatis拦截器配置流程

jdbc执行流程 1. 注册驱动 2. 创建数据库操作对象 3. 执行sql语句 4 .处理操作结果 5 .关闭连接释放资源 MyBatis 执行流程 Executor执行器、MappedStatement 对象、 StatementHandler 语句处理器 关系可以用以下步骤概括 用户通过 SqlSession 调用一个方法&#xff0c;Sq…

GC-垃圾回收

一、what&#xff08;概念&#xff09; garbage collection 二、where&#xff08;作用的区域&#xff09; 堆和方法区 &#xff08;栈和PC随着线程的创建而产生&#xff0c;销毁而消失&#xff09; 三、when&#xff08;什么时候进行垃圾回收&#xff09; 1、引用计数法&#…

双链表(上)

1.结构 1.带头双向循环链表 注意&#xff1a;这里的“带头”跟前面我们说的“头节点”是两个概念&#xff0c;实际前面的在单链表阶段称呼不严 谨&#xff0c;但是为了同学们更好的理解就直接称为单链表的头节点。 带头链表里的头节点&#xff0c;实际为“哨兵位”&#xff0c…

大模型在语音识别领域的最新进展与挑战

摘要&#xff1a; 本文概述了大模型在语音识别领域的最新进展与挑战&#xff0c;包括基础知识、核心组件、实现步骤、代码示例、技巧与实践、性能优化与测试、常见问题与解答、结论与展望等内容。 引言 语音识别技术的发展历程中&#xff0c;大模型的应用和重要性日益凸显。…

OMP实现MATLAB压缩感知实例

OMP(Orthogonal Matching Pursuit,正交匹配追踪)算法是一种用于稀疏信号重构的迭代算法。它的基本思想是在每一步选择与当前残差最相关的原子(或基),并使用它来更新估计值,直到满足停止准则为止。 基本流程 下面是OMP算法的基本流程: 初始化: 将残差初始化为原始信号…

【ARM】MDK在programming algorithm界面添加FLM

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 解决在programming algorithm界面中无法添加想要的Flash编程算法的问题 2、 问题场景 在对于Debug进行Flash Download进行配置的时候&#xff0c;在programming algorithm界面中有对应的Flash编程算法。可以通过…

数据结构 之 树

目录 1. 定义&#xff1a; 2. 概念&#xff08;重要&#xff09;&#xff1a; 3. 树的表示形式&#xff1a; 4. 树的应用&#xff1a; 1. 定义&#xff1a; 树是一种非线性的数据结构&#xff0c;&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有…