javascript
【云原生微服务>SCG网关篇十二】Spring Cloud Gateway集成Sentinel API实现多种限流方式
文章目錄
- 一、前言
- 二、Gateway集成Sentinel API
-
- 0、集成Sentinel的核心概念
-
- 1)GatewayFlowRule 和 ApiDefinition
- 2)GatewayFlowRule字段解釋
- 1、針對Route維度限流
-
- 驗證
- 2、針對API維度限流
-
- 驗證
- 3、自定義限流異常返回值
-
- 驗證
- 三、總結
一、前言
至此微服務網關系列文章已出:
聊了以下問題:
我們已經聊過了Spring Cloud Gateway的一種限流方式:使用內置的Filter(RequestRateLimiterGatewayFilterFactory)結合Redis使用令牌桶算法實現限流;這里我們再聊一下另外一種:Spring Cloud Gateway集成Sentinel實現限流。
PS:SpringCloud版本信息:
<properties><spring-boot.version>2.4.2</spring-boot.version><spring-cloud.version>2020.0.1</spring-cloud.version><spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version> </properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><!--整合spring cloud--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><!--整合spring cloud alibaba--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies> </dependencyManagement>二、Gateway集成Sentinel API
sentinel服務1.6.0以上的版本可支持整合到網關進行統一流控:
sentinel提供了兩種資源維度的限流:
- route維度:在配置文件中配置路由,資源名為對應的 routeId,一般是對某個微服務進行限流;這種維度屬于粗粒度的限流。
- 自定義API維度:通過Sentinel 提供的API來自定義一些API分組,針對某一類的uri進行匹配限流,可以跨多個微服務;這種維度屬于細粒度的限流。
gateway整合sentinel默認不支持 URL 粒度限流;因此通過 Spring Cloud Alibaba 接入時,將spring.cloud.sentinel.filter.enabled 配置為 false,以關閉流控控制臺上的 URL 資源視圖。
gateway整合Sentinel的maven依賴:
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-spring-cloud-gateway-adapter</artifactId><version>1.8.0</version> </dependency>0、集成Sentinel的核心概念
整體集成原理如下:
1)GatewayFlowRule 和 ApiDefinition
網關的限流規則GatewayFlowRule:
- 針對 API Gateway 的場景定制的限流規則,可以針對不同 route 或自定義的 API 分組進行限流,支持針對請求中的參數、Header、來源 IP 等進行定制化的限流;
用戶自定義的API分組ApiDefinition: - ApiDefinition可以看做是一些 URL 匹配的組合;限流的時候可以針對這個自定義的 API 分組進行限流。
2)GatewayFlowRule字段解釋
網關限流規則 GatewayFlowRule 的字段如下:
- resource:資源名稱,可以是網關中的 route 名稱或者用戶自定義的 API 分組名稱。
- resourceMode:規則是針對 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID)還是用戶在 Sentinel 中定義的 API 分組(RESOURCE_MODE_CUSTOM_API_NAME),默認是 route。
- grade:限流指標維度,同限流規則的 grade 字段。
- count:限流閾值
- intervalSec:統計時間窗口,單位是秒,默認是 1 秒。
- controlBehavior:流量整形的控制效果,同限流規則的 controlBehavior 字段,目前支持快速失敗和勻速排隊兩種模式,默認是快速失敗。
- burst:應對突發請求時額外允許的請求數目。
- maxQueueingTimeoutMs:勻速排隊模式下的最長排隊時間,單位是毫秒,僅在勻速排隊模式下生效。
- paramItem:參數限流配置。若不提供,則代表不針對參數進行限流,該網關規則將會被轉換成普通流控規則;否則會轉換成熱點規則。其中的字段:
- parseStrategy:從請求中提取參數的策略,目前支持提取來源 IP(PARAM_PARSE_STRATEGY_CLIENT_IP)、Host(PARAM_PARSE_STRATEGY_HOST)、任意 Header(PARAM_PARSE_STRATEGY_HEADER)和任意 URL 參數(PARAM_PARSE_STRATEGY_URL_PARAM)四種模式。
- fieldName:若提取策略選擇 Header 模式或 URL 參數模式,則需要指定對應的 header 名稱或 URL 參數名稱。
- pattern:參數值的匹配模式,只有匹配該模式的請求屬性值會納入統計和流控;若為空則統計該請求屬性的所有值。(1.6.2 版本開始支持)
- matchStrategy:參數值的匹配策略,目前支持精確匹配(PARAM_MATCH_STRATEGY_EXACT)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正則匹配(PARAM_MATCH_STRATEGY_REGEX)。(1.6.2 版本開始支持)
可以通過GatewayRuleManager.loadRules(rules) 手動加載網關規則、 或 通過 GatewayRuleManager.register2Property(property) 注冊規則(推薦方式);
特別注意:當使用 Spring Cloud Alibaba Sentinel 數據源模塊時,需要注意網關流控規則數據源類型是 gw-flow,若將網關流控規則數據源指定為 flow 則不生效。
application.yml配置文件:
spring:application:name: nacos-gatewaycloud:nacos:discovery:server-addr: 106.15.139.143:8848gateway:discovery:locator:# 開啟從注冊中心動態創建路由的功能enabled: true# 是否使用service-id的小寫,默認是大寫lower-case-service-id: trueroutes:- id: gateway-nacos-service-route# 其中配置的lb://表示從注冊中心獲取服務,后面的gateway-nacos-provider表示目標服務在注冊中心上的服務名uri: lb://gateway-nacos-providerpredicates:- Path=/nacos/sentinel/** #被限流filters:- StripPrefix=2# 自定義Sentinel API分組限流- id: red_routeuri: lb://gateway-nacos-providerpredicates:- Path=/red/** #被限流filters:- StripPrefix=1- id: green_routeuri: lb://gateway-nacos-providerpredicates:- Path=/green/** #不被限流filters:- StripPrefix=1下面的三個樣例均依賴此application.yml配置文件。
1、針對Route維度限流
搞一個ConfigurationClass,在類初始化的最后階段(@PostConstruct標注的方法)實例化一個GatewayFlowRule,并將其添加到GatewayRuleManager中;GatewayFlowRule中指定針對哪個Route限流、限流的規則是什么?
package com.saint.gateway.sentinel;import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set;import javax.annotation.PostConstruct;import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.web.reactive.result.view.ViewResolver;import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter; import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;/*** 針對某個Route限流** @author Saint*/ @Configuration public class GatewayConfiguration {private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {this.viewResolvers = viewResolvers.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer = serverCodecConfigurer;}/*** 注入一個全局限流過濾器SentinelGatewayFilter* Filter的優先級最高*/@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter();}/*** 注入限流異常處理器* Filter的優先級最高*/@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);}/*** 初始化限流規則*/@PostConstructpublic void doInit() {initGatewayRules();}/*** Route維度限流規則*/private void initGatewayRules() {Set<GatewayFlowRule> rules = new HashSet<>();// 這里表示1s僅允許通過一個請求,GatewayFlowRule構造函數中的如參為路由名GatewayFlowRule gatewayFlowRule = new GatewayFlowRule("gateway-nacos-service-route").setCount(1).setIntervalSec(1);rules.add(gatewayFlowRule);GatewayRuleManager.loadRules(rules);} }當前案例表示針對routeId為gateway-nacos-service-route的路由做限流:1s僅允許一個請求通過;
驗證
當請求被限流時會返回429狀態碼,響應體內容為:
{"code":429,"message":"Blocked by Sentinel: ParamFlowException"}2、針對API維度限流
自定義API分組限流,將/nacos/sentinel/**和/red/**進行統一分組,并提供name=saint_customized_api,然后在初始化網關限流規則時,針對該name設置限流規則;
package com.saint.gateway.sentinel;import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set;import javax.annotation.PostConstruct;import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.web.reactive.result.view.ViewResolver;import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants; import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem; import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem; import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager; import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter; import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;/*** 自定義API分組限流** @author Saint*/ @Configuration public class GatewayConfiguration1 {private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration1(ObjectProvider<List<ViewResolver>> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {this.viewResolvers = viewResolvers.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer = serverCodecConfigurer;}/*** 注入SentinelGatewayFilter* Filter的優先級最高*/@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter();}/*** 注入限流異常處理器* Filter的優先級最高*/@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);}/*** 初始化限流規則*/@PostConstructpublic void doInit() {initCustomizedApis();initGatewayRules();}/*** 自定義API分組限流,將/nacos/sentinel/**和/red/**進行統一分組,并提供name=saint_customized_api,然后在初始化網關限流規則時,針對該name設置* 限流規則。同時,可以通過setMatchStrategy來設置不同path下的限流參數策略*/private void initCustomizedApis() {Set<ApiDefinition> definitions = new HashSet<>();// 自定義針對API限流的ApiDefinition,apiName可以隨便取,在GatewayFlowRule中填入即可。ApiDefinition apiDefinition = new ApiDefinition("saint_customized_api");// 匹配下面請求路徑的請求將被限流apiDefinition.setPredicateItems(new HashSet<ApiPredicateItem>() {{add(new ApiPathPredicateItem().setPattern("/nacos/sentinel/**").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));add(new ApiPathPredicateItem().setPattern("/red/**").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));}});definitions.add(apiDefinition);GatewayApiDefinitionManager.loadApiDefinitions(definitions);}/*** 針對分組name來設置限流規則*/private void initGatewayRules() {// 針對ApiDefinition進行限流GatewayFlowRule rule = new GatewayFlowRule("saint_customized_api").setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME).setCount(1).setIntervalSec(1);Set<GatewayFlowRule> rules = new HashSet<>();rules.add(rule);GatewayRuleManager.loadRules(rules);} }驗證
當我們訪問/nacos/sentinel/**和/red/**路徑時才會被限流,其他路徑均不會。
3、自定義限流異常返回值
Sentinel默認的異常處理器是com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler,我們可以根據這個Handler自定義一個WebExceptionHandler實現類。
1> 自定義WebExceptionHandler實現類:
package com.saint.gateway.sentinel;import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.util.function.Supplier; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.result.view.ViewResolver; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebExceptionHandler; import reactor.core.publisher.Mono;import java.util.List;/*** 異常處理器*/ public class MySentinelGatewayBlockExceptionHandler implements WebExceptionHandler {private List<ViewResolver> viewResolvers;private List<HttpMessageWriter<?>> messageWriters;private final Supplier<ServerResponse.Context> contextSupplier = () -> {return new ServerResponse.Context() {public List<HttpMessageWriter<?>> messageWriters() {return MySentinelGatewayBlockExceptionHandler.this.messageWriters;}public List<ViewResolver> viewResolvers() {return MySentinelGatewayBlockExceptionHandler.this.viewResolvers;}};};public MySentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers,ServerCodecConfigurer serverCodecConfigurer) {this.viewResolvers = viewResolvers;this.messageWriters = serverCodecConfigurer.getWriters();}/*** 該方法的作用是,將限流的異常信息寫回客戶端*/private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {ServerHttpResponse serverHttpResponse = exchange.getResponse();serverHttpResponse.getHeaders().add("Content-type", "application/json;charset=UTF-8");byte[] datas = "{"code":999,"msg":"訪問人數太多了,讓我歇歇吧"}".getBytes();DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);return serverHttpResponse.writeWith(Mono.just(buffer));}public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {if (exchange.getResponse().isCommitted()) {return Mono.error(ex);} else {return !BlockException.isBlockException(ex) ? Mono.error(ex): this.handleBlockedRequest(exchange, ex).flatMap((response) -> this.writeResponse(response, exchange));}}private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);} }2> 將MySentinelGatewayBlockExceptionHandler注入到Spring容器中:
// 注入自定義的限流異常處理器 @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public MySentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {return new MySentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer); }驗證
接著根據API分組限流的案例進行驗證;
響應的狀態碼是200,body是我們自定義的body:
{"code":999,"msg":"訪問人數太多了,讓我歇歇吧"}三、總結
sentinel提供了兩種資源維度的限流:
- route維度:在配置文件中配置路由,資源名為對應的 routeId,一般是對某個微服務進行限流;這種維度屬于粗粒度的限流。
- 自定義API維度:通過Sentinel 提供的API來自定義一些API分組,針對某一類的uri進行匹配限流,可以跨多個微服務;這種維度屬于細粒度的限流。
本文的演示案例基于文章()
PS:Sentinel整合Nacos配置源:
spring:cloud: #配置SpringCloudGateway的路由sentinel:transport:dashboard: ip:port #sentinel控制臺的請求地址datasource:ds1:nacos:server-addr: ip:portdata-id: ${spring.application.name}-${spring.profiles.active}-sentinel-gw-flowgroup-id: DEFAULT_GROUPdata-type: json# 流控規則rule-type: gw-flowds2:nacos:server-addr: ip:portdata-id: ${spring.application.name}-${spring.profiles.active}-sentinel-gw-api-groupgroup-id: DEFAULT_GROUPdata-type: json# api類型rule-type: gw-api-groupeager: true #立即加載log:# 日志存放目錄dir: /data/sentinel/gateway引入maven依賴:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> </dependency>先自我介紹一下,小編13年上師交大畢業,曾經在小公司待過,去過華為OPPO等大廠,18年進入阿里,直到現在。深知大多數初中級java工程師,想要升技能,往往是需要自己摸索成長或是報班學習,但對于培訓機構動則近萬元的學費,著實壓力不小。自己不成體系的自學效率很低又漫長,而且容易碰到天花板技術停止不前。因此我收集了一份《java開發全套學習資料》送給大家,初衷也很簡單,就是希望幫助到想自學又不知道該從何學起的朋友,同時減輕大家的負擔。添加下方名片,即可獲取全套學習資料哦
總結
以上是生活随笔為你收集整理的【云原生微服务>SCG网关篇十二】Spring Cloud Gateway集成Sentinel API实现多种限流方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 台式计算机蓝牙完成配对,一分钟让台式电脑
- 下一篇: 文件格式转换