之前分享過 一篇 《Spring Cloud Gateway 原生的接口限流該怎麼玩》, 核心是依賴Spring Cloud Gateway 默認提供的限流過濾器來實現
原生RequestRateLimiter 的不足
- 配置方式
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: lb://pigx-upms
order: 10000
predicates:
- Path=/admin/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 3
key-resolver: "#{@remoteAddrKeyResolver}" #SPEL表達式去的對應的bean
- StripPrefix=1
- RequestRateLimiterGatewayFilterFactory
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) -> {
return exchange.getResponse().setComplete();
});
});
};
}
- 在實際生產過程中,必定不能滿足我們的需求
生產中路由信息是保存數據庫持久化或者配置中心,
RequestRateLimiterGatewayFilterFactory
並不能隨着持久化數據的改變而動態改變限流參數,不能做到實時根據流量來改變流量閾值
Sentinel Spring Cloud Gateway 流控支持
Sentinel 是什麼?
隨着微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 以流量爲切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性,分佈式系統的流量防衛兵。
從 1.6.0 版本開始,Sentinel 提供了 Spring Cloud Gateway 的適配模塊,可以提供兩種資源維度的限流:
route 維度:即在 Spring 配置文件中配置的路由條目,資源名爲對應的 routeId
自定義 API 維度:用戶可以利用 Sentinel 提供的 API 來自定義一些 API 分組
pom 依賴
<!--Spring Cloud Alibaba 封裝的 sentinel 模塊-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!--使用nacos 保存限流規則-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
配置本地路由規則及其sentinel數據源
spring:
application:
name: sentinel-spring-cloud-gateway
cloud:
gateway:
enabled: true
discovery:
locator:
lower-case-service-id: true
routes:
- id: pigx_route
uri: https://api.readhub.cn
predicates:
- Path=/topic/**
sentinel:
datasource.ds1.nacos:
server-addr: 127.0.0.1:8848
data-id: gw-flow
group-id: DEFAULT_GROUP
ruleType: gw-api-group
filter:
enabled: true
配置nacos數據源中的限流策略
- 常用限流策略 常量
以客戶端IP作爲限流因子
public static final int PARAM_PARSE_STRATEGY_CLIENT_IP = 0;
以客戶端HOST作爲限流因子
public static final int PARAM_PARSE_STRATEGY_HOST = 1;
以客戶端HEADER參數作爲限流因子
public static final int PARAM_PARSE_STRATEGY_HEADER = 2;
以客戶端請求參數作爲限流因子
public static final int PARAM_PARSE_STRATEGY_URL_PARAM = 3;
以客戶端請求Cookie作爲限流因子
public static final int PARAM_PARSE_STRATEGY_COOKIE = 4;
- 核心源碼解析 SentinelGatewayFilter
sentinel通過擴展Gateway的過濾器,通過選擇的不同
GatewayParamParser
過處理請求限流因子和數據源中的配置進行比較
源碼如下:
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
Mono<Void> asyncResult = chain.filter(exchange);
if (route != null) {
String routeId = route.getId();
Object[] params = paramParser.parseParameterFor(routeId, exchange,
r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID);
String origin = Optional.ofNullable(GatewayCallbackManager.getRequestOriginParser())
.map(f -> f.apply(exchange))
.orElse("");
asyncResult = asyncResult.transform(
new SentinelReactorTransformer<>(new EntryConfig(routeId, EntryType.IN,
1, params, new ContextConfig(contextName(routeId), origin)))
);
}
Set<String> matchingApis = pickMatchingApiDefinitions(exchange);
for (String apiName : matchingApis) {
Object[] params = paramParser.parseParameterFor(apiName, exchange,
r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME);
asyncResult = asyncResult.transform(
new SentinelReactorTransformer<>(new EntryConfig(apiName, EntryType.IN, 1, params))
);
}
return asyncResult;
}
效果演示
- 以上nacos 配置爲 每秒只能通過5個請求,我們使用
jmeter 4.0
來併發10個線程測試一下
- 通過上圖可以結果證明sentinel限流確實有效
動態修改限流參數
-
sentinel-datasource-nacos
作爲sentinel的數據源,可以從如上 nacos 管理臺實時刷新限流參數及其閾值 - 目前sentinel dashboard 1.6.2 暫未實現gateway 流控圖形化控制 ,
1.7.0
會增加此功能
總結
歡迎關注我們獲得更多的好玩JavaEE 實踐