Gateway自定义限流规则

Gateway实现自定义限流规则

有些同学在使用gateway配置限流规则,可能需要自定义一些规则对哪些请求不限流。常规的做法是参考

org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory

继承或者实现其接口,来重写其

apply()

或者

isAllowed()

如图是Spring Cloud Gateway 3.1.1的默认实现
在这里插入图片描述
如果需要自定义限流的逻辑仅限于决定谁被限流,谁不被限流的话,可以看图中下面这段代码

@Override
public GatewayFilter apply(Config config) {
	KeyResolver resolver = getOrDefault(config.keyResolver, defaultKeyResolver);
	RateLimiter<Object> limiter = getOrDefault(config.rateLimiter, defaultRateLimiter);
	boolean denyEmpty = getOrDefault(config.denyEmptyKey, this.denyEmptyKey);
	HttpStatusHolder emptyKeyStatus = HttpStatusHolder
			.parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode));

	return (exchange, chain) -> resolver.resolve(exchange).defaultIfEmpty(EMPTY_KEY).flatMap(key -> {
		if (EMPTY_KEY.equals(key)) { //如果等于EMPTY_KEY
			if (denyEmpty) {		//且denyEmpty为true,拦截请求
				setResponseStatus(exchange, emptyKeyStatus);
				return exchange.getResponse().setComplete();
			}
			return chain.filter(exchange); //否则放行,不限流
		}
		String routeId = config.getRouteId();
		if (routeId == null) {
			Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
			routeId = route.getId();
		}
		return limiter.isAllowed(routeId, key).flatMap(response -> {

			for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
				exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
			}

			if (response.isAllowed()) {
				return chain.filter(exchange);
			}

			setResponseStatus(exchange, config.getStatusCode());
			return exchange.getResponse().setComplete();
		});
	});
}

这里先判断了key值是否与EMPTY_KEY 相等
EMPTY_KEY 值如下:

private static final String EMPTY_KEY = "____EMPTY_KEY__";

又判断了变量denyEmpty的boolean值,如果为false则直接放行,不限流

所以我们可以通过调整denyEmpty的值为false,再将无需限流的请求key设置为字符串"__EMPTY_KEY",即可实现自定义限流 ~

首先是设置key,我们调整KeyResolver的逻辑,如下:

@Configuration
public class Config {

    private static final String EMPTY_KEY = "____EMPTY_KEY__";

    @Bean
    KeyResolver hostAddressKeyResolver() {
        return exchange -> {
            ServerHttpRequest serverHttpRequest = exchange.getRequest();
            if(serverHttpRequest.getHeaders().containsKey("此处举例,该逻辑替换成你需要的逻辑")){
                return Mono.just(EMPTY_KEY);
            }
            String key = Objects.requireNonNull(serverHttpRequest.getRemoteAddress()).getAddress().getHostAddress();//假设你也是根据host限流
            return Mono.just(key);
        };
    }
}

其次是denyEmpty的值,denyEmpty来源于配置文件,默认为true,源码如下

private boolean denyEmptyKey = true;//默认值

@Override
	public GatewayFilter apply(Config config) {
		...
		boolean denyEmpty = getOrDefault(config.denyEmptyKey, this.denyEmptyKey);
		...
	}

	private <T> T getOrDefault(T configValue, T defaultValue) {
		return (configValue != null) ? configValue : defaultValue;
	}

所以我们可以调整如下配置

spring:
  cloud:
    gateway:
      routes:
        - id: ...
          uri: ...
          predicates:
            ...
          filters:
            - name: RequestRateLimiter
              args:
                key-resolver: "#{@自定义}"
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 1
                deny-empty-key: false

这样,对于我们 Mono.just(EMPTY_KEY)的请求,就可以实现不限流了。

以上代码仅基于Spring Cloud Gateway 3.1.1版本,供大家参考

### 关于 Spring Cloud Gateway 限流配置失败的原因及解决方案 #### 一、原因分析 Spring Cloud Gateway 的 `RequestRateLimiter` 功能虽然提供了基本的限流能力,但在实际应用中可能存在一些局限性和配置不当的情况。以下是可能导致限流失效的主要原因: 1. **未正确配置 Redis 或其他存储服务** 默认情况下,`RequestRateLimiter` 使用 Redis 来实现分布式限流。如果 Redis 配置错误或者连接不可用,则可能会导致限流逻辑无法正常执行[^1]。 2. **限流策略设计不合理** 如果限流规则设置得不够精确(例如时间窗口过大或过小),可能会影响系统的稳定性。比如,在某些场景下,即使设置了 QPS 限制为 3,但由于时间窗口计算误差,可能会在一秒钟内放行超过预期数量的请求[^4]。 3. **过滤器顺序问题** 在 Spring Cloud Gateway 中,多个过滤器按照定义的顺序依次执行。如果 `RequestRateLimiter` 过滤器被放置在不恰当的位置,可能会导致其作用范围受限甚至完全不起效[^2]。 4. **缺乏自定义扩展** 如前所述,默认的 `RequestRateLimiter` 存在诸多限制,例如不支持单机限流、并发量控制不足等问题。如果没有针对具体业务需求进行定制化开发,则难以满足复杂的生产环境要求。 --- #### 二、解决方案 为了有效解决上述问题并优化 Spring Cloud Gateway限流功能,可以从以下几个方面入手: ##### 1. 确保依赖组件可用性 确认项目已引入必要的依赖库,并验证 Redis 是否能够稳定运行以及网络连通状态良好。可以通过以下方式测试 Redis 连接情况: ```java @SpringBootTest public class RedisTest { @Autowired private ReactiveRedisTemplate<String, String> redisTemplate; @Test public void testRedisConnection() { Mono<Boolean> result = this.redisTemplate.hasKey("test_key"); Assertions.assertTrue(result.block()); } } ``` ##### 2. 调整限流规则参数 合理规划限流阈值与时窗大小,避免因设定过于宽松而引发资源耗尽的风险;同时也需注意不要让规则变得过于严格以至于影响用户体验。例如,采用滑动日志算法替代固定窗口计数法可获得更平滑的效果。 ##### 3. 修改过滤器链顺序 通过调整 YAML 文件中的 filter 定义位置或将特定路径映射至单独路由实例的方法重新安排各阶段操作次序,从而保障 `RequestRateLimiter` 得到优先级较高的处理权。 ```yaml spring: cloud: gateway: routes: - id: rate_limit_route uri: http://example.org predicates: - Path=/limited/** filters: - name: RequestRateLimiter args: key-resolver: "#{@userKeyResolver}" redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20 ``` ##### 4. 开发专属限流方案 当内置机制无法充分匹配复杂应用场景下的特殊诉求时,考虑基于 Sentinel 等第三方框架构建更加灵活高效的流量管控体系成为一种可行的选择。Sentinel 提供了更为丰富的指标维度监控选项及其配套插件生态,有助于达成精细化管理目标。 --- ### 总结 通过对 Spring Cloud Gateway 的默认限流特性的深入剖析可知,尽管它具备一定的基础服务能力但仍存在较多改进空间。只有综合考量基础设施健康状况、参数调优技巧以及潜在技术选型方向等多个层面因素之后才能真正意义上克服现有难题并提升整体架构质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值