Spring Cloud Gateway 限流

news/2025/1/3 3:59:15/

在高并发的应用中,限流是一个绕不开的话题。限流可以保障我们的 API 服务对所有用户的可用性,也可以防止网络攻击。

一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池、线程池)、限制瞬时并发数(如 nginx 的 limit_conn 模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(如 Guava 的 RateLimiter、nginx 的 limit_req 模块,限制每秒的平均速率);其他还有如限制远程接口调用速率、限制 MQ 的消费速率。另外还可以根据网络连接数、网络流量、CPU 或内存负载等来限流。

本文详细探讨在 Spring Cloud Gateway 中如何实现限流。

# 限流算法

做限流 (Rate Limiting/Throttling) 的时候,除了简单的控制并发,如果要准确的控制 TPS,简单的做法是维护一个单位时间内的 Counter,如判断单位时间已经过去,则将 Counter 重置零。此做法被认为没有很好的处理单位时间的边界,比如在前一秒的最后一毫秒里和下一秒的第一毫秒都触发了最大的请求数,也就是在两毫秒内发生了两倍的 TPS。

常用的更平滑的限流算法有两种:漏桶算法和令牌桶算法。很多传统的服务提供商如华为中兴都有类似的专利,参考采用令牌漏桶进行报文限流的方法。

# 漏桶算法

漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率。

可见这里有两个变量,一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate)。因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使流突发(burst)到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。

# 令牌桶算法

令牌桶算法(Token Bucket)和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解。随着时间流逝,系统会按恒定 1/QPS 时间间隔(如果 QPS=100,则间隔是 10ms)往桶里加入 Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了。新请求来临时,会各自拿走一个 Token,如果没有 Token 可拿了就阻塞或者拒绝服务。

令牌桶的另外一个好处是可以方便的改变速度。一旦需要提高速率,则按需提高放入桶中的令牌的速率。一般会定时(比如 100 毫秒)往桶中增加一定数量的令牌,有些变种算法则实时的计算应该增加的令牌的数量。 

Leakly Bucket vs Token Bucket

# 限流实现(Redis)

1.引入jar包

        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

 2.编写配置文件

spring:application:name: gateway-9205cloud:nacos:discovery:server-addr: localhost:8847gateway:routes:- id: user-provider-9206uri: lb://user-provider-9206predicates:- Path=/user/**filters:- RewritePath=/user(?<segment>/?.*), $\{segment}- name: RequestRateLimiterargs:# 如果返回的key是空的话,则不进行限流deny-empty-key: false# 每秒产生多少个令牌redis-rate-limiter.replenishRate: 1# 1秒内最大的令牌,即在1s内可以允许的突发流程,设置为0,表示阻止所有的请求redis-rate-limiter.burstCapacity: 1# 每次请求申请几个令牌redis-rate-limiter.requestedTokens: 1redis:host: 192.168.7.1database: 12port: 6379password: 123456server:port: 9205
debug: true

3.网关正常响应

 

4.网关限流响应

 

自定义限流算法和限流key 

1.自定义限流key

编写一个类实现 KeyResolver 接口即可。
 

package com.huan.study.gateway;import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.Optional;/*** 限流的key获取** @author huan.fu 2021/9/7 - 上午10:25*/
@Slf4j
@Component
public class DefaultGatewayKeyResolver implements KeyResolver {@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {// 获取当前路由Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);ServerHttpRequest request = exchange.getRequest();String uri = request.getURI().getPath();log.info("当前返回的uri:[{}]", uri);return Mono.just(Optional.ofNullable(route).map(Route::getId).orElse("") + "/" + uri);}
}

配置文件中的写法(部分)

spring:cloud:gateway:routes:- id: user-provider-9206filters:- name: RequestRateLimiterargs:# 返回限流的keykey-resolver: "#{@defaultGatewayKeyResolver}"

2.自定义限流算法

编写一个类实现 RateLimiter ,此处使用内存限流

package com.huan.study.gateway;import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.AbstractRateLimiter;
import org.springframework.cloud.gateway.support.ConfigurationService;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;/*** @author huan.fu 2021/9/7 - 上午10:36*/
@Component
@Slf4j
@Primary
public class DefaultGatewayRateLimiter extends AbstractRateLimiter<DefaultGatewayRateLimiter.Config> {/*** 和配置文件中的配置属性相对应*/private static final String CONFIGURATION_PROPERTY_NAME = "default-gateway-rate-limiter";private RateLimiter rateLimiter = RateLimiter.create(1);protected DefaultGatewayRateLimiter(ConfigurationService configurationService) {super(DefaultGatewayRateLimiter.Config.class, CONFIGURATION_PROPERTY_NAME, configurationService);}@Overridepublic Mono<Response> isAllowed(String routeId, String id) {log.info("网关默认的限流 routeId:[{}],id:[{}]", routeId, id);Config config = getConfig().get(routeId);return Mono.fromSupplier(() -> {boolean acquire = rateLimiter.tryAcquire(config.requestedTokens);if (acquire) {return new Response(true, Maps.newHashMap());} else {return new Response(false, Maps.newHashMap());}});}@Getter@Setter@ToStringpublic static class Config {/*** 每次请求多少个 token*/private Integer requestedTokens;}
}

配置文件中的写法(部分)

spring:cloud:gateway:routes:- id: user-provider-9206filters:- name: RequestRateLimiterargs:# 自定义限流规则rate-limiter: "#{@defaultGatewayRateLimiter}"

注意⚠️: 这个类需要加上 @Primary 注解。

3.配置文件中的写法

spring:application:name: gateway-9205cloud:nacos:discovery:server-addr: localhost:8847gateway:routes:- id: user-provider-9206uri: lb://user-provider-9206predicates:- Path=/user/**filters:- RewritePath=/user(?<segment>/?.*), $\{segment}- name: RequestRateLimiterargs:# 自定义限流规则rate-limiter: "#{@defaultGatewayRateLimiter}"# 返回限流的keykey-resolver: "#{@defaultGatewayKeyResolver}"# 如果返回的key是空的话,则不进行限流deny-empty-key: false# 限流后向客户端返回的响应码429,请求太多status-code: TOO_MANY_REQUESTS# 每次请求申请几个令牌  default-gateway-rate-limiter 的值是在 defaultGatewayRateLimiter 中定义的。default-gateway-rate-limiter.requestedTokens: 1
server:port: 9205
debug: true


http://www.ppmy.cn/news/61133.html

相关文章

『python爬虫』10. 数据解析之xpath解析(保姆级图文)

目录 安装库xpath入门怎么快速得到xpath路径xpath节点的关系xpath方法小型实战总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 安装库 pip install lxmlxpath入门 怎么快速得到xpath路径 &#xff08;相对路…

安装gitea

1、安装包&#xff08;gitea-1.13.1-linux-amd64&#xff09;上传到服务器&#xff0c;并添加执行权限 链接&#xff1a;https://pan.baidu.com/s/1SAxko0RhVmmD21Ev_m5JFg 提取码&#xff1a;ft07 chmod x gitea-1.13.1-linux-amd64 2、执行 ./gitea-1.13.1-linux-amd64 web…

Django如何使用templates显示网页和静态资源

Django templates and static 什么是templates? 在我们访问Django的url时&#xff0c;有时我们想返回html文件&#xff0c;但是这些文件不能随便放置&#xff0c; 我们就需要放到templates文件夹中&#xff0c;tamplates文件夹可以创建到每一个app中&#xff0c; 也可以创建…

AXI协议相关概念(一)

猝发&#xff08;Burst&#xff09; 拍&#xff08;beat&#xff09;&#xff1a;在时钟采样沿valid和ready信号同时拉高持续一个时钟周期&#xff0c;即为一拍&#xff0c;持续两个时钟周期&#xff0c;即为两拍&#xff0c;依次类推。可以概括为在时钟采样沿握手信号同时拉高…

Packet Tracer – 研究 VLAN 实施

Packet Tracer – 研究 VLAN 实施 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 S1 VLAN 99 172.17.99.31 255.255.255.0 不适用 S2 VLAN 99 172.17.99.32 255.255.255.0 不适用 S3 VLAN 99 172.17.99.33 255.255.255.0 不适用 PC1 NIC 172.17.10.2…

java servlet 农机租赁网站系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 java servlet 农机租赁网站系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助 系统采用 serlvetdaobean 模式开发 &#xff0c;系统具有完整的源代码和数 据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,M…

Codeforces Round 871 (Div. 4) 题解

题目 ALove StoryBBlank SpaceCMr. Perfectly FineDGold RushEThe LakesFForever WinterGHits DifferentHDont Blame Me A. Love Story Timur loves codeforces. Thats why he has a string &#x1d460; having length 1010 made containing only lowercase Latin letters…

有研究员公开了一个解析并提取 Dell PFS BIOS 固件的工具(上)

导语&#xff1a;研究员公开了一个解析并提取 Dell PFS BIOS 固件的工具。 Dell PFS BIOS提取器 介绍 解析 Dell PFS BIOS 映像并提取其 SPI/BIOS/UEFI 固件组件。它支持所有Dell PFS 修订版和格式&#xff0c;包括最初在 ThinOS 包中LZMA压缩、ZLIB压缩或拆分成块的格式。输出…