Sentinel源碼解析二(Slot總覽)

寫在前面

本文繼續來分析Sentinel的源碼,上篇文章對Sentinel的調用過程作了深刻分析,主要涉及到了兩個概念:插槽鏈和Node節點。那麼接下來咱們就根據插槽鏈的調用關係來依次分析每一個插槽(slot)的源碼。html

默認插槽鏈的調用順序,以及每種類型Node節點的關係都在上面文章開頭分析過 Sentinel源碼解析一java

NodeSelectorSlot

/**
 * 相同的資源可是Context不一樣,分別新建 DefaultNode,並以ContextName爲key
 */
private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);

public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
    throws Throwable {
    
    // 根據ContextName嘗試獲取DefaultNode
    DefaultNode node = map.get(context.getName());
    if (node == null) {
        synchronized (this) {
            node = map.get(context.getName());
            if (node == null) {
            // 初始化DefaultNode,每一個Context對應一個
                node = new DefaultNode(resourceWrapper, null);
                HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
                cacheMap.putAll(map);
                cacheMap.put(context.getName(), node);
                map = cacheMap;
            }
            // 構建 Node tree
            ((DefaultNode)context.getLastNode()).addChild(node);
        }
    }

    context.setCurNode(node);
    // 喚醒執行下一個插槽
    fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

NodeSelectorSlot顧名思義是用來構建Node的。
咱們能夠看到NodeSelectorSlot對於不一樣的上下文都會生成一個DefaultNode。這裏還有一個要注意的點:相同的資源({@link ResourceWrapper#equals(Object)})將全局共享相同的{@link ProcessorSlotChain},不管在哪一個上下文中,所以不一樣的上下文能夠進入到同一個對象的NodeSelectorSlot.entry方法中,那麼這裏要怎麼區分不一樣的上下文所建立的資源Node呢?顯然可使用上下文名稱做爲映射鍵以區分相同的資源Node。node

而後這裏要考慮另外一個問題。一個資源有可能建立多個DefaultNode(有多個上下文時),那麼咱們應該如何快速的獲取總的統計數據呢?app

答案就在下一個Slot(ClusterBuilderSlot)中被解決了。ide

ClusterBuilderSlot

上面有提到一個問題,咱們要如何統計不一樣上下文相同資源的總量數據。ClusterBuilderSlot給了很好的解決方案:具備相同資源名稱的共享一個ClusterNodeui

// 相同的資源共享一個 ClusterNode
private static volatile Map<ResourceWrapper, ClusterNode> clusterNodeMap = new HashMap<>();

private static final Object lock = new Object();

private volatile ClusterNode clusterNode = null;

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                  boolean prioritized, Object... args)
    throws Throwable {
    // 判斷本資源是否已經初始化過clusterNode
    if (clusterNode == null) {
        synchronized (lock) {
            if (clusterNode == null) {
                // 沒有初始化則初始化clusterNode
                clusterNode = new ClusterNode(resourceWrapper.getName(), resourceWrapper.getResourceType());
                HashMap<ResourceWrapper, ClusterNode> newMap = new HashMap<>(Math.max(clusterNodeMap.size(), 16));
                newMap.putAll(clusterNodeMap);
                newMap.put(node.getId(), clusterNode);

                clusterNodeMap = newMap;
            }
        }
    }
    // 給相同資源的DefaultNode設置同樣的ClusterNode
    node.setClusterNode(clusterNode);

    /*
     * 若是有來源則新建一個來源Node
     */
    if (!"".equals(context.getOrigin())) {
        Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin());
        context.getCurEntry().setOriginNode(originNode);
    }

    fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

上面的代碼其實就是作了一件事情,爲資源建立CluserNode。這裏我又要提一嘴 相同的資源({@link ResourceWrapper#equals(Object)})將全局共享相同的{@link ProcessorSlotChain},不管在哪一個上下文中。也就是說,能進入到同一個ClusterBuilderSlot對象的entry方法的請求都是來自同一個資源的,因此這些相同資源須要初始化一個統一的CluserNode用來作流量的彙總統計。this

LogSlot

代碼比較簡單,邏輯就是打印異常日誌,就不分析了線程

StatisticSlot

StatisticSlot 是 Sentinel 的核心功能插槽之一,用於統計實時的調用數據。日誌

public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                  boolean prioritized, Object... args) throws Throwable {
    try {
        // 先進行後續的check,包括規則的check,黑白名單check
        fireEntry(context, resourceWrapper, node, count, prioritized, args);

        // 統計默認qps 線程數
        node.increaseThreadNum();
        node.addPassRequest(count);

        if (context.getCurEntry().getOriginNode() != null) {
            // 根據來源統計qps 線程數
            context.getCurEntry().getOriginNode().increaseThreadNum();
            context.getCurEntry().getOriginNode().addPassRequest(count);
        }

        if (resourceWrapper.getEntryType() == EntryType.IN) {
            // 統計入口 qps 線程數
            Constants.ENTRY_NODE.increaseThreadNum();
            Constants.ENTRY_NODE.addPassRequest(count);
        }

        .... 省略其餘代碼
    }
}

StatisticSlot主要作了4種不一樣維度的流量統計

  1. 資源在上下文維度(DefaultNode)的統計
  2. clusterNode 維度的統計
  3. Origin 來源維度的統計
  4. 入口全局流量的統計

關於流量的統計原理的本文就不深刻分析了,接下來的文章中會單獨分析code

SystemSlot

SystemSlot比較簡單,其實就是根據StatisticSlot所統計的全局入口流量進行限流。

AuthoritySlot

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);
}

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());
    if (rules == null) {
        return;
    }
    // 對規則進行校驗,只要有一條不經過 就拋異常
    for (AuthorityRule rule : rules) {
        if (!AuthorityRuleChecker.passCheck(rule, context)) {
            throw new AuthorityException(context.getOrigin(), rule);
        }
    }
}

AuthoritySlot會對資源的黑白名單作檢查,而且只要有一條不經過就拋異常。

FlowSlot

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

    fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)
    throws BlockException {
    // 檢查限流規則
    checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);
}

這個slot主要根據預設的資源的統計信息,按照固定的次序,依次生效。若是一個資源對應兩條或者多條流控規則,則會根據以下次序依次檢驗,直到所有經過或者有一個規則生效爲止:
而且一樣也會根據三種不一樣的維度來進行限流:

  1. 資源在上下文維度(DefaultNode)的統計
  2. clusterNode 維度的統計
  3. Origin 來源維度的統計

關於流控規則源碼的深刻分析就不在本篇文章贅述了

DegradeSlot

這個slot主要針對資源的平均響應時間(RT)以及異常比率,來決定資源是否在接下來的時間被自動熔斷掉。

總結

  1. 相同的資源({@link ResourceWrapper#equals(Object)})將全局共享相同的{@link ProcessorSlotChain},不管在哪一個上下文中
  2. 流控有多個維度,分別包括:1.不一樣上下文中的資源 2.相同資源 3.入口流量 3.相同的來源

Sentinel系列

相關文章
相關標籤/搜索