asp.net建網(wǎng)站2023年8月疫情恢復(fù)
sentinel介紹
Sentinel的官方標題是:分布式系統(tǒng)的流量防衛(wèi)兵。從名字上來看,很容易就能猜到它是用來作服務(wù)穩(wěn)定性保障的。對于服務(wù)穩(wěn)定性保障組件,如果熟悉Spring Cloud的用戶,第一反應(yīng)應(yīng)該就是Hystrix。但是比較可惜的是Netflix已經(jīng)宣布對Hystrix停止更新。那么,在未來我們還有什么更好的選擇呢?除了Spring Cloud官方推薦的resilience4j之外,目前Spring Cloud Alibaba下整合的Sentinel也是用戶可以重點考察和選型的目標。
如果某一個服務(wù)不可用或者宕機了,就會出現(xiàn)線程池里所有線程都因等待響應(yīng)而被阻塞, 從而造成服務(wù)雪崩。解決雪崩問題的常見方式,也是微服務(wù)常見的治理策略:
- 服務(wù)超時:設(shè)定超時時間,請求超過一定的時間沒有響應(yīng)就返回錯誤信息,不會無休止等待。
- 資源隔離:將系統(tǒng)按照一定的規(guī)則劃分為若干個服務(wù)模塊,各個模塊之間相對獨立,無強依賴。常見的隔離方式有:線程池隔離和信號量隔離。
- 流量控制:限制業(yè)務(wù)訪問的QPS,避免服務(wù)因流量的突增而故障。
- 服務(wù)熔斷:有斷路器統(tǒng)計業(yè)務(wù)執(zhí)行的異常比例,如果超出閾值則會熔斷業(yè)務(wù),攔截訪問該業(yè)務(wù)的一切請求,直到該業(yè)務(wù)錯誤率降到閾值以下。
- 服務(wù)降級:當(dāng)某個服務(wù)熔斷之后,服務(wù)將不再被調(diào)用,此時客戶端可以自己準備一個本地的fallback(回退)回調(diào),返回一個缺省值。例如,在商品詳情頁一般都會展示商品的介紹信息,一旦商品詳情頁系統(tǒng)出現(xiàn)故障無法調(diào)用時,會直接獲取緩存中的商品介紹信息返回給前端頁面。
Sentinel 基本概念:
資源
資源是 Sentinel 的關(guān)鍵概念。它可以是 Java 應(yīng)用程序中的任何內(nèi)容,例如,由應(yīng)用程序提供的服務(wù),或由應(yīng)用程序調(diào)用的其它應(yīng)用提供的服務(wù),甚至可以是一段代碼。在接下來的文檔中,我們都會用資源來描述代碼塊。
只要通過 Sentinel API 定義的代碼,就是資源,能夠被 Sentinel 保護起來。大部分情況下,可以使用方法簽名,URL,甚至服務(wù)名稱作為資源名來標示資源。
規(guī)則
圍繞資源的實時狀態(tài)設(shè)定的規(guī)則,可以包括流量控制規(guī)則、熔斷降級規(guī)則以及系統(tǒng)保護規(guī)則。所有規(guī)則可以動態(tài)實時調(diào)整。
流量控制
Sentinel 作為一個調(diào)配器,可以根據(jù)需要把隨機的請求調(diào)整成合適的形狀。
流量控制有以下幾個角度:
- 資源的調(diào)用關(guān)系,例如資源的調(diào)用鏈路,資源和資源之間的關(guān)系;
- 運行指標,例如 QPS、線程池、系統(tǒng)負載等;
- 控制的效果,例如直接限流、冷啟動、排隊等。
熔斷降級
-
通過并發(fā)線程數(shù)進行限制
Sentinel 通過限制資源并發(fā)線程的數(shù)量,來減少不穩(wěn)定資源對其它資源的影響。這樣不但沒有線程切換的損耗,也不需要您預(yù)先分配線程池的大小。當(dāng)某個資源出現(xiàn)不穩(wěn)定的情況下,例如響應(yīng)時間變長,對資源的直接影響就是會造成線程數(shù)的逐步堆積。當(dāng)線程數(shù)在特定資源上堆積到一定的數(shù)量之后,對該資源的新請求就會被拒絕。堆積的線程完成任務(wù)后才開始繼續(xù)接收請求。 -
通過響應(yīng)時間對資源進行降級
除了對并發(fā)線程數(shù)進行控制以外,Sentinel 還可以通過響應(yīng)時間來快速降級不穩(wěn)定的資源。當(dāng)依賴的資源出現(xiàn)響應(yīng)時間過長后,所有對該資源的訪問都會被直接拒絕,直到過了指定的時間窗口之后才重新恢復(fù)。
Sentinel 的主要工作機制如下:
- 對主流框架提供適配或者顯示的 API,來定義需要保護的資源,并提供設(shè)施對資源進行實時統(tǒng)計和調(diào)用鏈路分析。
- 根據(jù)預(yù)設(shè)的規(guī)則,結(jié)合對資源的實時統(tǒng)計信息,對流量進行控制。同時,Sentinel 提供開放的接口,方便您定義及改變規(guī)則。
- Sentinel 提供實時的監(jiān)控系統(tǒng),方便您快速了解目前系統(tǒng)的狀態(tài)。
Sentiner與hystrix的區(qū)別:
安裝并整合sentinel客戶端
Sentinel的使用分為兩部分:
- sentinel-dashboard:與hystrix-dashboard類似,但是它更為強大一些。除了與hystrix-dashboard一樣提供實時監(jiān)控之外,還提供了流控規(guī)則、熔斷規(guī)則的在線維護等功能。
- 客戶端整合:每個微服務(wù)客戶端都需要整合sentinel的客戶端封裝與配置,才能將監(jiān)控信息上報給dashboard展示以及實時的更改限流或熔斷規(guī)則等。
(1)啟動sentinel-dashboard
下載地址:sentinel-dashboard-1.6.0.jar
其他版本:Sentinel/releases
通過命令啟動:
java -jar sentinel-dashboard-1.6.0.jar
默認情況下,sentinel-dashboard以8080端口啟動。由于sentinel-dashboard是一個標準的spring boot應(yīng)用,所以如果要自定義端口號等內(nèi)容的話,可以通過在啟動命令中增加參數(shù)來調(diào)整,比如:
java -jar sentinel-dashboard-1.6.0.jar --server.port=8888
所以可以通過訪問:localhost:8888 來驗證是否已經(jīng)啟動成功,如果一切順利的話,可以看到登錄頁面(默認用戶名和密碼都是sentinel)。
(2)整合Sentinel
依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
配置:
spring.application.name=alibaba-sentinel-rate-limiting
server.port=8002# sentinel dashboard
spring.cloud.sentinel.transport.dashboard=localhost:8888
主類:
package com.example.demospringboot;import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@SpringBootApplication
@Slf4j
public class DemospringbootApplication {public static void main(String[] args) {SpringApplication.run(DemospringbootApplication.class, args);}@Slf4j@RestControllerstatic class TestController {@GetMapping("/hello")public String hello() {return "Hello World";}}
}
啟動應(yīng)用,并執(zhí)行curl命令:
此時,在上面啟動的Sentinel Dashboard中就可以當(dāng)前我們啟動的alibaba-sentinel-rate-limiting這個服務(wù)以及接口調(diào)用的實時監(jiān)控了。具體如下圖所示:
流控限流
配置接口限流規(guī)則
在完成了上面的兩節(jié)之后,我們在alibaba-sentinel-rate-limiting服務(wù)下,點擊簇點鏈路菜單,可以看到如下界面:
通過點擊流控按鈕,來為該接口設(shè)置限流規(guī)則,比如:
流控模式:
- 直接:統(tǒng)計當(dāng)前資源的請求,觸發(fā)閾值時對當(dāng)前資源直接限流,也是默認模式。
- 關(guān)聯(lián):統(tǒng)計與當(dāng)前相關(guān)的另一個資源,觸發(fā)閾值時,對當(dāng)前資源限流。
- 鏈路:統(tǒng)計從指定鏈路訪問到本資源的請求,觸發(fā)閾值時,對指定鏈路限流,即只限流從指定接口進來的
流控效果:
-
快速失效:達到閾值后,新的請求會被立即拒絕并拋出FlowException異常。是默認的處理方式。
-
warm up:預(yù)熱模式,對超出閾值的請求同樣是拒絕,并拋出異常。但這種模式閾值會動態(tài)變化,從一個較小的值逐漸增加到最大值。請求閾值初始值是 threshold/coldFactor(默認值是3),持續(xù)指定時長后,逐漸提高到threshould值。例如,我設(shè)置QPS的threshold的為10,預(yù)熱時間為5秒,那么初始閾值就是10/3,也就是3,然后在5秒后逐漸增長到10.
-
排隊等待:讓所有請求按照先后次序進入一個隊列中排隊執(zhí)行,兩個請求的間隔不能小于指定時長。例如:QPS = 5,意味著每200ms處理一個隊列中的請求;timeout =2000,意味著預(yù)期等待超過2000ms的請求會被拒絕并拋出異常。
參考案例
點擊新增按鈕之后,可以看到如下界面:
其實就是左側(cè)菜單中流控規(guī)則的界面,這里可以看到當(dāng)前設(shè)置的所有限流策略。在完成了上面所有內(nèi)容之后,我們可以嘗試一下快速的調(diào)用這個接口,看看是否會觸發(fā)限流控制,比如:
可以看到,快速的調(diào)用/hello接口之后,調(diào)用被限流了。
在 sentinel-dashboard界面,也可以看到對應(yīng)統(tǒng)計:
Sentinel使用Nacos存儲規(guī)則
Dashboard中設(shè)置的限流規(guī)則在應(yīng)用重啟之后就丟失了。Sentinel自身就支持了多種不同的數(shù)據(jù)源來持久化規(guī)則配置,目前包括以下幾種方式:
- 文件配置
- Nacos配置
- ZooKeeper配置
- Apollo配置
本文我們使用Spring Cloud Alibaba的中整合的配置中心Nacos存儲限流規(guī)則。前置條件,啟動Nacos,詳見 Spring Cloud Alibaba實踐 --Nacos
默認配置下啟動后,它們的訪問地址為:
- Nacos:http://localhost:8848/nacos
- Sentinel Dashboard:http://localhost:8080/
(1)在Spring Cloud應(yīng)用的pom.xml中引入Spring Cloud Alibaba的Sentinel模塊和Nacos存儲擴展:
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><version>1.5.2</version></dependency>
(2)在Spring Cloud應(yīng)用中添加配置信息
spring.application.name=alibaba-sentinel-rate-limiting
server.port=8002
# sentinel dashboard
spring.cloud.sentinel.transport.dashboard=localhost:8888# ds-sentinel-nacos-datasource
spring.cloud.sentinel.datasource.ds.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds.nacos.dataId=${spring.application.name}-sentinel
spring.cloud.sentinel.datasource.ds.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds.nacos.rule-type=flow
(3)Nacos中創(chuàng)建限流規(guī)則的配置
注:Data ID、Group就是上面第二步中配置的內(nèi)容。配置格式選擇JSON,并在配置內(nèi)容中填入下面的內(nèi)容:
[{"resource": "/hello","limitApp": "default","grade": 1,"count": 5,"strategy": 0,"controlBehavior": 0,"clusterMode": false}
]
配置規(guī)則是一個數(shù)組類型,數(shù)組中的每個對象是針對每一個保護資源的配置對象,每個對象中的屬性解釋如下:
- resource:資源名,即限流規(guī)則的作用對象
- limitApp:流控針對的調(diào)用來源,若為 default 則不區(qū)分調(diào)用來源
- grade:限流閾值類型(QPS 或并發(fā)線程數(shù));0代表根據(jù)并發(fā)數(shù)量來限流,1代表根據(jù)QPS來進行流量控制
- count:限流閾值
- strategy:調(diào)用關(guān)系限流策略
- controlBehavior:流量控制效果(直接拒絕、Warm Up、勻速排隊)
- clusterMode:是否為集群模式
(4)啟動應(yīng)用,curl localhost:8002/hello
測試
如果一些順利,可以看到類似下面的日志,代表已經(jīng)成功從Nacos加載了一條限流規(guī)則:
2023-12-09 19:00:04.887 INFO 13800 --- [ main] o.s.c.a.s.c.SentinelDataSourceHandler : [Sentinel Starter] DataSource ds-sentinel-nacos-datasource load 1 FlowRule
在Sentinel Dashboard中就可以看到當(dāng)前我們啟動的alibaba-sentinel-rate-limiting
服務(wù)。點擊左側(cè)菜單中的流控規(guī)則,可以看到已經(jīng)存在一條記錄了,具體如下:
注:在完成了上面的整合之后,對于接口流控規(guī)則的修改就存在兩個地方了:Sentinel控制臺、Nacos控制臺。
這個時候,需要注意當(dāng)前版本的Sentinel控制臺不具備同步修改Nacos配置的能力,而Nacos由于可以通過在客戶端中使用Listener來實現(xiàn)自動更新。所以,在整合了Nacos做規(guī)則存儲之后,需要知道在下面兩個地方修改存在不同的效果:
- Sentinel控制臺中修改規(guī)則:僅存在于服務(wù)的內(nèi)存中,不會修改Nacos中的配置值,重啟后恢復(fù)原來的值。
- Nacos控制臺中修改規(guī)則:服務(wù)的內(nèi)存中規(guī)則會更新,Nacos中持久化規(guī)則也會更新,重啟后依然保持。
@SentinelResource對某個方法資源調(diào)用限流
在實際應(yīng)用過程中,我們可能需要限流的層面不僅限于接口,而是對于某個方法的調(diào)用限流,對于某個外部資源的調(diào)用限流等都希望做到控制。這個時候我們就不得不手工定義需要限流的資源點,并配置相關(guān)的限流策略等內(nèi)容了。使用@SentinelResource
注解可以靈活的定義控制資源以及如何配置控制策略。
主啟動類:
在應(yīng)用主類中增加注解支持的配置Bean
package com.example.demospringboot;import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@SpringBootApplication
@Slf4j
public class DemospringbootApplication {public static void main(String[] args) {SpringApplication.run(DemospringbootApplication.class, args);}// 注解支持的配置Bean@Beanpublic SentinelResourceAspect sentinelResourceAspect() {return new SentinelResourceAspect();}}
Service:
在需要通過Sentinel來控制流量的地方使用@SentinelResource
注解,自定義資源點;
并通過@SentinelResource
注解的blockHandler
屬性實現(xiàn)限流的異常處理
package com.example.demospringboot;import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;@Slf4j
@Service
public class TestService {@SentinelResource(value = "doSomeThing", blockHandler = "exceptionHandler")public void doSomeThing(String str) {log.info(str);}// 限流與阻塞處理public void exceptionHandler(String str, BlockException ex) {log.error( "blockHandler-> " + str, ex);}
}
Controller:
在Web層調(diào)用這個被保護的方法
package com.example.demospringboot;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.Date;@RestController
public class TestController {@Autowiredprivate TestService testService;@GetMapping("/hello")public String hello() {testService.doSomeThing("hello " + new Date());return "Hello World";}
}
啟動測試應(yīng)用,啟動Sentinel-Dashboard。 curl localhost:8002/hello
發(fā)一個請求到/hello接口上,使得Sentinel-Dashboard上可以看到如下圖所示的幾個控制點:
可以看到,多了一個doSomeThing資源點??梢酝ㄟ^界面為這個資源點設(shè)置限流規(guī)則,比如將其QPS設(shè)置為2。由于/hello資源不設(shè)置限流規(guī)則,所以只要請求/hello接口,就可以直接模擬調(diào)用doSomeThing資源,來觀察限流規(guī)則是否生效。只要QPS超過2,那么就會出現(xiàn)如下的錯誤返回,代表限流策略生效了。
2023-12-09 12:45:58.015 INFO 11368 --- [nio-8002-exec-7] com.example.demospringboot.TestService : hello Sat Dec 09 12:45:58 CST 2023
2023-12-09 12:45:58.378 TRACE 11368 --- [nio-8002-exec-8] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demospringboot.TestController#hello()
2023-12-09 12:45:58.401 ERROR 11368 --- [nio-8002-exec-8] com.example.demospringboot.TestService : blockHandler-> hello Sat Dec 09 12:45:58 CST 2023com.alibaba.csp.sentinel.slots.block.flow.FlowException: null
熔斷降級
@SentinelResource
注解除了可以用來做限流控制之外,還能實現(xiàn)與Hystrix類似的熔斷降級策略。
這里只需對TestService類改造,讓doSomeThing方法持續(xù)拋出異常,觸發(fā)fallback屬性指定的具體方法名進行降級處理。
package com.example.demospringboot;import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;@Slf4j
@Service
public class TestService {@SentinelResource(value = "doSomeThing", fallback = "fallbackHandler")public void doSomeThing(String str) {log.info(str);throw new RuntimeException("發(fā)生異常");}public void fallbackHandler(String str) {log.error("fallbackHandler-> " + str);}
}
在Sentinel-Dashboard名為doSomeThing的資源點上,點擊”降級“按鈕,為該資源設(shè)置降級規(guī)則。這里使用異常比例策略,比例設(shè)置為0.1(即:10%的異常率),時間窗口設(shè)置為2(秒):
驗證熔斷降級,根據(jù)上面的降級策略配置,當(dāng)doSomeThing方法的調(diào)用QPS >= 5時,如果異常率超過10%,那么后續(xù)2秒內(nèi)的調(diào)用將直接觸發(fā)熔斷降級:
2023-12-09 13:30:21.332 TRACE 4248 --- [io-8002-exec-10] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demospringboot.TestController#hello()
2023-12-09 13:30:21.333 ERROR 4248 --- [io-8002-exec-10] com.example.demospringboot.TestService : fallbackHandler-> hello Sat Dec 09 13:30:21 CST 2023
2023-12-09 13:30:21.678 TRACE 4248 --- [nio-8002-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demospringboot.TestController#hello()
2023-12-09 13:30:21.678 ERROR 4248 --- [nio-8002-exec-2] com.example.demospringboot.TestService : fallbackHandler-> hello Sat Dec 09 13:30:21 CST 2023
參考:
https://www.didispace.com/spring-cloud/spring-cloud-alibaba-sentinel-2-5.html
https://zhuanlan.zhihu.com/p/565074363?utm_id=0