一炮捧、引子
除了流量控制以外贫奠,對調(diào)用鏈路中不穩(wěn)定的資源進(jìn)行熔斷降級也是保障高可用的重要措施之一他膳。由于調(diào)用關(guān)系的復(fù)雜性响逢,如果調(diào)用鏈路中的某個資源不穩(wěn)定,最終會導(dǎo)致請求發(fā)生堆積棕孙。Sentinel 熔斷降級會在調(diào)用鏈路中某個資源出現(xiàn)不穩(wěn)定狀態(tài)時(例如調(diào)用超時或異常比例升高)舔亭,對這個資源的調(diào)用進(jìn)行限制,讓請求快速失敗蟀俊,避免影響到其它的資源而導(dǎo)致級聯(lián)錯誤钦铺。當(dāng)資源被降級后,在接下來的降級時間窗口之內(nèi)欧漱,對該資源的調(diào)用都自動熔斷(默認(rèn)行為是拋出 DegradeException
)职抡。
二、降級策略
我們通常用以下幾種方式來衡量資源是否處于穩(wěn)定的狀態(tài):
- 平均響應(yīng)時間 (
DEGRADE_GRADE_RT
):當(dāng)資源的平均響應(yīng)時間超過閾值(DegradeRule
中的count
误甚,以 ms 為單位)之后,資源進(jìn)入準(zhǔn)降級狀態(tài)谱净。接下來如果持續(xù)進(jìn)入 5 個請求窑邦,它們的 RT 都持續(xù)超過這個閾值,那么在接下的時間窗口(DegradeRule
中的timeWindow
壕探,以 s 為單位)之內(nèi)冈钦,對這個方法的調(diào)用都會自動地返回(拋出DegradeException
)。 - 異常比例 (
DEGRADE_GRADE_EXCEPTION_RATIO
):當(dāng)資源的每秒異忱钋耄總數(shù)占通過量的比值超過閾值(DegradeRule
中的count
)之后,資源進(jìn)入降級狀態(tài)导盅,即在接下的時間窗口(DegradeRule
中的timeWindow
较幌,以 s 為單位)之內(nèi),對這個方法的調(diào)用都會自動地返回白翻。異常比率的閾值范圍是[0.0, 1.0]
乍炉,代表 0% - 100%绢片。 - 異常數(shù) (
DEGRADE_GRADE_EXCEPTION_COUNT
):當(dāng)資源近 1 分鐘的異常數(shù)目超過閾值之后會進(jìn)行熔斷。
注意:異常降級僅針對業(yè)務(wù)異常岛琼,對 Sentinel 限流降級本身的異常(BlockException
)不生效底循。為了統(tǒng)計(jì)異常比例或異常數(shù),需要通過 Tracer.trace(ex)
記錄業(yè)務(wù)異常槐瑞。示例:
Entry entry = null;
try {
entry = SphU.entry(key, EntryType.IN, key);
// Write your biz code here.
// <<BIZ CODE>>
} catch (Throwable t) {
if (!BlockException.isBlockException(t)) {
//這里會統(tǒng)計(jì)異常數(shù)
Tracer.trace(t);
}
} finally {
if (entry != null) {
entry.exit();
}
}
開源整合模塊熙涤,如 Sentinel Dubbo Adapter, Sentinel Web Servlet Filter 或
@SentinelResource
注解會自動統(tǒng)計(jì)業(yè)務(wù)異常,無需手動調(diào)用困檩。
三祠挫、源碼分析
3.1 DegradeSlot
public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
throws Throwable {
//規(guī)則檢查
DegradeRuleManager.checkDegrade(resourceWrapper, context, node, count);
fireEntry(context, resourceWrapper, node, count, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
}
進(jìn)入DegradeRuleManager中,可以發(fā)現(xiàn)與前面的限流規(guī)則一樣窗看,這個是用于管理降級的類茸歧。
我們重點(diǎn)看下checkDegrade方法。
3.2 DegradeRuleManager
public static void checkDegrade(ResourceWrapper resource, Context context, DefaultNode node, int count)
throws BlockException {
if (degradeRules == null) {
return;
}
List<DegradeRule> rules = degradeRules.get(resource.getName());
if (rules == null) {
return;
}
for (DegradeRule rule : rules) {
if (!rule.passCheck(context, node, count)) {
throw new DegradeException(rule.getLimitApp());
}
}
}
- degradeRule是對應(yīng)資源的額降級規(guī)則显沈,是一個map软瞎。
- 獲取到對應(yīng)資源的降級規(guī)則。
- 調(diào)用Degrade的passCheck檢測是否需要降級拉讯。
- 若降級了則拋出DegradeException異常涤浇。
3.3 DegradeRule
降級規(guī)則的參數(shù)
- count: RT臨界值或者異常數(shù)、異常比列
- timeWindow:降級的時間間隔魔慷,單位秒
- grade:閾值類型RT只锭、異常數(shù)、異常比例
下面看下passCheck方法:
@Override
public boolean passCheck(Context context, DefaultNode node, int acquireCount, Object... args) {
if (cut) {
return false;
}
ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(this.getResource());
if (clusterNode == null) {
return true;
}
if (grade == RuleConstant.DEGRADE_GRADE_RT) {
double rt = clusterNode.avgRt();
if (rt < this.count) {
passCount.set(0);
return true;
}
// Sentinel will degrade the service only if count exceeds.
if (passCount.incrementAndGet() < RT_MAX_EXCEED_N) {
return true;
}
} else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) {
double exception = clusterNode.exceptionQps();
double success = clusterNode.successQps();
long total = clusterNode.totalQps();
// if total qps less than RT_MAX_EXCEED_N, pass.
if (total < RT_MAX_EXCEED_N) {
return true;
}
double realSuccess = success - exception;
if (realSuccess <= 0 && exception < RT_MAX_EXCEED_N) {
return true;
}
if (exception / success < count) {
return true;
}
} else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
double exception = clusterNode.totalException();
if (exception < count) {
return true;
}
}
synchronized (lock) {
if (!cut) {
// Automatically degrade.
cut = true;
ResetTask resetTask = new ResetTask(this);
pool.schedule(resetTask, timeWindow, TimeUnit.SECONDS);
}
return false;
}
}
關(guān)鍵參數(shù):
- cut:資源是否已經(jīng)降級標(biāo)志院尔,為true表示已經(jīng)降級了蜻展。
- passCount:若達(dá)到降級條件后,連續(xù)復(fù)合降級條件的次數(shù)邀摆,默認(rèn)為RT_MAX_EXCEED_N(5)次纵顾。
過程大致如下:
- 如果已經(jīng)降級了(cut為ture),則阻塞栋盹;否則獲取
clusterNode
施逾。 - 降級規(guī)則為
RuleConstant.DEGRADE_GRADE_RT
,先獲取資源的平均RT例获;若RT小于設(shè)置的閾值count
汉额,則請求通過并設(shè)置passCount
為0,否在判斷passCount是否小于5榨汤,若小于則請求通過蠕搜;否則請求阻塞。 - 降級規(guī)則為
RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO
異常比例時件余,先獲取資源的exception
數(shù)讥脐,success
數(shù)遭居,total
數(shù)。若total數(shù)小于5請求通過旬渠;exception是小于5請求通過俱萍;異常比列exception/total
小于設(shè)置的閾值則請求通過;否則請求阻塞告丢。 - 降級規(guī)則為
RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT
異常數(shù)時枪蘑,若異常數(shù)小于設(shè)置的閾值時則請求通過;否則請求阻塞岖免。 - 若上述有規(guī)則不滿足岳颇,則說明該資源需要降級;降級時需要先設(shè)置cut為true颅湘,并啟動一個定時任務(wù)來設(shè)置降級時間窗口后降級的重置话侧。該任務(wù)如下:
private static final class ResetTask implements Runnable {
private DegradeRule rule;
ResetTask(DegradeRule rule) {
this.rule = rule;
}
@Override
public void run() {
//設(shè)置passCount為0
rule.getPassCount().set(0);
//設(shè)置cut為false
rule.setCut(false);
}
}
四、我的總結(jié)
- 介紹了Sentinel的j降級規(guī)則以及降級原理闯参。
- 降級有三種策略瞻鹏,rt,異常數(shù)鹿寨,異常比例新博;目前dashboard控制臺能夠設(shè)置應(yīng)該就只有rt和異常比例了。
- 通過設(shè)置passCount來避免出現(xiàn)偶爾一個請求異常的情況脚草,提高降級的準(zhǔn)確性赫悄。
- 生產(chǎn)環(huán)境下建議使用設(shè)置rt策略來控制降級。
以上內(nèi)容馏慨,若有不當(dāng)之處埂淮,請指正。