微服務實戰(八)整合Sentinel閘道器服務限流功能 SpringCloud GateWay + Sentinel + Nacos
本章主要內容
在SpringCloud GateWay服務閘道器中整合Sentinel來實現服務的限流功能。
首先,我們談一下什麼是服務限流。
在如今網際網路的大環境下,我們後端的介面呼叫頻次(QPS/TPS)動輒上百萬,甚至達到千萬級別,這對於服務端的承受能力是一個巨大的考驗。那麼限流在這個問題中起到什麼作用呢?其實就是對於流量進行有策略的管理和限制。 比如說在一個系統中,有訂單查詢服務,商品查詢服務,積分查詢等,如果一個系統能支援的QPS(每秒查詢次數)是100,那麼這100該如何如何分配就是限流功能所考慮的事情。
根據這些服務的重要性,我們可能會把商品查詢服務的QPS設定的比較高,因為這類使用者查詢到商品是有購買意向的,而訂單查詢、積分查詢啥的管他呢!那麼在一段時間內使用者訪問量過大時,我們就能儘量保證有購買意向的使用者正常使用系統。這就是限流的作用。
什麼是Sentinel
Sentinel 由阿里巴巴研發,主要以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。
文件:https://github.com/alibaba/Sentinel/wiki/主頁
原始碼:https://github.com/alibaba/Sentinel
其實文件還是非常全面的,我也就不做搬運工了,主要是有目的性地講述一下與SpringCloud GateWay的整合以及初步使用吧!
和SpringCloud GateWay 整合
新增依賴
先在我們之前搭建上的閘道器工程的pom.xml 中新增sentinel的閘道器依賴包
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
The managed version is 1.6.3 The artifact is managed in com.alibaba.cloud:spring-cloud-alibaba-dependencies:2.1.0.RELEASE
由於我們已經引入了 spring-cloud-alibaba-dependencies ,所以在上面就不用特意新增版本號了,預設就使用了1.6.3,與整體的SpringCloud版本對應。
注入Sentinel配置
我們寫一個 @Configuration 配置類(放在Boot的類路徑同級或者子級),裡面的程式碼主要作用是設定需要限流的API (根據路徑),以及限流的規則
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;
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@PostConstruct
public void doInit() {
initCustomizedApis();
initGatewayRules();
}
private void initCustomizedApis() {
/*
ApiDefinition:使用者自定義的 API 定義分組,可以看做是一些 URL 匹配的組合。
比如我們可以定義一個 API 叫 my_api,請求 path 模式為 /foo/** 和 /baz/** 的都歸到 my_api 這個 API 分組下面。
限流的時候可以針對這個自定義的 API 分組維度進行限流。
*/
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition("combat_gateway_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/nacos-provider/loadBanlance/print"));
}});
definitions.add(api1);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
private void initGatewayRules() {
/*
GatewayFlowRule:閘道器限流規則,
針對 API Gateway 的場景定製的限流規則,可以針對不同 route 或自定義的 API 分組進行限流,
支援針對請求中的引數、Header、來源 IP 等進行定製化的限流。
*/
Set<GatewayFlowRule> rules = new HashSet<>();
/*設定限流規則
count: QPS即每秒鐘允許的呼叫次數
intervalSec: 每隔多少時間統計一次彙總資料,統計時間視窗,單位是秒,預設是 1 秒。
*/
rules.add(new GatewayFlowRule("combat_gateway_api")
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(5)
.setIntervalSec(1)
);
GatewayRuleManager.loadRules(rules);
}
}
其中閘道器限流規則 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)
註冊動態規則源動態推送(推薦方式)。
限流效果測試
把我們之前章節中搭建的那些個玩意兒都執行起來吧。
我這裡執行了 nacos、combat-gateway、combat-provider
然後通過閘道器,呼叫我們之前寫好的API介面。
http://127.0.0.1:9000/nacos-provider/loadBanlance/print
(注意呼叫的介面需要在 GatewayConfiguration 中設定好限流的路徑)
在瀏覽器或者PostMan中呼叫介面,連續重新整理。
然後我們檢視Sentinel的日誌(啟動時會輸出日誌地址)
下面就是限流的統計資料
passQps | blockQps | successQps | exceptionQps | rt | occupiedPassQps | concurrency | classification |
5 | 36 | 9 | 0 | 1275 | 0 | 0 | 3 |
5 | 49 | 5 | 0 | 130 | 0 | 0 | 3 |
其中 passQps 代表通過的請求, blockQps 代表被阻止的請求, successQps 代表成功執行完成的請求個數, exceptionQps 代表使用者自定義的異常, rt
代表平均響應時長。
由於之前設定的限流是 QPS =5 ,可以看到上面的限流日誌中,通過的請求數是 <=5/s的。
rules.add(new GatewayFlowRule("combat_gateway_api")
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(5)
.setIntervalSec(1)
自定義限流的返回值
通過剛才的測試發現,被限流後的返回編碼是429。
Server returned HTTP response code: 429 for URL: http://127.0.0.1:9000/nacos-provider/loadBanlance/print
如何讓它按照我們業務系統的規範返回定製的錯誤資訊呢?這樣才便於我們針對閘道器限流後的處理。
在之前的 GatewayConfiguration 中註冊一個限流處理器
GatewayCallbackManager.setBlockHandler(new MyBlockRequestHandler());
實現BlockRequestHandler
package com.zjf.combat.sentinel;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import reactor.core.publisher.Mono;
public class MyBlockRequestHandler implements BlockRequestHandler {
private static final String DEFAULT_BLOCK_MSG_PREFIX = "Blocked by Sentinel: ";
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable ex) {
// 返回http狀態碼為200
return ServerResponse.status(200).contentType(MediaType.APPLICATION_JSON_UTF8)
.body(fromObject(buildErrorResult(ex)));
}
private ErrorResult buildErrorResult(Throwable ex) {
return new ErrorResult(200,
DEFAULT_BLOCK_MSG_PREFIX + ex.getClass().getSimpleName());
}
private static class ErrorResult {
private final int code;
private final String message;
ErrorResult(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
}
被限流後的請求返回
相關文章
- SpringCloud Alibaba(二) - Sentinel,整合OpenFeign,GateWay服務閘道器SpringGCCloudGateway
- Spring Cloud Gateway 整合阿里 Sentinel閘道器限流實戰!SpringCloudGateway阿里
- springcloud服務閘道器-gatewaySpringGCCloudGateway
- SpringCloud 微服務閘道器 Gateway 元件SpringGCCloud微服務Gateway元件
- SpringCloud Alibaba實戰(11:引入服務閘道器Gateway)SpringGCCloudGateway
- 微服務(七)Gateway服務閘道器微服務Gateway
- SpringCloud Gateway微服務閘道器實戰與原始碼分析-上SpringGCCloudGateway微服務原始碼
- 微服務閘道器實戰——Spring Cloud Gateway微服務SpringCloudGateway
- SpringCloud微服務專案實戰 - API閘道器Gateway詳解實現SpringGCCloud微服務APIGateway
- SpringCloud微服務實戰——搭建企業級開發框架(十四):整合Sentinel高可用流量管理框架【限流】SpringGCCloud微服務框架
- SpringCloud系列之API閘道器(Gateway)服務ZuulSpringGCCloudAPIGatewayZuul
- 微服務閘道器Spring Cloud Gateway的應用實戰微服務SpringCloudGateway
- 微服務閘道器Gateway實踐總結微服務Gateway
- 【SpringCloud技術專題】「Gateway閘道器係列」(3)微服務閘道器服務的Gateway全流程開發實踐指南(2.2.X)SpringGCCloudGateway微服務
- 微服務閘道器 Spring Cloud Gateway微服務SpringCloudGateway
- Sentinel分散式限流元件,SpringCloud Alibaba整合分散式元件SpringGCCloud
- ⑥SpringCloud 實戰:引入gateway元件,開啟閘道器路由功能SpringGCCloudGateway元件路由
- 微服務架構 | 5.2 基於 Sentinel 的服務限流及熔斷微服務架構
- SpringCloud使用Sentinel,Sentinel持久化,Sentinel使用nacos持久化SpringGCCloud持久化
- Sentinel 實戰-限流篇
- 《吃透微服務》 - 服務容錯之Sentinel微服務
- 使用springcloud gateway搭建閘道器(分流,限流,熔斷)SpringGCCloudGateway
- 微服務03 微服務sentinel, springcloudgateway微服務SpringGCCloudGateway
- SpringCloud微服務治理三(Zuul閘道器)SpringGCCloud微服務Zuul
- 構建SpringCloud閘道器服務SpringGCCloud
- 《springcloud 二》微服務動態閘道器,閘道器叢集SpringGCCloud微服務
- Spring Cloud Alibaba | Sentinel: 服務限流基礎篇SpringCloud
- Spring Cloud Alibaba | Sentinel: 服務限流高階篇SpringCloud
- 《springcloud 二》SrpingCloud Zuul 微服務閘道器搭建SpringGCCloudZuul微服務
- 阿里springcloud alibaba 閘道器gateway + nacos 遇到503錯誤阿里SpringGCCloudGateway
- 微服務元件 Sentinel(三)微服務元件
- 微服務元件 Sentinel(二)微服務元件
- 微服務元件 Sentinel(一)微服務元件
- 微服務與閘道器技術(SIA-GateWay)微服務Gateway
- 微服務閘道器SIA-GateWay使用指南微服務Gateway
- 微服務閘道器Zuul遷移到Spring Cloud Gateway微服務ZuulSpringCloudGateway
- Gateway服務閘道器 (入門到使用)Gateway
- Spring Cloud Alibaba系列(五)sentinel實現服務限流降級SpringCloud