SpringCloud-Alibaba-Sentinel
1、概述
Sentinel,中文翻譯為哨兵,是為微服務提供流量控制、熔斷降級的功能,它和Hystrix提供的功能一樣,可以有
效的解決微服務呼叫產生的“雪崩”效應,為微服務系統提供了穩定性的解決方案。隨著Hytrxi進入了維護期,不
再提供新功能,Sentinel是一個不錯的替代方案。通常情況,Hystrix採用執行緒池對服務的呼叫進行隔離,
Sentinel才用了使用者執行緒對介面進行隔離,二者相比,Hystrxi是服務級別的隔離,Sentinel提供了介面級別的
隔離,Sentinel隔離級別更加精細,另外Sentinel直接使用使用者執行緒進行限制,相比Hystrix的執行緒池隔離,減
少了執行緒切換的開銷。另外Sentinel的DashBoard提供了線上更改限流規則的配置,也更加的優化。
從官方文件的介紹,Sentinel 具有以下特徵:
豐富的應用場景: Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控
制在系統容量可以承受的範圍)、訊息削峰填谷、實時熔斷下游不可用應用等。
完備的實時監控: Sentinel 同時提供實時的監控功能。您可以在控制檯中看到接入應用的單臺機器秒級資料,
甚至 500 臺以下規模的叢集的彙總執行情況。
廣泛的開源生態: Sentinel 提供開箱即用的與其它開源框架/庫的整合模組,例如與 Spring Cloud、Dubbo、
gRPC 的整合。您只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。
完善的 SPI 擴充套件點: Sentinel 提供簡單易用、完善的 SPI 擴充套件點。您可以通過實現擴充套件點,快速的定製邏
輯。例如定製規則管理、適配資料來源等。
2、基本使用
1、新建專案,新增pom依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2、編寫配置檔案
server:
port: 9003
spring:
application:
name: cloud-sentinel-9003
cloud:
sentinel:
transport:
dashboard: localhost:8080
port: 8719
nacos:
discovery:
server-addr: xxx.xxx.xxx.xxx:8848
management:
endpoints:
web:
exposure:
include: "*"
***在這裡,sentinel執行在本地,嘗試使用docker,但是獲取不到服務的詳細鏈路,原因是sentinel主動向服務拉取資訊,阿里雲伺服器無法訪問到本機,因此失敗。
3、啟動專案後,訪問兩個埠/testA和/testB,重新整理sentinel即可看到
3、流控規則
3.1、QPS直接快速失敗
每秒如果訪問超過五次,就會丟擲異常
3.2執行緒數直接失敗
當呼叫介面的執行緒數超過閥值時,進行限流,
3.3關聯
當每秒訪問HelloA超過一次後,將對HelloB進行限流,比如支付服務滿了以後對訂單服務進行限流,防止連帶。
3.4鏈路
鏈路流控模式指的是,當從某個介面過來的資源達到限流條件時,開啟限流;它的功能有點類似於針對 來源配置項,區別在於:針對來源是針對上級微服務,而鏈路流控是針對上級介面,也就是說它的粒度 更細;
建立一個service,新增註解
@SentinelResource(value = "getOrder",blockHandler = "handlerException")
在流量監控處設定:
訪問HelloB一秒超過一次就會限流。
注意:sentinel1.7以後需要設定spring.cloud.sentinel.web-context-unify=false即可
詳情:https://github.com/alibaba/Sentinel/issues/1213
3.5預熱
在預熱的5秒內每秒的QPS最多是10/3=3次,如果超過3次就會限流,5秒以後閥值恢復到10
3.6排隊等待
HelloA一秒處理一次請求,超過就排隊等待,等待的超時時間為2000毫秒,超過等待毫秒就會限流。
4、服務降級
1、RT(平均響應時間,秒級)
平均響應時間超出閥值且在時間視窗內通過的請求次數>=5次,兩個條件同時滿足後觸發降級
視窗期過後關閉斷路器,RT最大4900
2、異常比例(秒級)
QPS>=5且異常比例(秒級統計)超過閥值時,觸發降級,時間視窗期結束後,關閉降級
3、異常數(分鐘級)
異常數(分鐘統計)超過閥值時,觸發降級;時間視窗結束後,關閉降級
4.1RT
一秒持續進入>=5次請求,平均響應時間如果大於200毫秒,則開啟斷路器,在時間視窗期2秒後關閉降級
4.2異常比例
一秒持續進入>=5次請求,如果每秒處理的請求異常比例大於百分之20,則開啟斷路器,在時間視窗期2秒後關閉降級
4.3異常數
異常數 (DEGRADE_GRADE_EXCEPTION_COUNT):當資源近 1 分鐘的異常數目超過閾值之後會進行熔斷。注意由於統計時間視窗是分鐘級別的,若 timeWindow 小於 60s,則結束熔斷狀態後仍可能再進入熔斷狀態。
異常數是按分鐘來統計的,所以時間視窗必須大於等於60s
在61秒內如果超過異常5次,則開啟斷路器,在61秒以後關閉斷路器
5、熱點Key
@GetMapping("/HelloE")
@SentinelResource(value = "helloE",blockHandler = "hostKeyHandler")
public String getE(@RequestParam(value = "a",required = false)String a
,@RequestParam(value = "b",required = false)String b)
{
return "------HelloE------";
}
public String hostKeyHandler(String a, String b, BlockException ex){
return ex.getRule().getResource()+",服務正忙,稍後再試~";
}
對HelloE請求,攜帶的第一個引數,如果QPS超過1就會進行降級,返回自定義資訊
新增引數例外項,設定如果第一個引數是2,則閥值是10,第一個引數不是2,則閥值是1
6、系統規則
Sentinel 系統自適應限流從整體維度對應用入口流量進行控制,結合應用的 Load、CPU 使用率、總體平均
RT、入口 QPS 和併發執行緒數等幾個維度的監控指標,通過自適應的流控策略,讓系統的入口流量和系統的負載達
到一個平衡,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性
Field | 說明 | 預設值 |
---|---|---|
highestSystemLoad | load1 觸發值,用於觸發自適應控制階段 | -1 (不生效) |
avgRt | 所有入口流量的平均響應時間 | -1 (不生效) |
maxThread | 入口流量的最大併發數 | -1 (不生效) |
qps | 所有入口資源的 QPS | -1 (不生效) |
highestCpuUsage | 當前系統的 CPU 使用率(0.0-1.0) | -1 (不生效) |
當任何請求訪問系統QPS超過1,都會開啟斷路器
7、SentinelResource
7.1根據資源名進行限流
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
public CommonResult byResource()
{
return new CommonResult(200,"按資源名稱限流測試OK",new Payment(2020L,"serial001"));
}
public CommonResult handleException(BlockException exception)
{
return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服務不可用");
}
根據SentinelResource註解的Value進行限流
7.2根據URL進行限流
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl()
{
return new CommonResult(200,"按url限流測試OK",new Payment(2020L,"serial002"));
}
根據Url進行限流,但使用的是Sentinel自己的提示資訊,不友好
7.3自定義限流處理邏輯
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class,
blockHandler = "handlerException2")
public CommonResult customerBlockHandler()
{
return new CommonResult(200,"按客戶自定義",new Payment(2020L,"serial003"));
}
public class CustomerBlockHandler {
public static CommonResult handlerException2(BlockException exception)
{
return new CommonResult(4444,"按客戶自定義,global handlerException----1");
}
將處理降級提示資訊與業務邏輯分離,使用blockHandlerClass和blockHandler指明類名和方法名即可
SentinelResouce無法對private方法進行監控
8、服務熔斷
1、新建兩個服務提供者和一個消費者,將其註冊到nacos
2、分別通過Ribbion和OpenFeign呼叫服務
pom依賴
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 後續做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
8.1@SentinelResource只配置value
對value進行流控後,會報500錯誤
8.2@SentinelResource只配置fallback
只對系統異常進行降級處理,不會對sentinel設定的流控和降級進行兜底
8.3@SentinelResource只配置blockhandler
只對sentinel設定的流控和降級進行兜底處理,不會對系統異常進行降級處理
8.3@SentinelResource配置fallback和blockhandler
會對異常和sentinel設定的流控和降級進行不同的處理
8.4@SentinelResource中配置exceptionsToIgnore
會忽略設定的異常,不會進行兜底處理,可以自己寫全域性異常處理類進行處理
8.5OpenFeign通過在介面上新增註解,將降級邏輯和業務邏輯分離
新建類實現介面,在過載的方法裡進行降級處理,在介面的註解加上fallback = xxx.class即可
注意需要將實現類加上@Component否則會報錯
@RestController
@Slf4j
public class OrderController {
@Resource
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
@Value("${service.url.paymentUrl}")
private String paymentUrl;
// ----------OpenFeign-----------
@GetMapping("/consumer/getByFeign/{id}")
public CommonResult getPayByFeign(@PathVariable("id") Long id){
log.info("**************OpenFeign");
CommonResult<Payment> result = orderService.paymentSQL(id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法引數異常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,該ID沒有對應記錄,空指標異常");
}
return result;
}
// ----------Ribbion-----------
@GetMapping("/consumer/getByRibbion/{id}")
//@SentinelResource(value = "fallback")
//@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只負責業務異常
//@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只負責sentinel控制檯配置違規
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
exceptionsToIgnore = {IllegalArgumentException.class})
public CommonResult getByRibbion(@PathVariable("id") Long id){
log.info("**************Ribbion");
CommonResult result = restTemplate.getForObject(paymentUrl + "/payment/" + id, CommonResult.class);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法引數異常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,該ID沒有對應記錄,空指標異常");
}
return result;
}
//本例是fallback
public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底異常handlerFallback,exception內容 "+e.getMessage(),payment);
}
//本例是blockHandler
public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(445,"blockHandler-sentinel限流,無此流水: blockException "+blockException.getMessage(),payment);
}
}
------------------------------------------------------------
@Component
@FeignClient(value = "cloud-sentinel-payment-provider",fallback = OrderSerivceHandler.class)
public interface OrderService {
@GetMapping(value = "/payment/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
-----------------------------------------------------------------
@Component
public class OrderSerivceHandler implements OrderService {
@Override
public CommonResult<Payment> paymentSQL(Long id) {
return new CommonResult<>(44444,"服務降級返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
}
}
9、持久化規則
將限流配置持久化儲存到Nacos裡
1、新增pom依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2、在專案配置檔案裡新增配置如下
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
port: 8719
datasource:
wx1: #名字隨意起
nacos:
server-addr: xxx.xxx.xxx.xxx:8848 #nacos地址
dataId: cloud-alibaba-sentinel-service #配置檔案的dataId
groupId: DEFAULT_GROUP #配置檔案的分組
data-type: json #配置檔案的型別
rule-type: flow #用來定義儲存的規則型別,flow代表限流規則
#degrade代表降級
#system代表系統規則
#authority代表授權
3、在Sentinel新增配置檔案如下圖
4、規則說明
resource:資源名稱
limitApp:來源應用
grade:閥值型別,0代表執行緒數,1代表QPS
count:單機閥值
strategy:流控模式,0代表直接,1代表關聯,2代表鏈路
controlBehavior:流控效果,0代表快速失敗,1代表Warm Up(預熱),2代表排隊等待
clusterMode:是否叢集,false代表沒有,true代表叢集
6、監控資料說明
流控的資料只會儲存5分鐘,如果想持久化,可以後期使用:
1.實現 MetricsRepository 介面;
2.註冊成 Spring Bean 並在相應位置通過 @Qualifier 註解指定對應的 bean name 即可。
10、其他說明
1、sentinel資料持久化有五種方式
1.File 2.Redis 3.Nacos 4.Zookeeper 5.apollo
2、sentinel預設監控資料只儲存五分鐘,如果想要對監控的資料情況持久化有以下兩種方式
1.自行擴充套件實現 MetricsRepository 介面;
2.註冊成 Spring Bean 並在相應位置通過 @Qualifier 註解指定對應的 bean name 即可。