一, Sentinel熔斷降級git
Sentinel除了流量控制之外,對調用鏈路中不穩定的資源進行熔斷降級也是保障高可用的重要措施之一。因爲調用關係的複雜性,若是調用鏈路中的某個資源不穩定,最終會致使請求發生堆積。Sentinel 熔斷降級會在調用鏈路中某個資源出現不穩定狀態時(例如調用超時或異常比例升高),對這個資源的調用進行限制,讓請求快速失敗,避免影響到其它的資源而致使級聯錯誤。當資源被降級後,在接下來的降級時間窗口以內,對該資源的調用都自動熔斷(默認行爲是拋出 DegradeException)。github
Sentinel的熔斷降級和Hystrix對比, 請查看:https://blog.csdn.net/xiongxianze/article/details/87566963 Hystrix和Sentinel技術選型.編程
二, Sentinel以三種方式衡量被訪問的資源是否處理穩定的狀態less
1 平均響應時間 (DEGRADE_GRADE_RT):當資源的平均響應時間超過閾值(DegradeRule 中的 count,以 ms 爲單位)以後,資源進入準降級狀態。接下來若是持續進入 5 個請求,它們的 RT 都持續超過這個閾值,那麼在接下的時間窗口(DegradeRule 中的 timeWindow,以 s 爲單位)以內,對這個方法的調用都會自動地返回(拋出 DegradeException)。在下一個時間窗口到來時, 會接着再放入5個請求, 再重複上面的判斷.ide
2 異常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):當資源的每秒異常總數佔經過量的比值超過閾值(DegradeRule 中的 count)以後,資源進入降級狀態,即在接下的時間窗口(DegradeRule中的 timeWindow,以 s 爲單位)以內,對這個方法的調用都會自動地返回。異常比率的閾值範圍是 [0.0, 1.0],表明 0% - 100%。ui
3 異常數 (DEGRADE_GRADE_EXCEPTION_COUNT):當資源近 1 分鐘的異常數目超過閾值以後會進行熔斷。.net
三, Sentinel以訪問資源的平均響應時間RT做爲降級策略線程
1 初始化降級規則, 設置RT : 200ms, 設置降級規則 : RuleConstant.DEGRADE_GRADE_RT, 設置時間窗口 : 10scode
private static void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<DegradeRule>();
DegradeRule rule = new DegradeRule();
rule.setResource(KEY);
// set threshold rt, 200 ms
rule.setCount(200);
// 設置降級規則RT, 平均響應時間
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
// 設置時間窗口
rule.setTimeWindow(10);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
2 設置資源耗時, 以sleeptime休眠時間模擬資源耗時, 剛開始時sleeptime:600ms, 而設置的RT閾值爲200ms, 因此全部訪問該資源的請求響應時間均超過了這個閾值. blog
private static int sleeptime = 600;
當60s後, 修改sleeptime : 100ms, 模擬訪問資源耗時下降, 這樣全部的請求訪問該資源時, 請求響應時間RT均沒有超過閾值RT=200.
if (seconds == 40) {
System.out.println("===>修改資源耗時時間爲100ms");
sleeptime = 100;
}
3 啓動threadCount個線程去訪問該資源, 這裏爲了驗證sentinel每一個時間窗口都會放入5個請求去訪問資源, 這裏就設置threadCount = 1個線程, 能從後臺很明顯的看見每一個時間窗口TimeWindow=10s會放入5個請求.
public static void main(String[] args) throws Exception {
tick();
initDegradeRule();
for (int i = 0; i < threadCount; i++) {
Thread entryThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
Entry entry = null;
try {
TimeUnit.MILLISECONDS.sleep(5);
entry = SphU.entry(KEY);
// token acquired
pass.incrementAndGet();
// sleep 600 ms, as rt
TimeUnit.MILLISECONDS.sleep(sleeptime);
} catch (Exception e) {
block.incrementAndGet();
} finally {
total.incrementAndGet();
if (entry != null) {
entry.exit();
}
}
}
}
});
entryThread.setName("working-thread");
entryThread.start();
}
}
4 後臺console展現運行效果:
步驟一, 當資源的平均響應時間超過閾值, 資源進入準降級狀態, 接着sentinel會放入5個請求;
步驟二, 當這5個請求的RT響應時間仍是超過閾值, 那麼在接下來的時間窗口(這裏是10s)內, 對該資源的請求所有熔斷, 默認拋出DegradeException異常;
步驟三, 會在下一個時間窗口前, 再次放入5個請求, 這樣作的目的是, 當資源恢復正常時, Sentinel就能經歷一個時間窗口, 自動恢復對該資源的正常調用, 不過最差的狀況下, 至少須要等待一個時間窗口的時間才能恢復.
5 dashboard實時監控展現
以下圖所示: 每一個時間窗口都會放入5個請求, 驗證訪問該資源的響應時間RT是否超過閾值, 至於後面的b_qps穩定在10左右, b_qps爲0, 是由於在60s後, 訪問該資源的sleeptime耗時編程了100ms, 全部對於該資源的請求響應時間均不超過閾值200ms, 因此沒有block掉的請求, 請求所有正常經過.
四, Sentinel以訪問資源的異常比例Exception_Ratio做爲降級策略
1 初始化降級規則, 設置異常比例ExceptionRatio : 0.5(請求所有block, 偶數所有鮑一菜場), 設置降級規則 : RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO, 設置時間窗口 TimeWindow: 10s
private static void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<DegradeRule>();
DegradeRule rule = new DegradeRule();
rule.setResource(KEY);
// set limit exception ratio to 0.1
// 將比例設置成0.6將所有經過, exception_ratio = 異常/經過量
// 當資源的每秒異常總數佔經過量的比值超過閾值(DegradeRule 中的 count)以後,資源進入降級狀態
rule.setCount(0.4);
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
rule.setTimeWindow(10);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
2 當訪問該資源的每秒異常數/訪問該資源時的經過數 >= 閾值(這裏是0.5), 該資源就進入降級狀態, 在接下來的時間窗口(這裏是10s)內, 對該資源的訪問請求拋出DegradeException異常, 執行catch降級邏輯.
public static void main(String[] args) throws Exception {
tick();
initDegradeRule();
for (int i = 0; i < threadCount; i++) {
Thread entryThread = new Thread(new Runnable() {
@Override
public void run() {
int count = 0;
while (true) {
count++;
Entry entry = null;
try {
Thread.sleep(20);
entry = SphU.entry(KEY);
// token acquired, means pass
pass.addAndGet(1);
// 偶數拋出異常
if (count % 2 == 0) {
// biz code raise an exception.
throw new RuntimeException("throw runtime ");// 拋出一個業務異常,
// 對sentinel自己的異常BlockException不生效
}
} catch (BlockException e) {
block.addAndGet(1);
} catch (Throwable t) {
bizException.incrementAndGet();
// 當使用ExceptionRatio異常比例來衡量資源是否處於穩定狀態時, 須要顯示的調用 Tracer.trace(t); 用於sentinel統計異常比例
Tracer.trace(t);
} finally {
total.addAndGet(1);
if (entry != null) {
entry.exit();
}
}
}
}
});
entryThread.setName("working-thread");
entryThread.start();
}
}
3 以下圖, 展現的就是上面所描述的狀況.閾值0.5, 時間窗口10s.
4 設置異常比例閾值爲ExceptionRatio : 0.6時, 則每秒異常數/每秒經過數 < 閾值(0.6)的, 因此訪問該資源的請求, 所有經過, 以下圖:
5 dashboard實時監控展現
以下圖, 在下一個時間窗口前, 持續判斷每秒異常數/每秒經過數 是否大於閾值, 是的話, 拋出DegradeException異常, 執行catch降級邏輯.
五, Sentinel以訪問資源的異常數Exception_Count做爲降級策略
1 初始化降級規則, 設置時間窗口80s, 異常數大於等於4個, 就發生熔斷, 該時間窗口內的請求, 拋出DegradeException異常, 執行catch降級邏輯.
private static void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<DegradeRule>();
DegradeRule rule = new DegradeRule();
rule.setResource(KEY);
// set limit exception count to 4
rule.setCount(4);
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
/**
* When degrading by {@link RuleConstant#DEGRADE_GRADE_EXCEPTION_COUNT}, time window less than 60 seconds will
* not work as expected. Because the exception count is summed by minute, when a short time window elapsed, the
* degradation condition may still be satisfied.
*/
// 這裏要求的時間窗口最小值最低是1m,
// 設置時間窗口爲80s, 就會判斷在這個時間窗口內的異常數到了閾值, 達到該閾值就會熔斷
// 該時間窗口內的請求所有熔斷, 拋出DegradeException異常, 走catch降級邏輯.
rule.setTimeWindow(80);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
2 設置異常數閾值爲4, 時間窗口80s, 下面這段代碼的邏輯, 遇到偶數時就拋出一個異常
public static void main(String[] args) throws Exception {
tick();
initDegradeRule();
for (int i = 0; i < threadCount; i++) {
Thread entryThread = new Thread(new Runnable() {
@Override
public void run() {
int count = 0;
while (true) {
count++;
Entry entry = null;
try {
Thread.sleep(20);
entry = SphU.entry(KEY);
// token acquired, means pass
pass.addAndGet(1);
if (count % 2 == 0) {
// biz code raise an exception.
throw new RuntimeException("throw runtime "); // 拋出一個業務異常,
}
} catch (BlockException e) {
block.addAndGet(1);
} catch (Throwable t) {
bizException.incrementAndGet();
// 當使用ExceptionCount異常數來衡量資源是否處於穩定狀態時, 須要顯示的調用 Tracer.trace(t); 用戶sentinel統計異常數
Tracer.trace(t);
} finally {
total.addAndGet(1);
if (entry != null) {
entry.exit();
}
}
}
}
});
entryThread.setName("working-thread");
entryThread.start();
}
}
3 後臺console展現運行效果:
當該時間窗口內的異常數達到閾值4時, 直接熔斷, 全部請求將拋出DegradeException異常, 執行catch降級邏輯.
4 dashboard實時監控展現
每一個時間窗口80s, 都會去判斷異常數是否達到了閾值4, 達到了話, 全部請求直接熔斷, 因此下圖的時間窗口內p_qps=0, 沒有請求經過資源.
六, 總結
判斷一個資源是夠處於穩定狀態, 1是訪問該資源時的平均響應時間RT, 2是每秒異常數佔經過數比例ExceptionRatio, 3是時間窗口內的異常數ExceptionCount, 根據上面3種狀況, 相應的的Sentinel提供了3種方式的熔斷降級策略:
1 DEGRADE_GRADE_RT, 基於響應時間的熔斷降級
2 DEGRADE_GRADE_EXCEPTION_RATIO, 基於異常比例的熔斷降級策略
3 DEGRADE_GRADE_EXCEPTION_COUNT, 基於異常數的熔斷降級策略
詳細請查看:
https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7 ---------------------