天天看點

《一起學sentinel》五、Slot的子類及實作之AuthoritySlot和SystemSlot

一、概述

在 Sentinel 裡面,所有的資源都對應一個資源名稱(

resourceName

),每次資源調用都會建立一個

Entry

對象。Entry 可以通過對主流架構的适配自動建立,也可以通過注解的方式或調用

SphU

API 顯式建立。Entry 建立的時候,同時也會建立一系列功能插槽(slot chain),這些插槽有不同的職責,例如:

  • NodeSelectorSlot

    負責收集資源的路徑,并将這些資源的調用路徑,以樹狀結構存儲起來,用于根據調用路徑來限流降級;
  • ClusterBuilderSlot

    則用于存儲資源的統計資訊以及調用者資訊,例如該資源的 RT, QPS, thread count 等等,這些資訊将用作為多元度限流,降級的依據;
  • LogSlot

    則用于記錄用于記錄塊異常,為故障排除提供具體的日志
  • StatisticSlot

    則用于記錄、統計不同緯度的 runtime 名額監控資訊;
  • FlowSlot

    則用于根據預設的限流規則以及前面 slot 統計的狀态,來進行流量控制;
  • AuthoritySlot

    則根據配置的黑白名單和調用來源資訊,來做黑白名單控制;
  • DegradeSlot

    則通過統計資訊以及預設的規則,來做熔斷降級;
  • SystemSlot

    則通過系統的狀态,例如 load1 等,來控制總的入口流量;

下面是關系結構圖

《一起學sentinel》五、Slot的子類及實作之AuthoritySlot和SystemSlot

二、AuthoritySlot分析

1.AuthoritySlot介紹

官方文檔是這樣描述AuthoritySlot的:

很多時候,我們需要根據調用來源來判斷該次請求是否允許放行,這時候可以使用 Sentinel 的來源通路控制(黑白名單控制)的功能。來源通路控制根據資源的請求來源(

origin

)限制資源是否通過,若配置白名單則隻有請求來源位于白名單内時才可通過;若配置黑名單則請求來源位于黑名單時不通過,其餘的請求通過。

調用方資訊通過

ContextUtil.enter(resourceName, origin)

方法中的

origin

參數傳入。

規則配置

來源通路控制規則(

AuthorityRule

)非常簡單,主要有以下配置項:
  • resource

    :資源名,即限流規則的作用對象。
  • limitApp

    :對應的黑名單/白名單,不同 origin 用

    ,

    分隔,如

    appA,appB

  • strategy

    :限制模式,

    AUTHORITY_WHITE

    為白名單模式,

    AUTHORITY_BLACK

    為黑名單模式,預設為白名單模式。

示例

比如我們希望控制對資源

test

的通路設定白名單,隻有來源為

appA

appB

的請求才可通過,則可以配置如下白名單規則:
AuthorityRule rule = new AuthorityRule();
rule.setResource("test");
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("appA,appB");
AuthorityRuleManager.loadRules(Collections.singletonList(rule));           

2.源碼解讀

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)throws Throwable {
    checkBlackWhiteAuthority(resourceWrapper, context);
    fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
    fireExit(context, resourceWrapper, count, args);
}           

1.在

entry

階段,執行了一個校驗方法.

void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {
        Map<String, Set<AuthorityRule>> authorityRules =  AuthorityRuleManager.getAuthorityRules();

        if (authorityRules == null) {
            return;
        }

        Set<AuthorityRule> rules = authorityRules.get(resource.getName());//根據resource擷取指定rules
        if (rules == null) {
            return;
        }

        for (AuthorityRule rule : rules) {
            if (!AuthorityRuleChecker.passCheck(rule, context)) {
                throw new AuthorityException(context.getOrigin(), rule);
            }
        }
    }           

2.如果

AuthorityRuleManager

裡面的

authorityRules

為空,則跳過黑白名單的校驗,否則根據

resource

擷取本資源的

rules

(Set),循環這個

rules

,在循環裡面調用校驗方法(

AuthorityRuleChecker.passCheck(rule, context)

static boolean passCheck(AuthorityRule rule, Context context) {
    String requester = context.getOrigin();

    // Empty origin or empty limitApp will pass.
    if (StringUtil.isEmpty(requester) || StringUtil.isEmpty(rule.getLimitApp())) {
        return true;
    }

    // Do exact match with origin name.
    int pos = rule.getLimitApp().indexOf(requester);//包含關系驗證
    boolean contain = pos > -1;

    if (contain) {//equals驗證
        boolean exactlyMatch = false;
        String[] appArray = rule.getLimitApp().split(",");
        for (String app : appArray) {
            if (requester.equals(app)) {
                exactlyMatch = true;
                break;
            }
        }

        contain = exactlyMatch;
    }

    int strategy = rule.getStrategy();
    if (strategy == RuleConstant.AUTHORITY_BLACK && contain) {//黑名單驗證
        return false;
    }

    if (strategy == RuleConstant.AUTHORITY_WHITE && !contain) {//白名單驗證
        return false;
    }

    return true;
}           

3.這個核心的校驗方法内,首先會進行基礎判斷,

Empty origin or empty limitApp will pass.

。然後判斷

resource

中的

rule

,是否已經注冊到了

rule

中,如果注冊了并且能夠對應的上,

exactlyMatch

參數會被置為

true

4.拿到了對應結果

contain(exactlyMatch)

後,開始進行

strategy

的判斷。如果是黑名單,則傳回

contain

;如果是白名單驗證則傳回

!contain

作為

sentinel

的黑白名單控制,實作的功能也比較簡單,隻需要我們需要定義控制規則就行了。

多個相同的

resource

(name),對應着不同的

context

(name),那麼我們就可以快速統計出某個資源的總統計資訊。對應着多個(歸屬于同一個resource的)

DefaultNode

對應同一個

ClusterNode

三、SystemSlot分析

1.SystemSlot介紹

官方文檔是這樣描述SystemSlot的:一句話概括就是系統級限流的控制器

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,  boolean prioritized, Object... args) throws Throwable {
    SystemRuleManager.checkSystem(resourceWrapper);//整個系統限流的切入點
    fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
    fireExit(context, resourceWrapper, count, args);
}           

1.在下一個slot的開始前,直接調用了com.alibaba.csp.sentinel.slots.system.SystemRuleManager的checkSystem方法。

public static void checkSystem(ResourceWrapper resourceWrapper) throws BlockException {
    if (resourceWrapper == null) {
        return;
    }
    // 是否打開校驗開關
    if (!checkSystemStatus.get()) {
        return;
    }

    // 是否為入口流量
    if (resourceWrapper.getEntryType() != EntryType.IN) {
        return;
    }

    //qps 的對比
    double currentQps = Constants.ENTRY_NODE == null ? 0.0 : Constants.ENTRY_NODE.successQps();
    if (currentQps > qps) {
        throw new SystemBlockException(resourceWrapper.getName(), "qps");
    }

    //線程數的對比
    int currentThread = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.curThreadNum();
    if (currentThread > maxThread) {
        throw new SystemBlockException(resourceWrapper.getName(), "thread");
    }

    //獲得總成功計數的對比
    double rt = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.avgRt();
    if (rt > maxRt) {
        throw new SystemBlockException(resourceWrapper.getName(), "rt");
    }

    // 按照BBR算法
    if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {
        if (!checkBbr(currentThread)) {
            throw new SystemBlockException(resourceWrapper.getName(), "load");
        }
    }

    // cpu狀态
    if (highestCpuUsageIsSet && getCurrentCpuUsage() > highestCpuUsage) {
        throw new SystemBlockException(resourceWrapper.getName(), "cpu");
    }
}           

2.進入到校驗方案,慣例 的校驗了一次resource的非空。

3.開始校驗是否打開系統限流開關,流量類型(全局限流隻校驗入口流量)。

4.進行qps,線程數的校驗,當超過任何系統規則的門檻值時throws BlockException。

5.進行成功通路數,RT,CPU狀态的校驗,當超過任何系統規則的門檻值時throws BlockException。

四、小結

本期我們講述了Slot的子類

LogSlot

StatisticSlot

的基本實作原理。

現在建立我們的知識樹

執行個體化DefaultNode和ClusterNode,建立結構樹

建立上下文時,首先會在

NodeSelectorSlot

中判斷是否有

DefaultNode

如果沒有則新增一個基于

resource

DefaultNode

,然後執行下一個

slot

下一個

slot

ClusterBuilderSlot

ClusterBuilderSlot

會判斷是否有對應的

ClusterNode

,如果沒有則新增一個基于resource的

ClusterNode

并繼續下一個流程(

slot

)。

總結來說,這個兩個

slot

奠定了一個基于

resource

進行全局控制的基調。

進行資訊收集

LogSlot

DefaultNode

ClusterNode

初始化後,作為業務執行個體子產品的分界點,收集全局異常并處理。

StatisticSlot

作為全局統計的執行個體,依托于

ClusterNode

,将全局的

RT

,

QPS

thread

count

等等資訊存放在

clusterNodeMap

裡面。

進行權限校驗及系統級限流

在樹結構和資訊收集的slot建立完畢後,開始業務邏輯的實作,首先實作的就是AuthoritySlot的黑白名單能力,依托sentinel的resource的定義,我們很簡單就可以拿到關于resource的authorityRules,将對應的rules取出後,以此進行黑、白名單判斷,也可以了解為一種權限級别的限流措施。

SystemSlot則是全統計的全局限流,從調用點origins級别的配置中讀取了配置好的限流措施,在下一個slot實作前完成了所有的判斷,如qps,線程數,成功通路數,RT,CPU狀态。如果出現異常,則throws BlockException,交給之前的slot去處理相應邏輯。到這裡,一個基礎的限流架構已經基本實作。

繼續閱讀