1 Sentinel首頁
github.com/alibaba/Sen…
1.1 Sentinel介紹
随着微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 是面向分布式服務架構的流量控制元件,主要以流量為切入點,從限流、流量整形、熔斷降級、系統負載保護、熱點防護等多個次元來幫助開發者保障微服務的穩定性。
1)Sentinel核心元件
1:核心庫(Java 用戶端):不依賴任何架構/庫,能夠運作于 Java 7 及以上的版本的運作時環境,同時對 Dubbo / Spring Cloud 等架構也有較好的支援。
2:控制台(Dashboard):控制台主要負責管理推送規則、監控、叢集限流配置設定管理、機器發現等。
2)Sentinel vs Hystrix
對比内容 | Sentinel | Hystrix |
隔離政策 | 信号量隔離 | 線程池隔離/信号量隔離 |
熔斷降級政策 | 基于響應時間或失敗比率 | 基于失敗比率 |
實時名額實作 | 滑動視窗 | 滑動視窗(基于 RxJava) |
規則配置 | 支援多種資料源 | 支援多種資料源 |
擴充性 | 多個擴充點 | 插件的形式 |
基于注解的支援 | 支援 | 支援 |
限流 | 基于 QPS,支援基于調用關系的限流 | 不支援 |
流量整形 | 支援慢啟動、勻速器模式 | 不支援 |
系統負載保護 | 支援 | 不支援 |
控制台 | 開箱即用,可配置規則、檢視秒級監控、機器發現等 | 不完善 |
常見架構的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
3)Sentinel基本概念
- 資源
資源是 Sentinel 的關鍵概念。它可以是 Java 應用程式中的任何内容,例如,由應用程式提供的服務,或由應用程式調用的其它應用提供的服務,甚至可以是一段代碼。
隻要通過 Sentinel API 定義的代碼,就是資源,能夠被 Sentinel 保護起來。大部分情況下,可以使用方法簽名,URL,甚至服務名稱作為資源名來标示資源。
- 規則
圍繞資源的實時狀态設定的規則,可以包括流量控制規則、熔斷降級規則以及系統保護規則。所有規則可以動态實時調整。
1.2 Sentinel核心功能
1.2.1 流量控制
流量控制在網絡傳輸中是一個常用的概念,它用于調整網絡包的發送資料。然而,從系統穩定性角度考慮,在處理請求的速度上,也有非常多的講究。任意時間到來的請求往往是随機不可控的,而系統的處理能力是有限的。我們需要根據系統的處理能力對流量進行控制。Sentinel 作為一個調配器,可以根據需要把随機的請求調整成合适的形狀,如下圖所示:
流量控制有以下幾個角度:
- 資源的調用關系,例如資源的調用鍊路,資源和資源之間的關系;
- 運作名額,例如 QPS、線程池、系統負載等;
- 控制的效果,例如直接限流、冷啟動、排隊等。
Sentinel 的設計理念是讓您自由選擇控制的角度,并進行靈活組合,進而達到想要的效果。
1.2.2 熔斷降級
1)什麼是熔斷降級
除了流量控制以外,及時對調用鍊路中的不穩定因素進行熔斷也是 Sentinel 的使命之一。由于調用關系的複雜性,如果調用鍊路中的某個資源出現了不穩定,可能會導緻請求發生堆積,進而導緻級聯錯誤。
Sentinel 和 Hystrix 的原則是一緻的: 當檢測到調用鍊路中某個資源出現不穩定的表現,例如請求響應時間長或異常比例升高的時候,則對這個資源的調用進行限制,讓請求快速失敗,避免影響到其它的資源而導緻級聯故障。
2)Sentinel熔斷降級設計
Hystrix 通過 線程池隔離 的方式,來對依賴(在 Sentinel 的概念中對應 資源)進行了隔離。這樣做的好處是資源和資源之間做到了最徹底的隔離。缺點是除了增加了線程切換的成本(過多的線程池導緻線程數目過多),還需要預先給各個資源做線程池大小的配置設定。
Sentinel熔斷降級設計:
并發線程數限制:和資源池隔離的方法不同,Sentinel 通過限制資源并發線程的數量,來減少不穩定資源對其它資源的影響。這樣不但沒有線程切換的損耗,也不需要您預先配置設定線程池的大小。當某個資源出現不穩定的情況下,例如響應時間變長,對資源的直接影響就是會造成線程數的逐漸堆積。當線程數在特定資源上堆積到一定的數量之後,對該資源的新請求就會被拒絕。堆積的線程完成任務後才開始繼續接收請求。
響應時間降級:除了對并發線程數進行控制以外,Sentinel 還可以通過響應時間來快速降級不穩定的資源。當依賴的資源出現響應時間過長後,所有對該資源的通路都會被直接拒絕,直到過了指定的時間視窗之後才重新恢複。
3)系統自适應保護
Sentinel 同時提供系統次元的自适應保護能力。防止雪崩,是系統防護中重要的一環。當系統負載較高的時候,如果還持續讓請求進入,可能會導緻系統崩潰,無法響應。在叢集環境下,網絡負載均衡會把本應這台機器承載的流量轉發到其它的機器上去。如果這個時候其它的機器也處在一個邊緣狀态的時候,這個增加的流量就會導緻這台機器也崩潰,最後導緻整個叢集不可用。
針對這個情況,Sentinel 提供了對應的保護機制,讓系統的入口流量和系統的負載達到一個平衡,保證系統在能力範圍之内處理最多的請求。
Sentinel內建Gateway
我們的項目流量入口是
SpringCloud Gateway
,是以我們重點講解Sentinel內建
Gateway
。
3.1 Sentinel對網關支援
Sentinel 支援對 Spring Cloud Gateway、Zuul 等主流的 API Gateway 進行限流。
Sentinel 1.6.0 引入了
Sentinel API Gateway Adapter Common
子產品,此子產品中包含網關限流的規則和自定義 API 的實體和管理邏輯
從 1.6.0 版本開始,Sentinel 提供了 Spring Cloud Gateway 的适配子產品,可以提供兩種資源次元的限流:
- route 次元:即在 Spring 配置檔案中配置的路由條目,資源名為對應的 routeId
spring:
gateway:
#路由配置
routes:
#唯一辨別符
- id: hailtaxi-driver
uri: lb://hailtaxi-driver
#路由斷言
predicates:
- Path=/driver/**
#唯一辨別符
- id: hailtaxi-order
uri: lb://hailtaxi-order
#路由斷言
predicates:
- Path=/order/**
自動将每個路由辨別為資源,
-
自定義 API 次元:使用者可以利用 Sentinel 提供的 API 來自定義一些 API 分組
這兩種次元分别對應如下:
- GatewayFlowRule:網關限流規則,針對 API Gateway 的場景定制的限流規則,可以針對不同 route 或自定義的 API 分組進行限流,支援針對請求中的參數、Header、來源 IP 等進行定制化的限流。
- ApiDefinition:使用者自定義的 API 定義分組,可以看做是一些 URL 比對的組合。比如我們可以定義一個 API 叫
,請求 path 模式為 my_api
和 /foo/**
的都歸到 /baz/**
這個 API 分組下面。限流的時候可以針對這個自定義的 API 分組次元進行限流。my_api
其中網關限流規則
GatewayFlowRule
的字段解釋如下:
-
:資源名稱,可以是網關中的 route 名稱或者使用者自定義的 API 分組名稱。resource
-
:規則是針對 API Gateway 的 route(resourceMode
)還是使用者在 RESOURCE_MODE_ROUTE_ID
中定義的 API 分組(Sentinel
),預設是 route。RESOURCE_MODE_CUSTOM_API_NAME
-
:限流名額次元,同限流規則的 grade
字段。grade
-
:限流門檻值count
-
:統計時間視窗,機關是秒,預設是 1 秒。intervalSec
-
:流量整形的控制效果,同限流規則的 controlBehavior
字段,目前支援快速失敗和勻速排隊兩種模式,預設是快速失敗。controlBehavior
-
:應對突發請求時額外允許的請求數目。burst
-
:勻速排隊模式下的最長排隊時間,機關是毫秒,僅在勻速排隊模式下生效。maxQueueingTimeoutMs
-
:參數限流配置。若不提供,則代表不針對參數進行限流,該網關規則将會被轉換成普通流控規則;否則會轉換成熱點規則。其中的字段:paramItem
-
:從請求中提取參數的政策,目前支援提取來源 IP(parseStrategy
)、Host(PARAM_PARSE_STRATEGY_CLIENT_IP
)、任意 Header(PARAM_PARSE_STRATEGY_HOST
)和任意 URL 參數(PARAM_PARSE_STRATEGY_HEADER
)四種模式。PARAM_PARSE_STRATEGY_URL_PARAM
-
:若提取政策選擇 Header 模式或 URL 參數模式,則需要指定對應的 header 名稱或 URL 參數名稱。fieldName
-
:參數值的比對模式,隻有比對該模式的請求屬性值會納入統計和流控;若為空則統計該請求屬性的所有值。(1.6.2 版本開始支援)pattern
-
:參數值的比對政策,目前支援精确比對(matchStrategy
)、子串比對(PARAM_MATCH_STRATEGY_EXACT
)和正則比對(PARAM_MATCH_STRATEGY_CONTAINS
)。(1.6.2 版本開始支援)PARAM_MATCH_STRATEGY_REGEX
使用者可以通過
GatewayRuleManager.loadRules(rules)
手動加載網關規則,或通過
GatewayRuleManager.register2Property(property)
注冊動态規則源動态推送(推薦方式)。
3.2 GateWay內建Sentinel
我們如果想要讓微服務網關內建Sentinel,需要引入依賴包,使用時隻需注入對應的
SentinelGatewayFilter
執行個體以及
SentinelGatewayBlockExceptionHandler
執行個體即可。
1、首先在
hailtaxi-gateway
中引入如下依賴:
<!--Sentinel-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.8.0</version>
</dependency>
2、執行個體引入:建立配置類
com.itheima.config.GatewayConfiguration
:
package com.itheima.config;
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.GatewayParamFlowItem;
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;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
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 javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@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;
}
/**
* 限流的異常處理器
* @return
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
/***
* Sentinel路由處理核心過濾器
* @return
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@PostConstruct
public void doInit() {
// 自定義 api 分組
initCustomizedApis();
// 初始化網關流控規則
initGatewayRules();
}
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition("customer_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/order/**")
/**
* 比對政策:
* URL_MATCH_STRATEGY_EXACT:url精确比對
* URL_MATCH_STRATEGY_PREFIX:url字首比對
* URL_MATCH_STRATEGY_REGEX:url正則比對
*/
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(api1);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("hailtaxi-driver") // 資源名稱,可以是網關中的 routeid或者使用者自定義的 API分組名稱
.setCount(2) // 限流門檻值
.setIntervalSec(10) // 統計時間視窗預設1s
.setGrade(RuleConstant.FLOW_GRADE_QPS) // 限流模式
/**
* 限流行為:
* CONTROL_BEHAVIOR_RATE_LIMITER 勻速排隊
* CONTROL_BEHAVIOR_DEFAULT 快速失敗(預設)
* CONTROL_BEHAVIOR_WARM_UP:
* CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:
*/
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
//勻速排隊模式下的最長排隊時間,機關是毫秒,僅在勻速排隊模式下生效
.setMaxQueueingTimeoutMs(1000)
/**
* 熱點參數限流配置
* 若不設定,該網關規則将會被轉換成普通流控規則;否則會轉換成熱點規則
*/
.setParamItem(new GatewayParamFlowItem()
/**
* 從請求中提取參數的政策:
* PARAM_PARSE_STRATEGY_CLIENT_IP
* PARAM_PARSE_STRATEGY_HOST
* PARAM_PARSE_STRATEGY_HEADER
* PARAM_PARSE_STRATEGY_URL_PARAM
*/
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
/**
* 若提取政策選擇 Header 模式或 URL 參數模式,
* 則需要指定對應的 header 名稱或 URL 參數名稱。
*/
.setFieldName("token")
/**
* 參數的比對政策:
* PARAM_MATCH_STRATEGY_EXACT
* PARAM_MATCH_STRATEGY_PREFIX
* PARAM_MATCH_STRATEGY_REGEX
* PARAM_MATCH_STRATEGY_CONTAINS
*/
.setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
//參數值的比對模式,隻有比對該模式的請求屬性值會納入統計和流控
.setPattern("123456") // token=123456 10s内qps達到2次會被限流
)
);
rules.add(new GatewayFlowRule("customer_api")
/**
* 規則是針對 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID)
* 還是使用者在 Sentinel 中定義的 API 分組(RESOURCE_MODE_CUSTOM_API_NAME),預設是 route。
*/
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(2)
.setIntervalSec(1)
.setGrade(RuleConstant.FLOW_GRADE_QPS)
);
GatewayRuleManager.loadRules(rules);
}
}
此時內建就完成了。
3、啟動
hailtaxi-gateway
,
hailtaxi-drvier
,
hailtaxi-order
測試: