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.hqwc.cn/news/140324.html

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

相关文章

成都瀚网科技:如何有效运营抖店来客呢?

随着电子商务的快速发展和移动互联网的普及&#xff0c;越来越多的企业开始将目光转向线上销售渠道。其中&#xff0c;抖音成为备受关注的平台。作为中国最大的短视频社交平台之一&#xff0c;抖音每天吸引数亿用户&#xff0c;这也为企业提供了巨大的商机。那么&#xff0c;如…

FPGA软件【紫光】

软件&#xff1a;编程软件。 注册账号需要用到企业邮箱 可以使用【企业微信】的邮箱 注册需要2~3天&#xff0c;会收到激活邮件 授权&#xff1a; 找到笔记本网卡的MAC&#xff0c; 软件授权选择ADS 提交申请后&#xff0c;需要2~3天等待邮件通知。 使用授权&#xff1a; 文…

如何实现前端社交媒体分享功能?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

【计算机网络】网络原理

目录 1.网络的发展 2.协议 3.OSI七层网络模型 4.TCP/IP五层网络模型及作用 5.经典面试题 6.封装和分用 发送方(封装) 接收方(分用) 1.网络的发展 路由器&#xff1a;路由指的是最佳路径的选择。一般家用的是5个网口&#xff0c;1个WAN口4个LAN口(口&#xff1a;端口)。可…

github: kex_exchange_identification: Connection closed by remote host

问题描述 (base) ➜ test git:(dev) git pull kex_exchange_identification: Connection closed by remote host Connection closed by 192.30.255.113 port 22 致命错误&#xff1a;无法读取远程仓库。解决方案 参照下边文档 https://docs.github.com/en/authentication/tr…

jvm 各个版本支持的参数

知道一些 jvm 调优参数&#xff0c;但是没有找到官网对应的文档&#xff0c;在网上的一些文章偶然发现&#xff0c;记录一下。 https://docs.oracle.com/en/java/javase/ 包含各个版本 jdk 8 分为 windows 和 unix 系统 https://docs.oracle.com/javase/8/docs/technotes/too…

【HTML+CSS】零碎知识点

公告滚动条 <!DOCTYPE html> <html><head><title>动态粘性导航栏</title><style>.container {background: #00aeec;overflow: hidden;padding: 20px 0;}.title {float: left;font-size: 20px;font-weight: normal;margin: 0;margin-left:…

GeoServer改造Springboot启动五(解决接口返回xml而不是json)

请求接口返回的是xml&#xff0c;而不是我们常用的json&#xff0c;问题呈现如下图 40 图 40请求接口返回XML 在RequestMapping注解上增加produces {MediaType.APPLICATION_JSON_UTF8_VALUE} 图 41增加produces

Linux权限基础知识

前言&#xff1a;作者也是初学Linux&#xff0c;可能总结的还不是很到位 Linux修炼功法&#xff1a;初阶功法 ♈️今日夜电波&#xff1a;修炼爱情 —林俊杰 0:30━━━━━━️&#x1f49f;──────── 4:47 …

大规模语言LLaVA:多模态GPT-4智能助手,融合语言与视觉,满足用户复杂需求

大规模语言LLaVA&#xff1a;多模态GPT-4智能助手&#xff0c;融合语言与视觉&#xff0c;满足用户复杂需求 一个面向多模式GPT-4级别能力构建的助手。它结合了自然语言处理和计算机视觉&#xff0c;为用户提供了强大的多模式交互和理解。LLaVA旨在更深入地理解和处理语言和视…

在 Windows Server RDS 服务器 上重置 120 天宽限期

如果您出于测试目的安装了 RDS Server 2016/2019/2022&#xff0c;并且 RDS 许可宽限期已过期&#xff0c;请继续阅读下面的内容以了解如何重置 120 天宽限期。您可能知道&#xff0c;在安装 RDS Server 2016 时&#xff0c;您有 120 天的时间来安装 RD 客户端访问许可证 &…

5分钟内在Linux上安装.NET Core应用程序

作为开源的忠实粉丝&#xff0c;我喜欢 .NET Core 的跨平台特性。它开启了无限的可能性&#xff0c;从业余爱好项目、实验和概念验证&#xff0c;到在具有高安全性和可扩展性的经济高效基础设施上运行的大规模高负载生产应用程序。我通常从任何云平台提供商那里获得最简单、最便…