Sentinel的控制檯啟動後,控制檯頁面的內容資料都是空的,接下來我們來逐步操作演示結合控制檯的使用,在上一節也已說明整合SpringCloud Alibaba第一步先加入spring-cloud-starter-alibaba-sentinel啟動器依賴
組態檔新增引數,dashboard即為控制檯的埠,我們是本地啟動使用8858埠
spring:
cloud:
sentinel:
enabled: true
transport:
dashboard: localhost:8858
port: 8719
訂單新增控制器中有一個訂單新增的介面
啟動訂單微服務程式,存取訂單控制器的新增訂單介面,http://localhost:4070/add-order
然後再檢視sentinel的控制檯首頁-簇點鏈路,這時已經有/add-order這個資源名的資料
多次存取後在實時監控也可以看到相應資源的實時QPS統計資料
流量控制:Sentinel的流控的原理主要是監控應用流量或者說是資源的QPS或者並行執行緒數,當達到指定的閾值後對流量進行控制,以避免被瞬時的流量洪峰沖垮,從而保障應用的高可用性
QPS:每秒請求數,即在不斷向伺服器傳送請求的情況下,伺服器每秒能夠處理的請求數量。
並行執行緒數:指的是施壓機施加的同時請求的執行緒數量。
流量規則的定義的重要屬性:
Field | 說明 | 預設值 |
---|---|---|
resource | 資源名,資源名是限流規則的作用物件 | |
count | 限流閾值 | |
grade | 限流閾值型別,QPS 或執行緒數模式 | QPS 模式 |
limitApp | 流控針對的呼叫來源 | default ,代表不區分呼叫來源 |
strategy | 呼叫關係限流策略:直接、鏈路、關聯 | 根據資源本身(直接) |
controlBehavior | 流控效果(直接拒絕 / 排隊等待 / 慢啟動模式),不支援按呼叫關係限流 | 直接拒絕 |
FlowSlot
會根據預設的規則,結合前面 NodeSelectorSlot
、ClusterNodeBuilderSlot
、StatistcSlot
統計出來的實時資訊進行流量控制。同一個資源可以對應多條限流規則。FlowSlot
會對該資源的所有限流規則依次遍歷,直到有規則觸發限流或者所有規則遍歷完畢。一條限流規則主要由下面幾個因素組成,可以組合這些元素來實現不同的限流效果:
resource
:資源名,即限流規則的作用物件count
: 限流閾值grade
: 限流閾值型別,QPS 或執行緒數strategy
: 根據呼叫關係選擇策略通過控制檯首頁-簇點鏈路,在相應資源的記錄右邊點選流控按鈕並設定相應的流控規則。
快速存取訂單控制器的新增訂單介面,當前設定每秒超過2次就會被流控
同樣我們也可以和前面核心庫範例一樣自定流控提示
設定訂單查詢介面的流控規則
通過控制檯首頁-流控規則檢視當前的流控規則,每次重啟微服務模組其設定規則在記憶體中丟失了,也即是前面設定新增訂單的流控規則也沒有了,如需要則需重新設定
快速存取訂單控制器的查詢訂單介面
執行緒數限流用於保護業務執行緒數不被耗盡。例如,當應用所依賴的下游應用由於某種原因導致服務不穩定、響應延遲增加,對於呼叫者來說,意味著吞吐量下降和更多的執行緒數佔用,極端情況下甚至導致執行緒池耗盡。為應對高執行緒佔用的情況,業內有使用隔離的方案,比如通過不同業務邏輯使用不同執行緒池來隔離業務自身之間的資源爭搶(執行緒池隔離),或者使用號誌來控制同時請求的個數(號誌隔離)。這種隔離方案雖然能夠控制執行緒數量,但無法控制請求排隊時間。當請求過多時排隊也是無益的,直接拒絕能夠迅速降低系統壓力。Sentinel執行緒數限流不負責建立和管理執行緒池,而是簡單統計當前請求上下文的執行緒個數,如果超出閾值,新的請求會被立即拒絕。我們在查詢訂單介面中增加休眠來演示效果
@RequestMapping("/query-order")
@SentinelResource(value = "query-order",blockHandler = "querydOrderBlockHandler")
public String querydOrder() {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "訂單查詢成功";
}
設定並行執行緒數的閾值為1
存取第一個頁面的緊跟著再開啟另外一個頁面繼續存取
第二個頁面就顯示被流控了
預設的流量控制方式,當QPS超過任意規則的閾值後,新的請求就會被立即拒絕,拒絕方式為丟擲。前面的例子都是使用直接拒絕,這裡就不再說明。
具有關係的資源流量控制,當兩個資源之間具有資源爭搶或者依賴關係的時候,這兩個資源便具有了關聯。比如對資料庫同一個欄位的讀操作和寫操作存在爭搶,讀的速度過高會影響寫得速度,寫的速度過高會影響讀的速度。如果放任讀寫操作爭搶資源,則爭搶本身帶來的開銷會降低整體的吞吐量。可使用關聯限流來避免具有關聯關係的資源之間過度的爭搶,舉例來說,read_db
和 write_db
這兩個資源分別代表資料庫讀寫,我們可以給 read_db
設定限流規則來達到寫優先的目的:設定 FlowRule.strategy
為 RuleConstant.RELATE
同時設定 FlowRule.ref_identity
為 write_db
。這樣當寫庫操作過於頻繁時,讀資料的請求會被限流。我們先把前面的querydOrder休眠去掉,設定流控模式為關聯,關聯資源為新增訂單介面
先通過ApiFox設定迴圈存取,也可以通過其他工具如jmeter等,ApiFox也可以前面的文章也有講解去使用
啟動持續的存取訂單查詢介面
存取訂單新增已經顯示被流控了,暫停ApiFox後存取則正常。
NodeSelectorSlot
中記錄了資源之間的呼叫鏈路,這些資源通過呼叫關係,相互之間構成一棵呼叫樹。這棵樹的根節點是一個名字為 machine-root
的虛擬節點,呼叫鏈的入口都是這個虛節點的子節點。在訂單實現類中增加測試方法
@Override
@SentinelResource(value = "getOrder")
public String getOrder() {
return "測試查詢訂單";
}
訂單控制器增加兩個介面方法,都呼叫到了getOrder
@RequestMapping("/test1")
public String test1(){
return orderService.getOrder();
}
@RequestMapping("/test2")
public String test2(){
return orderService.getOrder();
}
組態檔增加下面引數
spring:
cloud:
sentinel:
web-context-unify: false
啟動程式為getOrder資源設定鏈路流控模式,入口資源為/test2
快速存取http://localhost:4070/test2 ,出現被流控了,而快速存取http://localhost:4070/test1則都是正常請求
預設的流量控制方式,當QPS超過任意規則的閾值後,新的請求就會被立即拒絕,拒絕方式為丟擲FlowException
。前面大部分例子都是使用了快速失敗演示。
RuleConstant.CONTROL_BEHAVIOR_WARM_UP
)方式,即預熱/冷啟動方式。當系統長期處於低水位的情況下,當流量突然增加時,直接把系統拉昇到高水位可能瞬間把系統壓垮。通過"冷啟動",讓通過的流量緩慢增加,讓伺服器一點一點處理,再慢慢加量,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮。設定相應流控規則,流控效果選擇Warm Up
有激增的流量持續請求訂單查詢介面http://localhost:4070/query-order,檢視實時監控資料,有一個慢慢預熱的過程
除了流量控制以外,對呼叫鏈路中不穩定的資源進行熔斷降級也是保障高可用的重要措施之一。一個服務常常會呼叫別的模組,可能是另外的一個遠端服務、資料庫,或者第三方 API 等。例如,支付的時候,可能需要遠端呼叫銀聯提供的 API;查詢某個商品的價格,可能需要進行資料庫查詢。然而,這個被依賴服務的穩定性是不能保證的。如果依賴的服務出現了不穩定的情況,請求的響應時間變長,那麼呼叫服務的方法的響應時間也會變長,執行緒會產生堆積,最終可能耗盡業務自身的執行緒池,服務本身也變得不可用。
現代微服務架構都是分散式的,由非常多的服務組成。不同服務之間相互呼叫,組成複雜的呼叫鏈路。以上的問題在鏈路呼叫中會產生放大的效果。複雜鏈路上的某一環不穩定,就可能會層層級聯,最終導致整個鏈路都不可用。因此我們需要對不穩定的弱依賴服務呼叫進行熔斷降級,暫時切斷不穩定呼叫,避免區域性不穩定因素導致整體的雪崩。熔斷降級作為保護自身的手段,通常在使用者端(呼叫端)進行設定。
Sentinel 提供以下幾種熔斷策略:
SLOW_REQUEST_RATIO
):選擇以慢呼叫比例作為閾值,需要設定允許的慢呼叫 RT(即最大的響應時間),請求的響應時間大於該值則統計為慢呼叫。當單位統計時長(statIntervalMs
)內請求數目大於設定的最小請求數目,並且慢呼叫的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求響應時間小於設定的慢呼叫 RT 則結束熔斷,若大於設定的慢呼叫 RT 則會再次被熔斷。ERROR_RATIO
):當單位統計時長(statIntervalMs
)內請求數目大於設定的最小請求數目,並且異常的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。異常比率的閾值範圍是 [0.0, 1.0]
,代表 0% - 100%。ERROR_COUNT
):當單位統計時長內的異常數目超過閾值之後會自動進行熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。在新增訂單介面休眠兩秒來演示
在簇點鏈路點選熔斷按鈕進行設定
先用用apifox多個執行緒呼叫http://localhost:4070/add-order
再存取http://localhost:4070/add-order,顯示當前請求被流量限制了
在新增訂單介面中新增異常程式碼
設定規則
同樣先用apifox多個執行緒呼叫http://localhost:4070/add-order,然後再存取http://localhost:4070/add-order,顯示當前請求被流量限制了
同樣異常數的設定也是如此
熱點引數規則(ParamFlowRule
)類似於前面列出流量控制規則(FlowRule
),詳細可以查閱官網
建立測試方法,帶路徑變數引數
@RequestMapping("/get/{id}")
public String getByOrderId(@PathVariable("id") Integer id){
log.info("getByOrderId id={}",id);
return "查詢訂單正常";
}
啟動程式,存取http://localhost:4070/order/get/1
設定熱點規則
點選編輯進入設定高階選項,增加引數例外項,點選新增
存取http://localhost:4070/order/get/2 則可以正常,不受限流閾值2的限制,而連續存取http://localhost:4070/order/get/1 則出現限流
統一例外處理適合對BlockException返回的資訊處理是一樣的,如果不一樣則還是需要使用@SentinelResource
建立統一返回實體類Result
package cn.itxs.ecom.commons.entity;
public class Result<T> {
private Integer code;
private String msg;
private T data;
public Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public Result(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static Result error(Integer code,String msg){
return new Result(code,msg);
}
}
新增ItxsBlockExceptionHandler.java
package cn.itxs.ecom.order.exception;
import cn.itxs.ecom.commons.entity.Result;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Component
@Slf4j
public class ItxsBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
//getRule返回資源、規則的詳細資訊
log.info("BlockExceptionHandler BlockException================"+e.getRule());
Result r = null;
if(e instanceof FlowException){
r = Result.error(400,"哈哈哈,統一處理方法處介面被限流了");
}else if (e instanceof DegradeException){
r = Result.error(401,"哈哈哈,統一處理方法處服務降級了");
}else if (e instanceof ParamFlowException){
r = Result.error(402,"哈哈哈,統一處理方法處熱點引數限流了");
}else if (e instanceof AuthorityException){
r = Result.error(404,"哈哈哈,統一處理方法處理授權規則不通過");
}else if (e instanceof SystemBlockException){
r = Result.error(405,"哈哈哈,統一處理方法處理系統規則不通過");
}
//返回Json資料
response.setStatus(200);
response.setCharacterEncoding("UTF-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
PrintWriter writer=null;
try {
writer=response.getWriter();
writer.write(JSON.toJSONString(r));
writer.flush();
} catch (IOException ioException) {
log.error("異常:{}",ioException);
}finally {
if(writer!=null) {
writer.close();
}
}
}
}
測試的控制器則無需使用@SentinelResource
啟動存取http://localhost:4070/order/add ,然後設定流控規則,再次存取,這是則是統一異常返回結果,設定熔斷降級規則命中也是如此。
系統保護規則是從應用級別的入口流量進行控制,從單臺機器的總體 Load、RT、入口 QPS 和執行緒數四個維度監控應用資料,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性。系統保護規則是應用整體維度的,而不是資源維度的,並且僅對入口流量生效。入口流量指的是進入應用的流量(EntryType.IN
),比如 Web 服務或 Dubbo 伺服器端接收的請求,都屬於入口流量。在系統規則中設定閾值型別CPU ,閾值為0.1
由於我本機CPU一直高於10%的,存取http://localhost:4070/order/add 後會出現系統規則限流了,而調高CPU的閾值如0.8後存取則是正常的。
準備一個庫存微服務,前面我們已經使用,在庫存控制器增加一個測試方法和啟動庫存微服務
@RequestMapping("/deduct-storage")
public String deductStorage(){
int i = 1/0;
return "扣減庫存";
}
訂單微服務中增加一個StorageFeignService的Feign介面宣告
package cn.itxs.ecom.order.service;
import cn.itxs.ecom.commons.service.openfeign.StorageFeignServiceFackBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(value = "ecom-storage-service",fallback = StorageFeignServiceFackBack.class)
public interface StorageFeignService {
@RequestMapping("/deduct-storage")
String deductStorage();
}
建立降級的實現類
package cn.itxs.ecom.order.service;
import cn.itxs.ecom.commons.service.openfeign.StorageFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class StorageFeignServiceFackBack implements StorageFeignService {
@Override
public String deductStorage() {
log.info("進入補償處理的流程----------");
return "進入補償處理的流程----------";
}
}
訂單控制器增加方法
@Autowired
OrderService orderService;
@RequestMapping("/create-order")
public String createOrder(){
return orderService.createOrder();
}
訂單服務介面的實現類呼叫宣告庫存的Feign介面
package cn.itxs.ecom.order.service.impl;
import cn.itxs.ecom.commons.service.OrderService;
import cn.itxs.ecom.commons.service.openfeign.StorageFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private StorageFeignService storageFeignService;
@Override
public String createOrder() {
return storageFeignService.deductStorage();
}
}
啟動組態檔增加啟用feign整合sentinel
feign:
sentinel:
enabled: true
在訂單微服務的啟動類增加@EnableFeignClients開啟
存取http://localhost:4070/order/create-order,觸發庫存服務異常後返回補償流程提示。
DataSource
擴充套件常見的實現方式有:
Sentinel 目前支援以下資料來源擴充套件:
Dashboard中新增的規則資料儲存在記憶體,微服務停掉規則資料就消失,在⽣產環境下不合適。我們可以將Sentinel規則資料持久化到Nacos設定中⼼,讓微服務從Nacos獲取規則資料。
新增依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
Nacos增加設定
啟動組態檔中增加
spring:
application:
name: ecom-order-service
cloud:
sentinel:
enabled: true
transport:
dashboard: localhost:8858
port: 8719
datasource:
# 此處的flow為⾃定義資料來源名
flow: # 流控規則
nacos:
# server-addr: ${spring.cloud.nacos.discovery.server-addr}
server-addr: ${spring.cloud.nacos.server-addr}
namespace: a2b1a5b7-d0bc-48e8-ab65-04695e61db01
data-id: ${spring.application.name}-flow-rules
groupId: order-group
username: itsx
password: itxs123
data-type: json
rule-type: flow # 型別來⾃RuleType類
快速存取http://localhost:4070/order/add,出現被流控的提示
檢視sentinel控制檯流控規則也是我們在Nacos上的流控規則設定
**本人部落格網站 **IT小神 www.itxiaoshen.com