Spring Cloud Gateway 使用 Redis 限流使用教程

从本文开始,笔者将总结 spring cloud 相关内容的教程

版本选择

为了适应 java8,笔者选择了下面的版本,后续会出 java17的以SpringBoot3.0.X为主的教程

SpringBoot 版本 2.6.5

SpringCloud 版本 2021.0.1

SpringCloudAlibaba 版本 2021.0.1.0

SpringCloudAlibaba github 版本说明截图

SpringCloud 官网:https://spring.io/projects/spring-cloud

Spring Cloud Alibaba 官网:https://sca.aliyun.com/zh-cn/

目录

1、环境准备

2、项目创建

3、测试限流

4、改进限流返回

5、项目代码


1、环境准备

本文讲解Spring Cloud Gateway 使用 Redis 限流,注册中心使用 Nacos

Macos 官网:https://nacos.io/zh-cn/index.html

Nacos 安装这里不做过多介绍,不了解的朋友可以参考

Nacos 单机安装:https://blog.csdn.net/wsjzzcbq/article/details/123916233

Nacos 集群安装:https://blog.csdn.net/wsjzzcbq/article/details/123956116

笔者使用 docker 开启 nacos 和 redis

Spring Cloud Alibaba  2021.0.1.0 版本对应的nacos版本是 1.4.2

笔者使用的 Naocs 版本是 2.0.0-bugfix,redis 版本是 7.0.6

2、项目创建

新建 maven 聚合项目 cloud-learn

最外层父工程 cloud-learn 的 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.wsjzzcbq</groupId><artifactId>cloud-learn</artifactId><version>1.0-SNAPSHOT</version><modules><module>gateway-learn</module><module>consumer-learn</module></modules><packaging>pom</packaging><repositories><repository><id>naxus-aliyun</id><name>naxus-aliyun</name><url>https://maven.aliyun.com/repository/public</url><releases><enabled>true</enabled></releases><snapshots><enabled>false</enabled></snapshots></repository></repositories><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.5</version><relativePath/></parent><properties><spring-cloud.version>2021.0.1</spring-cloud.version><spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version><alibaba-nacos-discovery.veriosn>2021.1</alibaba-nacos-discovery.veriosn><alibaba-nacos-config.version>2021.1</alibaba-nacos-config.version><spring-cloud-starter-bootstrap.version>3.1.1</spring-cloud-starter-bootstrap.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>${alibaba-nacos-discovery.veriosn}</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>${alibaba-nacos-config.version}</version></dependency><!--spring-cloud-dependencies 2020.0.0 版本不在默认加载bootstrap文件,如果需要加载bootstrap文件需要手动添加依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId><version>${spring-cloud-starter-bootstrap.version}</version></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.40</version></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies></project>

然后创建2个子工程 consumer-learn 和 gateway-learn

gateway-learn 中配置路由转发到 consumer-learn

consumer-learn 工程 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>cloud-learn</artifactId><groupId>com.wsjzzcbq</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>consumer-learn</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--feign负载均衡--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

consumer-learn 工程 启动类

package com.wsjzzcbq;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;/*** ConsumerApplication** @author wsjz* @date 2023/09/17*/
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}
}

consumer-learn 工程 配置文件

spring.application.name=consumer-learn
server.port=8081
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
spring.cloud.nacos.discovery.server-addr=192.168.31.152:8848
spring.cloud.nacos.discovery.namespace=public
logging.level.com.alibaba.cloudlearnconsumer.feign.ProducerService=DEBUG

consumer-learn 工程 controller

package com.wsjzzcbq.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** ConsumerController** @author wsjz* @date 2023/09/17*/
@RestController
public class ConsumerController {@RequestMapping("/name")public String user() {return "宝剑锋从磨砺出,梅花香自苦寒来";}
}

gateway-learn

gateway-learn 工程 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>cloud-learn</artifactId><groupId>com.wsjzzcbq</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>gateway-learn</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- gateway负载均衡需要下面依赖,不添加会报错503--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!--redis使用较高版本,5以上版本,不要使用windows版redis,低版本redis不支持lua中的命令--><!--https://blog.csdn.net/wxxiangge/article/details/95024214/--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

gateway-learn 工程 启动类

package com.wsjzzcbq;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** GatewayApplication** @author wsjz* @date 2023/09/17*/
@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}

限流需要实现 KeyResolver 接口的 resolve 方法

在 resolve 方法中返回限流的维度,如请求路径、ip地址、请求参数等

笔者这里限流维度是请求路径

package com.wsjzzcbq.limit;import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;/*** ApiKeyResolver** @author wsjz* @date 2023/09/17*/
@Component
public class ApiKeyResolver implements KeyResolver {@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {System.out.println("API限流: " + exchange.getRequest().getPath().value());return Mono.just(exchange.getRequest().getPath().value());}
}

gateway-learn 工程 配置文件

server:port: 9000
spring:application:name: gateway-learnredis:host: 192.168.31.152password: 123456timeout: 5000database: 0cloud:nacos:discovery:server-addr: http://192.168.31.152:8848gateway:routes:- id: consumer-learnuri:  lb://consumer-learnpredicates:- Path=/cloudlearn/consumer/**filters:- name: RequestRateLimiterargs:key-resolver: "#{@apiKeyResolver}"redis-rate-limiter.replenishRate: 1 #生成令牌速率:个/秒redis-rate-limiter.burstCapacity: 2 #令牌桶容量redis-rate-limiter.requestedTokens: 1 #每次消费的Token数量,默认是 1- StripPrefix=2

配置说明:

RequestRateLimiter 是 gateway 提供的限流过滤器

#{@apiKeyResolver} 是笔者实现的 ApiKeyResolver

redis-rate-limiter.replenishRate 生成令牌的速率每秒几个

redis-rate-limiter.burstCapacity 令牌桶容量

redis-rate-limiter.requestedTokens 每次消费的令牌数量

当请求到网关以 /cloudlearn/consumer/  为开头前缀时,会路由到 consumer-learn 服务上

创建完成的项目结构

3、测试限流

分别启动 consumer-learn 和 gateway-learn

登录 Nacos 控制台查看

启动成功后,可在Naocs 控制台查看注册服务信息

浏览器访问测试限流:http://localhost:9000/cloudlearn/consumer/name

运行效果

可以看到当1秒钟内请求超过2次时会被限流

4、改进限流返回

上面代码实现了限流,但限流触发后返回的是429,不利于前端处理,这里我们可以在默认的限流过滤器基础上进行改进,自定义限流时的返回

新建 NewRequestRateLimiterGatewayFilterFactory 类

继承默认的 RequestRateLimiterGatewayFilterFactory

package com.wsjzzcbq.filter;import com.alibaba.fastjson2.JSONObject;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.HttpStatusHolder;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Map;/*** NewRequestRateLimiterGatewayFilterFactory** @author wsjz* @date 2023/09/17*/
@Component
public class NewRequestRateLimiterGatewayFilterFactory extends RequestRateLimiterGatewayFilterFactory {private final RateLimiter defaultRateLimiter;private final KeyResolver defaultKeyResolver;private boolean denyEmptyKey = true;private String emptyKeyStatusCode = HttpStatus.FORBIDDEN.name();public NewRequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {super(defaultRateLimiter, defaultKeyResolver);this.defaultRateLimiter = defaultRateLimiter;this.defaultKeyResolver = defaultKeyResolver;}@Overridepublic GatewayFilter apply(Config config) {System.out.println("过滤限流");KeyResolver resolver = (KeyResolver)this.getOrDefault(config.getKeyResolver(), this.defaultKeyResolver);RateLimiter<Object> limiter = (RateLimiter)this.getOrDefault(config.getRateLimiter(), this.defaultRateLimiter);boolean denyEmpty = (Boolean)this.getOrDefault(config.getDenyEmptyKey(), this.denyEmptyKey);HttpStatusHolder emptyKeyStatus = HttpStatusHolder.parse((String)this.getOrDefault(config.getEmptyKeyStatus(), this.emptyKeyStatusCode));return (exchange, chain) -> {return resolver.resolve(exchange).defaultIfEmpty("____EMPTY_KEY__").flatMap((key) -> {if ("____EMPTY_KEY__".equals(key)) {if (denyEmpty) {ServerWebExchangeUtils.setResponseStatus(exchange, emptyKeyStatus);return exchange.getResponse().setComplete();} else {return chain.filter(exchange);}} else {String routeId = config.getRouteId();if (routeId == null) {Route route = (Route)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);routeId = route.getId();}return limiter.isAllowed(routeId, key).flatMap((response) -> {Iterator var4 = response.getHeaders().entrySet().iterator();while(var4.hasNext()) {Map.Entry<String, String> header = (Map.Entry)var4.next();exchange.getResponse().getHeaders().add((String)header.getKey(), (String)header.getValue());}if (response.isAllowed()) {return chain.filter(exchange);} else {ServerHttpResponse httpResponse = exchange.getResponse();httpResponse.getHeaders().set("Content-Type", "application/json");JSONObject json = new JSONObject();json.put("code", 0);json.put("msg", "当前请求人数较多,请稍后再访问");DataBuffer dataBuffer = httpResponse.bufferFactory().wrap(json.toJSONString().getBytes(StandardCharsets.UTF_8));return httpResponse.writeWith(Mono.just(dataBuffer));}});}});};}private <T> T getOrDefault(T configValue, T defaultValue) {return configValue != null ? configValue : defaultValue;}
}

修改配置文件

配置我们自定义的限流过滤器

server:port: 9000
spring:application:name: gateway-learnredis:host: 192.168.31.152password: 123456timeout: 5000database: 0cloud:nacos:discovery:server-addr: http://192.168.31.152:8848gateway:routes:- id: consumer-learnuri:  lb://consumer-learnpredicates:- Path=/cloudlearn/consumer/**filters:- name: NewRequestRateLimiterargs:key-resolver: "#{@apiKeyResolver}"redis-rate-limiter.replenishRate: 1 #生成令牌速率:个/秒redis-rate-limiter.burstCapacity: 2 #令牌桶容量redis-rate-limiter.requestedTokens: 1 #每次消费的Token数量- StripPrefix=2

重新启动 gateway-learn

请求测试

5、项目代码

码云地址:https://gitee.com/wsjzzcbq/csdn-blog/tree/master/cloud-learn

至此完

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

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

相关文章

Prometheus的Pushgateway快速部署及使用

prometheus-pushgateway安装 一. Pushgateway简介 Pushgateway为Prometheus整体监控方案的功能组件之一&#xff0c;并做于一个独立的工具存在。它主要用于Prometheus无法直接拿到监控指标的场景&#xff0c;如监控源位于防火墙之后&#xff0c;Prometheus无法穿透防火墙&…

C++基础——内存分区模型

1 概述 C程序在执行是&#xff0c;将内存大致分为4个区域&#xff1a; 代码区&#xff1a;用于存放二进制代码&#xff0c;由操作系统进行管理全局区&#xff1a;存放全局变量和静态变量及常量栈区&#xff1a;由编译器自动分配释放&#xff0c;存放函数的参数、局部变量等堆…

回首往昔,初学编程那会写过的两段愚蠢代码

一、关于判断两个整数是否能整除的GW BASIC创意代码 记得上大学时第一个编程语言是BASIC&#xff0c;当时Visual Basic还没出世&#xff0c;QBASIC虽然已经在1991年随MS-DOS5.0推出了&#xff0c;但我们使用的还是 GW-BASIC&#xff0c; 使用的教材是谭浩强、田淑清编著的《BA…

【广州华锐互动】VR建筑安全培训体验为建筑行业人才培养提供有力支持

随着建筑行业的快速发展&#xff0c;建筑施工安全问题日益受到广泛关注。然而&#xff0c;传统的安全培训方式往往缺乏实践性和真实性&#xff0c;难以让员工真正掌握安全操作技能。近年来&#xff0c;虚拟现实(VR)技术的广泛应用为建筑施工安全培训提供了新的机遇。 虚拟现实技…

金融用户实践|分布式存储支持数据仓库业务系统性能验证

作者&#xff1a;深耕行业的 SmartX 金融团队 闫海涛 估值是指对资产或负债的价值进行评估的过程&#xff0c;这对于投资决策具有重要意义。每个金融公司资管业务人员都期望能够实现实时的业务估值&#xff0c;快速获取最新的数据和指标&#xff0c;从而做出更明智的投资决策。…

【数组的使用续篇】

文章目录 以数组的形式打印数组打印方法&#xff1a;Arrays.toString(数组名) 数组排序大小排序方法是 Arrays.sort(数组名) 创建一个自己的打印数组的方法自己创建一个冒泡排序两数之间交换方法 逆置数组打印核心思路还是 i 和 j 交换 总结 以数组的形式打印数组 打印方法&am…

软件测试用例设计方法-因果图法

边界值法是等价类划分法的补充&#xff0c;所以&#xff0c;它们是一对搭档。 那么&#xff0c;判定表法有没有它的搭档呢&#xff1f; 答案是&#xff0c;有的。那就是本篇文章分享的用例设计方法—— 因果图法 。 定义 因果图法&#xff1a; 用来处理等价类划分和边界值…

三维地图开发三维地图服务器

三维地图开发三维地图服务器 发布时间&#xff1a;2020-03-03 版权&#xff1a; 搭建离线地图服务主要是两个步骤&#xff1a;一是&#xff1a;下载离线地图服务需要的地图数据&#xff1b;二是&#xff1a;将下载的离线地图数据发布成地图服务&#xff1b;只有做好这两步&…

出差学小白知识No5:ubuntu连接开发板|上传源码包|板端运行的环境部署

1、ubuntu连接开发板&#xff1a; 在ubuntu终端通过ssh协议来连接开发板&#xff0c;例如&#xff1a; ssh root<IP_address> 即可 这篇文章中也有关于如何连接开发板的介绍&#xff0c;可以参考SOC侧跨域实现DDS通信总结 2、源码包上传 通过scp指令&#xff0c;在ub…

python使用dataset快速使用SQLite

目录 一、官网地址 二、安装 三、 快速使用 一、官网地址 GitHub - pudo/dataset: Easy-to-use data handling for SQL data stores with support for implicit table creation, bulk loading, and transactions. 二、安装 pip install dataset 如果是mysql&#xff0c;则…

堆/二叉堆详解[C/C++]

前言 堆是计算机科学中-类特殊的数据结构的统称。实现有很多,例如:大顶堆,小顶堆&#xff0c;斐波那契堆&#xff0c;左偏堆&#xff0c;斜堆等等。从子结点个数上可以分为二汊堆&#xff0c;N叉堆等等。本文将介绍的是二叉堆。 二叉堆的概念 1、引例 我们小时候&#xff0c;基…

left join时筛选条件对查询结果的

-- 创建表 CREATE TABLE table1 (id int(11) NOT NULL AUTO_INCREMENT,card_num varchar(60) DEFAULT NULL,customer_id varchar(60) DEFAULT NULL,PRIMARY KEY (id) ) ENGINE InnoDBAUTO_INCREMENT 12DEFAULT CHARSET utf8mb4 COMMENT 测试表1;-- 创建表 CREAT…

44springboot摄影跟拍预定管理系统

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

basic_sr介绍

文章目录 pytorch基础知识和basicSR中用到的语法1.Sampler类与4种采样方式2.python dict的get方法使用3.prefetch_dataloader.py4. pytorch 并行和分布式训练4.1 选择要使用的cuda4.2 DataParallel使用方法常规使用方法保存和载入 4.3 DistributedDataParallel 5.wangdb 入门5.…

众佰诚:抖音小店的体验分什么时候更新

随着移动互联网的发展&#xff0c;越来越多的电商平台开始涌现&#xff0c;其中抖音小店作为一种新型的电商模式&#xff0c;受到了许多用户的欢迎。然而&#xff0c;对于抖音小店的体验分更新时间&#xff0c;很多用户并不是很清楚。本文将对此进行详细的解答。 首先&#xff…

SimpleCG图像操作基础

上一篇我们介绍了程序的交互功能&#xff0c;就可以编写一些简单的游戏了&#xff0c;例如贪吃蛇、扫雷、俄罗斯方块、五子棋等&#xff0c;都可以使用图形函数直接绘制&#xff0c;在后续文章中将逐一展示。不过编写画面丰富游戏离不开图像&#xff0c;所以本篇我们介绍一下基…

智能合同和TikTok:揭示加密技术的前景

在当今数字化时代&#xff0c;智能合同和加密技术都成为了技术和商业世界中的热门话题。它们代表了一个崭新的未来&#xff0c;有着潜在的巨大影响。 然而&#xff0c;你或许从未想过将这两者联系在一起&#xff0c;直到今天。本文将探讨智能合同和TikTok之间的联系&#xff0…

联想G50笔记本直接使用F键功能(F1~F12)需要在BIOS设置关闭热键功能可以这样操作!

如果开启启用热键模式按F1就会出现FnF1的效果&#xff0c;不喜欢此方式按键的用户可以进入BIOS设置界面停用热键模式即可。 停用热键模式方法如下&#xff1a; 1、重新启动笔记本电脑&#xff0c;当笔记本电脑屏幕出现Lenovo标识的时候&#xff0c;立即按FnF2进入BIOS设置界面…

表单规定输入域的选项列表(html5新元素)

datalist datalist 元素规定输入域的选项列表。 datalist属性规定 form 或 input 域应该拥有自动完成功能。当用户在自动完成域中开始输入时&#xff0c;浏览器应该在该域中显示填写的选项&#xff1a; 使用 input元素的列表属性与datalist元素绑定. 还有一定的搜索能力&…

CVE-2020-9483 apache skywalking SQL注入漏洞

漏洞概述 当使用H2 / MySQL / TiDB作为Apache SkyWalking存储时&#xff0c;通过GraphQL协议查询元数据时&#xff0c;存在SQL注入漏洞&#xff0c;该漏洞允许访问未指定的数据。 Apache SkyWalking 6.0.0到6.6.0、7.0.0 H2 / MySQL / TiDB存储实现不使用适当的方法来设置SQL参…