Gateway怎么实现限流的
在API网关(如Spring Cloud Gateway、Kong、Nginx等)中实现限流是为了控制服务请求的频率,从而避免系统过载,确保稳定性和可用性。限流可以通过多种策略实现,常见的方法包括基于请求次数、时间窗口、IP地址等方式进行限制。下面是 Spring Cloud Gateway 实现限流的一些常见方式:
1. 基于请求次数的限流
这种方法通过限制单位时间内的请求次数来防止过多的请求访问服务。例如,限制每秒钟只能处理最多100个请求。
- Spring Cloud Gateway实现: Spring Cloud Gateway提供了内置的限流功能,可以通过
RequestRateLimiter
过滤器来实现基于请求次数的限流。
示例配置:
XML
spring:
cloud:
gateway:
routes:
- id: rate_limiter_route
uri: http://example.com
predicates:
- Path=/api/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒钟请求的数量
redis-rate-limiter.burstCapacity: 20 # 突发容量,即短时间内允许的最大请求数
replenishRate
: 每秒钟允许的请求数。burstCapacity
: 突发容量,表示短时间内可以处理的最大请求数,超过该数的请求会被丢弃或拒绝。
2. 基于令牌桶算法(Token Bucket)限流
令牌桶算法是一种平滑请求流量的算法,它通过固定的速率生成令牌并将其存放在桶中,请求到达时需要获取令牌才能处理,如果令牌桶为空,则请求被拒绝。
- Spring Cloud Gateway实现: 使用
RequestRateLimiter
实现基于令牌桶的限流。
示例配置:
XML
spring:
cloud:
gateway:
routes:
- id: token_bucket_route
uri: http://example.com
predicates:
- Path=/api/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒生成的令牌数
redis-rate-limiter.burstCapacity: 20 # 令牌桶的容量
这里的配置和上面的请求次数限制类似,但背后实现的是令牌桶算法。
3. 基于IP限流
可以根据客户端IP地址来限制每个IP的请求频率,避免某个客户端过度访问服务。通常使用Redis来实现基于IP的限流。
示例配置(使用Redis):
XML
spring:
cloud:
gateway:
routes:
- id: ip_rate_limiter_route
uri: http://example.com
predicates:
- Path=/api/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
redis-rate-limiter.keyResolver: "#{@ipKeyResolver}"
在这个例子中,keyResolver
指定了如何基于客户端 IP 地址来限流,可以自定义一个IP解析器ipKeyResolver
,这样每个IP的请求次数会被限制。
4. 基于时间窗口的限流
时间窗口限流是按照固定的时间窗口(如每分钟、每小时等)来限制请求的数量。通过时间窗口来控制单位时间内的最大请求数。
示例配置:
XML
spring:
cloud:
gateway:
routes:
- id: time_window_route
uri: http://example.com
predicates:
- Path=/api/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 5 # 每秒钟请求的数量
redis-rate-limiter.burstCapacity: 10 # 突发容量
redis-rate-limiter.requestedTokens: 1 # 每次请求消耗的令牌数
这种方式将请求限制在一定的时间窗口内,如果在窗口内的请求数量超过限制,将拒绝额外的请求。
5. 自定义限流策略
Spring Cloud Gateway 还允许通过自定义KeyResolver
、RateLimiter
等来实现更灵活的限流策略。例如,基于用户的ID、API路径等来做不同的限流策略。
自定义 KeyResolver
示例:
XML
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostString());
}
这个例子通过客户端IP地址进行限流。
6. 其他网关限流工具
除了Spring Cloud Gateway,其他网关如 Kong 、Nginx 等也提供了丰富的限流功能:
- Kong 使用插件机制实现限流,支持基于请求次数、IP、API Key等的限流。
- Nginx 可以通过
limit_req
和limit_conn
模块来进行限流控制。
总结
在 Spring Cloud Gateway 中,常见的限流方式包括:
- 基于请求次数的限流(Rate Limiting)
- 基于令牌桶算法的限流(Token Bucket)
- 基于IP的限流
- 基于时间窗口的限流
可以通过RequestRateLimiter
过滤器实现这些策略,结合 Redis 等存储技术来共享和持久化限流状态。在选择限流策略时,需要根据业务需求、流量模式、以及系统的负载能力来决定。
分析Gateway 限流代码
java
@Component
public class RateLimitConfig {
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("token"));
}
@Bean
KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
@Bean
KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
@Primary
@Bean
KeyResolver appIpKeyResolver() {
return exchange -> {
Route route = (Route) exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
return Mono.just(exchange.getRequest().getRemoteAddress().getHostName().concat(route.getUri().getHost()));
};
}
}
这段代码实现了Spring Cloud Gateway的限流配置,具体的实现通过不同的KeyResolver
来为请求提供唯一的标识,用于区分不同的请求来源或策略。KeyResolver
是限流的核心组成部分,它决定了限流的"键"(即每个请求的唯一标识),从而确保不同的请求按照不同的规则被限流。
以下是对代码的详细分析:
1. KeyResolver
的作用
KeyResolver
是 Spring Cloud Gateway 中限流机制的一部分,用来生成与请求相关的唯一"键"。在限流时,基于这些"键"来计算每个限流单元(例如每个用户、每个IP、每个API等)的请求次数。限流规则会依据这些键来进行流量控制。
2. userKeyResolver
java
@Bean KeyResolver userKeyResolver() { return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("token")); }
- 功能 :该
KeyResolver
基于请求的查询参数token
生成唯一的限流键。 - 应用场景 :当客户端使用
token
参数(可能是API Key或用户身份标识符)时,可以基于该token
对每个用户进行限流。每个用户(或每个token
)的请求次数将被单独计数。 - 具体实现 :从请求的查询参数中获取
token
参数的第一个值。如果没有这个参数,返回null
或空值会导致限流功能不生效。
3. ipKeyResolver
java
@Bean KeyResolver ipKeyResolver() { return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); }
- 功能 :该
KeyResolver
基于客户端的 IP 地址生成限流键。 - 应用场景 :当需要限制每个IP地址的请求频率时,使用此
KeyResolver
。例如,可以限制每个IP在一定时间窗口内的请求次数,从而避免单个IP的过多请求对系统造成压力。 - 具体实现 :通过
exchange.getRequest().getRemoteAddress()
获取客户端的远程IP地址,然后使用getHostName()
获取IP的主机名。理论上,getHostName()
会尝试解析IP地址为主机名,但在某些情况下,可能直接返回IP地址。
4. apiKeyResolver
java
@Bean KeyResolver apiKeyResolver() { return exchange -> Mono.just(exchange.getRequest().getPath().value()); }
- 功能 :该
KeyResolver
基于请求的API路径生成唯一的限流键。 - 应用场景 :当需要限制某个API路径的请求频率时,使用此
KeyResolver
。例如,限制/api/v1/login
或/api/v1/register
路径的访问频率,可以防止某个特定API被过度访问。 - 具体实现 :通过
exchange.getRequest().getPath().value()
获取请求的路径。这个路径通常是 URI 的路径部分,如/api/v1/resource
。
5. appIpKeyResolver
java
@Primary @Bean KeyResolver appIpKeyResolver() { return exchange -> { Route route = (Route) exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR); return Mono.just(exchange.getRequest().getRemoteAddress().getHostName().concat(route.getUri().getHost())); }; }
- 功能 :该
KeyResolver
综合了客户端的 IP 地址和路由的 URI 来生成限流键。 - 应用场景 :当希望根据客户端 IP 和访问的具体服务进行限流时,使用此
KeyResolver
。例如,可以限制每个IP对特定API的访问频率。 - 具体实现 :
- 通过
exchange.getRequest().getRemoteAddress().getHostName()
获取客户端的 IP 地址。 - 通过
exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR)
获取当前请求的路由信息。 route.getUri().getHost()
获取请求目标服务的主机名(或域名),并将其与客户端IP拼接,作为限流的唯一键。- 使用
concat()
方法将客户端IP与目标服务主机名合并为一个字符串,以保证限流的独立性。
- 通过
6. @Primary
注解
java
@Primary @Bean KeyResolver appIpKeyResolver() { ... }
- 作用 :在有多个相同类型的
@Bean
时,@Primary
注解表示优先使用该Bean
。如果在其他地方有KeyResolver
的注入需求,Spring 将默认使用appIpKeyResolver()
方法返回的实例。 - 场景 :当存在多个
KeyResolver
时,如果没有明确指定使用哪个KeyResolver
,Spring 会自动选择标记为@Primary
的那个。这里标记了appIpKeyResolver
为默认的KeyResolver
。
总结
这段代码定义了多个 KeyResolver
,并为不同的限流策略提供了可选择的实现:
userKeyResolver
:基于请求的token
限流,适用于基于用户或身份的限流。ipKeyResolver
:基于客户端 IP 地址限流,适用于限制每个IP的请求次数。apiKeyResolver
:基于请求路径限流,适用于限制某个特定API的访问。appIpKeyResolver
:基于客户端 IP 地址和路由的 URI 组合限流,适用于更细粒度的限流需求,确保同一IP对不同API的请求可以独立计数。
@Primary
注解确保 appIpKeyResolver
是默认的限流键生成器,除非特别指定,否则会优先使用它。
这些 KeyResolver
会与 RequestRateLimiter
等限流功能配合使用,从而在 API 网关层实现精细化的限流控制。
怎么没有指定限流的量,每秒允许访问的量
在这段代码中,虽然定义了不同的 KeyResolver
来生成限流的标识符(如 token
、IP
或 API路径
),但确实没有指定实际的限流规则(例如每秒允许的访问量)。限流的具体控制是通过 Spring Cloud Gateway 的 RequestRateLimiter
过滤器来实现的,而这个过滤器是用来根据指定的规则对请求进行限流的。
要实现每秒允许的访问量,你需要配置 RequestRateLimiter
过滤器,并为它指定限流规则(如每秒最大访问量和最大突发流量)。
完整的限流配置示例
首先,你需要创建一个 RequestRateLimiter
过滤器,并在过滤器中配置限流策略。通常,你可以通过如下方式来配置:
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cloud.gateway.filter.ratelimit.RequestRateLimiter;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.config.GlobalFilterAutoConfiguration;
@Configuration
public class GatewayConfig {
@Bean
public RequestRateLimiter filter(KeyResolver keyResolver) {
// 配置限流:每秒最多允许 10 次请求,突发流量最多为 20 次
return new RequestRateLimiter()
.setRateLimiter(new MyRateLimiter(10, 20)) // 每秒 10 次请求,突发流量最多 20 次
.setKeyResolver(keyResolver); // 设置限流的 KeyResolver
}
// 自定义一个简单的 RateLimiter 实现类
public static class MyRateLimiter implements RateLimiter {
private final int replenishRate;
private final int burstCapacity;
public MyRateLimiter(int replenishRate, int burstCapacity) {
this.replenishRate = replenishRate;
this.burstCapacity = burstCapacity;
}
@Override
public boolean isAllowed(String key) {
// 此处实现你自己的限流逻辑,可以借助令牌桶算法、漏桶算法等
return true; // 只是示例,实际需要实现限流逻辑
}
// 配置每秒最大请求数(每秒 10 次)
public int getReplenishRate() {
return replenishRate;
}
// 配置突发容量
public int getBurstCapacity() {
return burstCapacity;
}
}
}
如何将限流规则与 KeyResolver
配合?
在这段代码中,RequestRateLimiter
的限流规则(如每秒允许 10 次请求,突发容量为 20)会与之前定义的 KeyResolver
配合,限制特定 token
、IP 或 API 路径的访问频率。
-
KeyResolver
选择 :可以使用你之前定义的userKeyResolver
、ipKeyResolver
或apiKeyResolver
来作为限流的依据。 -
限流逻辑 :通过
MyRateLimiter
类实现限流逻辑(如令牌桶、漏桶等算法)。每个限流的"key"(例如token
或IP
)会根据配置的规则(如每秒 10 次、突发流量 20 次)进行访问次数的限制。
示例配置:按 token
限流
假设你想基于 token
进行限流,你可以这样配置:
java
@Bean
public RequestRateLimiter filter(KeyResolver userKeyResolver) {
return new RequestRateLimiter()
.setRateLimiter(new MyRateLimiter(10, 20)) // 每秒 10 次请求,突发流量最多 20 次
.setKeyResolver(userKeyResolver); // 使用之前定义的基于 token 的 KeyResolver
}
限流的配置项
-
replenishRate
:每秒允许的请求数。例如,10
表示每秒最多可以处理 10 个请求。 -
burstCapacity
:突发容量,表示在短时间内可以允许的最大请求数。例如,20
表示最多允许瞬间访问 20 次,但通常会平滑地恢复到正常的请求速率。 -
KeyResolver
:你已经定义了多个KeyResolver
,它们决定了限流的粒度。你可以按用户(token
)、IP 地址、路径等来分别限流。
总结
这段代码本身只是定义了限流的标识符 (通过 KeyResolver
)。真正的限流控制需要通过 RequestRateLimiter
过滤器来实现,并在其中指定限流的具体限制量 (如每秒允许多少次请求)。你可以使用 replenishRate
和 burstCapacity
来配置每秒允许的访问量及突发流量。