Spring Cloud Gateway 閘道器限流
一、背景
在我們平時開發過程中,一般一個請求都是需要經過多個微服務的,**比如:**請求從A服務流過B服務,如果A服務請求過快,導致B服務響應慢,那麼必然會導致系統出現問題。因為,我們就需要有限流操作。
二、實現功能
-
提供自定義的限流key生成,需要實現
KeyResolver
介面。 -
提供預設的限流演算法,實現實現
RateLimiter
介面。 -
當限流的key為空時,直接不限流,放行,由引數
spring.cloud.gateway.routes[x].filters[x].args[x].deny-empty-key
來控制 -
限流時返回客戶端的相應碼有
spring.cloud.gateway.routes[x].filters[x].args[x].status-code
來控制,需要寫這個org.springframework.http.HttpStatus
類的列舉值。 -
RequestRateLimiter
只能使用name || args
這種方式來配置,不能使用簡寫的方式來配置。 -
RequestRateLimiter
過濾器的redis-rate-limiter
引數是在RedisRateLimiter
的CONFIGURATION_PROPERTY_NAME
屬性配置的。構造方法中用到了。
三、閘道器層限流
限流的key 生成規則,預設是 PrincipalNameKeyResolver
來實現 限流演算法,預設是 RedisRateLimiter
來實現,是令牌桶演算法。
1、使用預設的redis來限流
在Spring Cloud Gateway
中預設提供了 RequestRateLimiter
過濾器來實現限流操作。
1、引入jar包
<dependencies>
<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-gateway</artifactid>
</dependency>
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-loadbalancer</artifactid>
</dependency>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-data-redis-reactive</artifactid>
</dependency>
<dependency>
<groupid>org.projectlombok</groupid>
<artifactid>lombok</artifactid>
</dependency>
</dependencies>
2、編寫配置檔案
spring:
application:
name: gateway-9205
cloud:
nacos:
discovery:
server-addr: localhost:8847
gateway:
routes:
- id: user-provider-9206
uri: lb://user-provider-9206
predicates:
- Path=/user/**
filters:
- RewritePath=/user(?<segment>/?.*), $\{segment}
- name: RequestRateLimiter
args:
# 如果返回的key是空的話,則不進行限流
deny-empty-key: false
# 每秒產生多少個令牌
redis-rate-limiter.replenishRate: 1
# 1秒內最大的令牌,即在1s內可以允許的突發流程,設定為0,表示阻止所有的請求
redis-rate-limiter.burstCapacity: 1
# 每次請求申請幾個令牌
redis-rate-limiter.requestedTokens: 1
redis:
host: 192.168.7.1
database: 12
port: 6379
password: 123456
server:
port: 9205
debug: true
3、閘道器正常響應
4、閘道器限流響應
2、自定義限流演算法和限流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 {
@Override
public 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-9206
filters:
- name: RequestRateLimiter
args:
# 返回限流的key
key-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);
}
@Override
public 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
@ToString
public static class Config {
/**
* 每次請求多少個 token
*/
private Integer requestedTokens;
}
}
配置檔案中的寫法(部分)
spring:
cloud:
gateway:
routes:
- id: user-provider-9206
filters:
- name: RequestRateLimiter
args:
# 自定義限流規則
rate-limiter: "#{@defaultGatewayRateLimiter}"
注意⚠️: 這個類需要加上 @Primary
註解。
3、配置檔案中的寫法
spring:
application:
name: gateway-9205
cloud:
nacos:
discovery:
server-addr: localhost:8847
gateway:
routes:
- id: user-provider-9206
uri: lb://user-provider-9206
predicates:
- Path=/user/**
filters:
- RewritePath=/user(?<segment>/?.*), $\{segment}
- name: RequestRateLimiter
args:
# 自定義限流規則
rate-limiter: "#{@defaultGatewayRateLimiter}"
# 返回限流的key
key-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://gitee.com/huan1993/spring-cloud-alibaba-parent/tree/master/gateway-redis-limiter
五、參考文件
1、http://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-redis-ratelimiter
- Spring Data Redis Stream的使用
- Redis Stream型別的使用
- redis中lua指令碼的簡單使用
- seata整合多資料來源
- virtual box搭建虛擬機器nat和host only網路配置實用
- Spring Cloud Gateway 閘道器限流
- Canal Server傳送binlog訊息到Kafka訊息佇列中
- mysql的一些配置操作
- Spring Security 多過濾鏈的使用
- Spring動態新增定時任務
- 通過Nacos動態重新整理Spring Cloud Gateway的路由
- Spring父子上下文的使用案例
- CanalAdmin搭建Canal Server叢集
- Canal的簡單使用
- Sharding-JDBC自定義複合分片演算法
- 使用Mybatis的TypeHandler加解密資料
- filebeat收集日誌到elsticsearch中並使用ingest node的pipeline處理
- logstash收集的日誌輸出到elasticsearch中
- 使用logstash的grok外掛解析springboot日誌
- logstash的mutate過濾器的使用