之前分享過 一篇 《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 實踐