我們在上一篇文章中對 Sentinel 已經(jīng)有了基本的了解,接下來,我們一起對它的進(jìn)階進(jìn)行學(xué)習(xí)吧!
Sentinel的概念和功能
基本概念
資源
所謂資源就是 Sentinel 要保護(hù)的東西,資源是 Sentinel 的關(guān)鍵概念。它可以是Java應(yīng)用程序中的任何內(nèi)容,可以是一個服務(wù),也可以是一個方法,甚至可以是一段代碼。
我們?nèi)腴T案例中的 message1 方法就可以認(rèn)為是一個資源。
規(guī)則
規(guī)則就是用來定義如何進(jìn)行保護(hù)資源的。規(guī)則作用在資源之上,定義以什么樣的方式保護(hù)資源,主要包括流量控制規(guī)則、熔斷降級規(guī)則以及系統(tǒng)保護(hù)規(guī)則。
我們?nèi)腴T案例中就是為 message1 資源設(shè)置了一種流控規(guī)則,限制了進(jìn)入message1的流量。
重要功能
Sentinel 的主要功能就是容錯,主要體現(xiàn)為:
流量控制
流量控制在網(wǎng)絡(luò)傳輸中是一個常用的概念,它用于調(diào)整網(wǎng)絡(luò)包的數(shù)據(jù)。任意時間到來的請求往往是隨機(jī)不可控的,而系統(tǒng)的處理能力是有限的。我們需要根據(jù)系統(tǒng)的處理能力對流量進(jìn)行控制。Sentinel 作為一個調(diào)配器,可以根據(jù)需要把隨機(jī)的請求調(diào)整成合適的形狀。
熔斷降級
當(dāng)檢測到調(diào)用鏈路中某個資源出現(xiàn)不穩(wěn)定的表現(xiàn),例如請求響應(yīng)時間長或異常比例升高的時候,則對這個資源的調(diào)用進(jìn)行限制,讓請求快速失敗,避免影響到其它的資源而導(dǎo)致級聯(lián)故障。
Sentinel對這個問題采取了兩種手段:
- 通過并發(fā)線程數(shù)進(jìn)行限制:Sentinel 通過限制資源并發(fā)線程的數(shù)量,來減少不穩(wěn)定資源對其它資源的影響。當(dāng)某個資源出現(xiàn)不穩(wěn)定的情況下,例如響應(yīng)時間變長,對資源的直接影響就是會造成線程數(shù)的逐步堆積。當(dāng)線程數(shù)在特定資源上堆積到一定的數(shù)量之后,對該資源的新請求就會被拒絕。堆積的線程完成任務(wù)后才開始繼續(xù)接收請求。
- 通過響應(yīng)時間對資源進(jìn)行降級:除了對并發(fā)線程數(shù)進(jìn)行控制以外, Sentinel 還可以通過響應(yīng)時間來快速降級不穩(wěn)定的資源。當(dāng)依賴的資源出現(xiàn)響應(yīng)時間過長后,所有對該資源的訪問都會被直接拒絕,直到過了指定的時間窗口之后才重新恢復(fù)。
Sentinel和Hystrix對熔斷降級處理的區(qū)別
兩者的原則是一致的,都是當(dāng)一個資源出現(xiàn)問題時,讓其快速失敗,不要波及到其它服務(wù)。但是在限制的手段上,卻采取了完全不一樣的方法:
- Hystrix 采用的是線程池隔離的方式,優(yōu)點是做到了資源之間的隔離,缺點是增加了線程切換的成本。
- Sentinel 采用的是通過并發(fā)線程的數(shù)量和響應(yīng)時間來對資源做限制。
系統(tǒng)負(fù)載保護(hù)
Sentinel 同時提供系統(tǒng)維度的自適應(yīng)保護(hù)能力。當(dāng)系統(tǒng)負(fù)載較高的時候,如果還持續(xù)讓請求進(jìn)入可能會導(dǎo)致系統(tǒng)崩潰,無法響應(yīng)。
在集群環(huán)境下,會把本應(yīng)這臺機(jī)器承載的流量轉(zhuǎn)發(fā)到其它的機(jī)器上去。如果這個時候其它的機(jī)器也處在一個邊緣狀態(tài)的時候, Sentinel 提供了對應(yīng)的保護(hù)機(jī)制,讓系統(tǒng)的入口流量和系統(tǒng)的負(fù)載達(dá)到一個平衡,保證系統(tǒng)在能力范圍之內(nèi)處理最多的請求。
總之一句話:我們需要做的事情,就是在 Sentinel 的資源上配置各種各樣的規(guī)則,來實現(xiàn)各種容錯的功能。
SentineI 規(guī)則
流控規(guī)則
流量控制,其原理是監(jiān)控應(yīng)用流量的QPS(每秒查詢率)或并發(fā)線程數(shù)等指標(biāo),當(dāng)達(dá)到指定的閾值時對流量進(jìn)行控制,以避免被瞬時的流量高峰沖垮,從而保障應(yīng)用的高可用性。
點擊簇點鏈路,我們就可以看到訪問過的接口地址,然后點擊對應(yīng)的流控按鈕,進(jìn)入流控規(guī)則配置頁面。新增流控規(guī)則界面如下:
- 資源名:唯一名稱,默認(rèn)是請求路徑,可自定義。
- 針對來源:指定對哪個微服務(wù)進(jìn)行限流,默認(rèn)指default,意思是不區(qū)分來源,全部限制。
-
閾值類型單機(jī)閾值:
- QPS(每秒請求數(shù)量):當(dāng)調(diào)用該接口的QPS達(dá)到閾值的時候,進(jìn)行限流;
- 線程數(shù):當(dāng)調(diào)用該接口的線程數(shù)達(dá)到閾值的時候,進(jìn)行限流;
- 是否集群:暫不需要集群。
接下來我們以QPS為例來研究限流規(guī)則的配置。
簡單配置
我們先做一個簡單配置,設(shè)置閾值類型為QPS,單機(jī)閾值為3。即每秒請求量大于3的時候開始限流。接下來,在流控規(guī)則頁面就可以看到這個配置。
配置流控模式
點擊上面設(shè)置流控規(guī)則的編輯按鈕,然后在編輯頁面點擊高級選項,會看到有流控模式一欄
sentinel 共有三種流控模式,分別是:
- 直接(默認(rèn)):接口達(dá)到限流條件時,開啟限流。
- 關(guān)聯(lián):當(dāng)關(guān)聯(lián)的資源達(dá)到限流條件時,開啟限流(適合做應(yīng)用讓步)。
- 鏈路:當(dāng)從某個接口過來的資源達(dá)到限流條件時,開啟限流。
下面呢分別演示三種模式:
直接流控模式
直接流控模式是最簡單的模式,當(dāng)指定的接口達(dá)到限流條件時開啟限流。上面案例使用的就是直接流控模式。
關(guān)聯(lián)流控模式
關(guān)聯(lián)流控模式指的是,當(dāng)指定接口關(guān)聯(lián)的接口達(dá)到限流條件時,開啟對指定接口開啟限流。
第1步:配置限流規(guī)則,將流控模式設(shè)置為關(guān)聯(lián),關(guān)聯(lián)資源設(shè)置為的 /order/message2
第2步:通過 Apache JMeter 軟件向 /order/message2 連續(xù)發(fā)送請求,注意QPS一定要大于3
第3步:通過瀏覽器訪問/order/message1
,會發(fā)現(xiàn)已經(jīng)被限流
鏈路流控模式
鏈路流控模式指的是,當(dāng)從某個接口過來的資源達(dá)到限流條件時,開啟限流。它的功能有點類似于【針對來源配置項】,區(qū)別在于:針對來源是針對上級微服務(wù),而鏈路流控是針對上級接口,也就是說它的粒度更細(xì)。
第1步:編寫一個Service,在里面添加一個方法 message
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, ShopOrder> implements IOrderService {
@Override
@SentinelResource("message")
public void message() {
System.out.println("message");
}
}
第2步:在Controller中聲明兩個方法,分別調(diào)用service中的方法message
@RestController
@RequestMapping("/order")
@Slf4j
public class OrderController3 {
@Autowired
private IOrderService orderService;
@RequestMapping("/message1")
public String message1() {
orderService.message();
return"message1";
}
@RequestMapping("/message2")
public String message2() {
orderService.message();
return"message2";
}
}
第3步:禁止收斂URL的入口context
從1.6.3版本開始,SentinelWebFluxFilter默認(rèn)收斂所有URL的入口context,因此鏈路限流不生效。
1.7.0版本開始(對應(yīng)SCA的2.1.1.RELEASE
),官方在CommonFilter引入了WEB_CONTEXT_UNIFY
參數(shù),用于控制是否收斂 context 。將其配置為 false 即可根據(jù)不同的URL 進(jìn)行鏈路限流。
SCA 2.1.1.RELEASE
之后的版本,可以通過配置spring.cloud.sentinel.web-context-unify=false
即可關(guān)閉收斂。我們當(dāng)前使用的版本是Spring Cloud Alibaba 2.1.0.RELEASE
,無法實現(xiàn)鏈路限流。
目前官方還未發(fā)布SCA 2.1.2.RELEASE
,所以我們只能使用2.1.1.RELEASE
,需要寫代碼的形式實現(xiàn)
(1)暫時將Spring Cloud Alibaba
的版本調(diào)整為2.1.1.RELEASE
,如果引入的依賴加了版本號也記得改一下
<spring-cloud-alibaba.version>2.1.1.RELEASE</spring-cloud-alibaba.version>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
(2)配置文件中關(guān)閉 sentinel 的 CommonFilter 實例化
spring:
cloud:
sentinel:
filter:
enabled: false
(3)添加一個配置類,自己構(gòu)建CommonFilter實例
@Configuration
public class FilterContextConfig {
@Bean
public FilterRegistrationBean sentinelFilterRegistration(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new CommonFilter());
filterRegistrationBean.addUrlPatterns("/*");
//入口資源關(guān)閉聚合
filterRegistrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY,"false");
filterRegistrationBean.setName("sentinelFilter");
filterRegistrationBean.setOrder(1);
return filterRegistrationBean;
}
}
第4步:控制臺配置限流規(guī)則
第5步:分別通過/order/message1
和/order/message2
訪問,發(fā)現(xiàn)2沒問題,1的被限流了
配置流控效果
- 快速失敗(默認(rèn)):直接失敗,拋出異常,不做任何額外的處理,是最簡單的效果。
- Warm Up:它從開始閾值到最大QPS閾值會有一個緩沖階段,一開始的閾值是最大QPS閾值的1/3,然后慢慢增長,直到最大閾值,適用于將突然增大的流量轉(zhuǎn)換為緩步增長的場景。
- 排隊等待:讓請求以均勻的速度通過,單機(jī)閾值為每秒通過數(shù)量,其余的排隊等待;它還會讓設(shè)置一個超時時間,當(dāng)請求超過超時時間還未處理,則會被丟棄。
熔斷規(guī)則
熔斷規(guī)則就是設(shè)置當(dāng)滿足什么條件的時候,對服務(wù)進(jìn)行熔斷。Sentinel提供了三個衡量條件:
慢調(diào)用比例
在1000ms內(nèi),請求至少有10個的情況下,出現(xiàn)了大于等于10%的請求調(diào)用時間大于100毫秒,則在3秒內(nèi)會自動熔斷,觸發(fā)降級。
參數(shù)解釋:
- 最大RT:調(diào)用接口的最大時間。
- 比例閾值:超過了最大RT調(diào)用時間的請求的比例。
- 熔斷時長:觸發(fā)熔斷后,熔斷的時間。
- 最小請求數(shù)據(jù):每秒最少的請求數(shù)量,只有大于等于這個數(shù)量,才會觸發(fā)熔斷策略。
- 統(tǒng)計時長:時間窗口的概念。
注意 Sentinel 默認(rèn)統(tǒng)計的RT上限是 4900 ms,超出此閾值的都會算作4900ms,若需要變更此上限可以通過啟動配置項
-Dcsp.sentinel.statistic.max.rt=xxx
來配置。
異常比例
如圖所示,即/order/message2接口,在1000ms內(nèi),請求至少有5個的情況下,出現(xiàn)了大于等于10%的請求拋出了異常,則在3秒內(nèi)會自動熔斷,觸發(fā)降級。
第1步:首先模擬一個異常
int i = 0;
@RequestMapping("/message2")
public String message2() {
i++;
if(i%3==0){
throw new RuntimeException();
}
return"message2";
}
第2步:設(shè)置異常比例為0.25
不想用 Apache JMeter 進(jìn)行測試的小伙伴,可以直接使用 Apifox 進(jìn)行測試,首先選中左側(cè)tab中的【自動化測試】,然后選擇【添加測試場景】
【添加步驟】選擇【添加自定義請求】
添加自己的請求,保存并返回,在右側(cè)設(shè)置好運行參數(shù),然后點擊【運行】按鈕。
此時再在瀏覽器去訪問/order4/message2
接口就會出現(xiàn)Blocked by Sentinel (flow limiting)
信息。
異常數(shù)
在一秒內(nèi),請求至少有5個的情況下,出現(xiàn)了大于等于一個的請求拋出了異常,則在3秒內(nèi)會自動熔斷,觸發(fā)降級。
熱點規(guī)則
熱點參數(shù)流控規(guī)則是一種更細(xì)粒度的流控規(guī)則,它允許將規(guī)則具體到參數(shù)上。
熱點規(guī)則簡單使用
- 編寫代碼
@RequestMapping("/message3")
//注意這里必須使用這個注解標(biāo)識,否則熱點規(guī)則不生效
@SentinelResource("message3")
public String message3(String name,String age) {
return name + age;
}
- 配置熱點規(guī)則
- 分別用兩個參數(shù)訪問,會發(fā)現(xiàn)只對第一個參數(shù)限流了
熱點規(guī)則增強(qiáng)使用
【參數(shù)例外項】允許對一個參數(shù)的具體值進(jìn)行流控。編輯剛才定義的規(guī)則,增加【參數(shù)例外項】
參數(shù)值為zha,限流閾值為10,這樣當(dāng)訪問路徑上第一個參數(shù)name的值為zha時,在一秒(統(tǒng)計窗口時長)內(nèi)訪問超過10次(單機(jī)閾值)才會發(fā)生限流,如果第一個參數(shù)name的值不是zha時,限流的閾值還是1,如果不帶參數(shù)name不會觸發(fā)限流,注意指定的參數(shù)類型要與方法的參數(shù)類型保持一致。
授權(quán)規(guī)則
很多時候,我們需要根據(jù)調(diào)用來源來判斷該次請求是否允許放行,這時候可以使用Sentinel的來源訪問控制的功能。來源訪問控制根據(jù)資源的請求來源(origin)限制資源是否通過:
- 若配置白名單,則只有請求來源位于白名單內(nèi)時才可通過;
- 若配置黑名單,則請求來源位于黑名單時不通過,其余的請求通過。
上面的資源名和授權(quán)類型不難理解,但是流控應(yīng)用怎么填寫呢?
其實這個位置要填寫的是來源標(biāo)識,Sentinel 提供了 RequestOriginParser 接口來處理來源。只要 Sentinel 保護(hù)的接口資源被訪問,Sentinel 就會調(diào)用 RequestOriginParser 的實現(xiàn)類去解析訪問來源。
- 自定義來源處理規(guī)則
@Service
public class RequestOriginParserDefinition implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
String serviceName = httpServletRequest.getParameter("serviceName");
return serviceName;
}
}
- 授權(quán)規(guī)則配置
這個配置的意思是只有serviceName=pc不能訪問(黑名單)
- 訪問
訪問http://localhost:8091/order4/message1?serviceName=pc
返回Blocked by Sentinel (flow limiting)
系統(tǒng)規(guī)則
系統(tǒng)保護(hù)規(guī)則是從應(yīng)用級別的入口流量進(jìn)行控制,從單臺機(jī)器的總體 Load、RT、入口QPS、CPU使用率和線程數(shù)五個維度監(jiān)控應(yīng)用數(shù)據(jù),讓系統(tǒng)盡可能跑在最大吞吐量的同時保證系統(tǒng)整體的穩(wěn)定性。
系統(tǒng)保護(hù)規(guī)則是應(yīng)用整體維度的,而不是資源維度的,并且僅對入口流量(進(jìn)入應(yīng)用的流量)生效。
- Load (僅對Linux/Unix-like 機(jī)器生效):當(dāng)系統(tǒng) load1 超過閾值,且系統(tǒng)當(dāng)前的并發(fā)線程數(shù)超過系統(tǒng)容量時才會觸發(fā)系統(tǒng)保護(hù)。系統(tǒng)容量由系統(tǒng)的
maxQps*minRt
計算得出。設(shè)定參考值一般是CPU cores*2.5
。 - RT:當(dāng)單臺機(jī)器上所有入口流量的平均 RT 達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù),單位是毫秒。
- 線程數(shù):當(dāng)單臺機(jī)器上所有入口流量的并發(fā)線程數(shù)達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
- 入口QPS:當(dāng)單臺機(jī)器上所有入口流量的QPS達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
- CPU使用率:當(dāng)單臺機(jī)器上所有入口流量的CPU使用率達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
擴(kuò)展:自定義異常返回
@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
//BlockException 異常接口,包含Sentinel的五個異常
// @SentinelResource 用于定義資源,并提供可選的異常處理和fallback 配置項。其主要參數(shù)如下:
// FlowException 限流異常
// DegradeException 降級異常
// ParamFlowException 參數(shù)限流異常
// AuthorityException 授權(quán)異常
// SystemBlockException 系統(tǒng)負(fù)載異常
BlockException e) throws IOException {
httpServletResponse.setContentType("application/json;charset=utf-8");
ResponseData data = null;
if(e instanceof FlowException) {
data = new ResponseData(-1, "接口被限流了…");
}else if (e instanceof DegradeException){
data= new ResponseData(-2, "接口被降級了…");
}
httpServletResponse.getWriter().write(JSON.toJSONString(data));
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResponseData {
private int code;
private String message;
}
總結(jié)
到這兒,服務(wù)容錯中間件Sentinel的概念和功能以及五大規(guī)則就已經(jīng)介紹完了。下一篇將為大家?guī)砣蒎e組件 Sentinel 的規(guī)則持久化的文章,敬請期待吧!文章來源:http://www.zghlxwxcb.cn/news/detail-858737.html
后續(xù)的文章,我們將繼續(xù)完善我們的微服務(wù)系統(tǒng),集成更多的Alibaba組件。想要了解更多JAVA后端知識,請點擊文末名片與我交流吧。留下您的一鍵三連,讓我們在這個寒冷的東西互相溫暖吧!文章來源地址http://www.zghlxwxcb.cn/news/detail-858737.html
到了這里,關(guān)于【Spring Cloud】服務(wù)容錯中間件Sentinel進(jìn)階——五大規(guī)則的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!