Backend Development 16 min read

Understanding Sentinel Flow Control Rules and FlowSlot Implementation

This article explains Sentinel's flow control rule definition, the attributes of the FlowRule class, and how the FlowSlot, FlowRuleChecker, and related components evaluate and enforce these rules, including code examples and a discussion of different flow control strategies and node types.

Manbang Technology Team
Manbang Technology Team
Manbang Technology Team
Understanding Sentinel Flow Control Rules and FlowSlot Implementation

The article introduces the usage of flow control rules in Sentinel, focusing on the rule entities defined in the source code rather than cluster-wide rate limiting, which will be covered in future articles.

Flow Control Rules

In the Sentinel dashboard, flow control rules can be added (see the screenshot). The core rule entity is com.alibaba.csp.sentinel.slots.block.flow.FlowRule , which contains the following attributes:

resource : the name of the resource to be limited.

limitApp : the origin for source‑based limiting, default is default .

grade : threshold type, 1 for QPS limiting, 0 for thread‑count limiting.

count : the single‑machine threshold value.

strategy : flow control mode (0 = direct, 1 = related, 2 = chain).

refResource : the related resource used when the mode is related or chain.

controlBehavior : the flow control effect, with four possible values: 0 – default fast‑fail. 1 – warm‑up mode. 2 – rate‑limiter (steady‑queue) mode. 3 – combined warm‑up and rate‑limiter.

warmUpPeriodSec : warm‑up time in seconds (used in warm‑up mode).

maxQueueingTimeMs : maximum waiting time in steady‑queue mode.

clusterMode and clusterConfig : flags and configuration for cluster rate limiting.

controller : the controller that implements the selected flow control effect.

FlowSlot

The FlowSlot is responsible for evaluating flow control rules as part of Sentinel's responsibility‑chain design. Its source entry point is com.alibaba.csp.sentinel.slots.block.flow.FlowSlot :

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                 boolean prioritized, Object... args) throws Throwable {
    // Rule validation
    checkFlow(resourceWrapper, context, node, count, prioritized);
    // If validation passes, invoke the next slot
    fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)
        throws BlockException {
    // Call FlowRuleChecker for rule validation
    checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);
}

The actual validation logic resides in the FlowRuleChecker class. Below is the checkFlow method:

FlowRuleCheck#checkFlow

public void checkFlow(Function
> ruleProvider, ResourceWrapper resource,
                      Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {
    // Code 1
    if (ruleProvider == null || resource == null) {
        return;
    }
    // Code 2
    Collection
rules = ruleProvider.apply(resource.getName());
    if (rules != null) {
        for (FlowRule rule : rules) {
            // Code 3
            if (!canPassCheck(rule, context, node, count, prioritized)) {
                // Code 4
                throw new FlowException(rule.getLimitApp(), rule);
            }
        }
    }
}

Explanation of the steps:

Code 1: Checks whether the rule provider or resource is null.

Code 2: Retrieves the list of rules for the given resource.

Code 3: Iterates over each rule and calls canPassCheck to verify it.

Code 4: If a rule fails, a FlowException is thrown, activating the rate limit.

canPassCheck

public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount,
                           boolean prioritized) {
    // Code 1
    String limitApp = rule.getLimitApp();
    if (limitApp == null) {
        return true;
    }
    // Code 2
    if (rule.isClusterMode()) {
        return passClusterCheck(rule, context, node, acquireCount, prioritized);
    }
    // Code 3
    return passLocalCheck(rule, context, node, acquireCount, prioritized);
}

Key points:

If limitApp is null, the method returns true (no limiting).

If cluster mode is enabled, cluster‑level checking is performed (not covered in this article).

Otherwise, local limiting is applied via passLocalCheck .

passLocalCheck

private static boolean passLocalCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount,
                                      boolean prioritized) {
    // Code 1
    Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node);
    if (selectedNode == null) {
        return true;
    }
    // Code 2
    return rule.getRater().canPass(selectedNode, acquireCount, prioritized);
}

This method selects the appropriate Node based on the requester's origin and the rule's strategy, then delegates to the rule's Rater to decide whether the request can pass.

selectNodeByRequesterAndStrategy

static Node selectNodeByRequesterAndStrategy(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node) {
    String limitApp = rule.getLimitApp();
    int strategy = rule.getStrategy();
    String origin = context.getOrigin();

    // If the limit app matches the origin and the origin is not default/other
    if (limitApp.equals(origin) && filterOrigin(origin)) {
        if (strategy == RuleConstant.STRATEGY_DIRECT) {
            // Return OriginNode
            return context.getOriginNode();
        }
        // Return related node
        return selectReferenceNode(rule, context, node);
    } else if (RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp)) {
        if (strategy == RuleConstant.STRATEGY_DIRECT) {
            // Return the cluster node of the current resource
            return node.getClusterNode();
        }
        return selectReferenceNode(rule, context, node);
    } else if (RuleConstant.LIMIT_APP_OTHER.equals(limitApp)
               && FlowRuleManager.isOtherOrigin(origin, rule.getResource())) {
        if (strategy == RuleConstant.STRATEGY_DIRECT) {
            return context.getOriginNode();
        }
        return selectReferenceNode(rule, context, node);
    }
    return null;
}

The method determines which Node to use based on the rule's limitApp and strategy . If source‑based limiting is configured, the strategy must be STRATEGY_DIRECT , otherwise a related node is fetched.

selectReferenceNode

static Node selectReferenceNode(FlowRule rule, Context context, DefaultNode node) {
    String refResource = rule.getRefResource();
    int strategy = rule.getStrategy();

    if (StringUtil.isEmpty(refResource)) {
        return null;
    }
    // Related resource mode
    if (strategy == RuleConstant.STRATEGY_RELATE) {
        return ClusterBuilderSlot.getClusterNode(refResource);
    }
    // Chain mode
    if (strategy == RuleConstant.STRATEGY_CHAIN) {
        if (!refResource.equals(context.getName())) {
            return null;
        }
        return node;
    }
    return null;
}

Depending on the strategy, this method returns either a ClusterNode (related resource) or the current DefaultNode (chain mode).

Flow Control Strategies

Direct (STRATEGY_DIRECT) : Limits the specified resource directly when its threshold is exceeded. This is the default mode.

Related (STRATEGY_RELATE) : Limits a resource when a *related* resource reaches its threshold.

Chain (STRATEGY_CHAIN) : Limits traffic based on the entry point of a call chain; only the entry resource is considered.

Node Types

DefaultNode : Holds statistics for a resource in a specific context; used for direct and chain limiting.

ClusterNode : Aggregates statistics for a resource across all contexts; used for default and related limiting.

OriginNode : Tracks statistics per request origin; used for source‑based limiting.

DefaultController (Fast‑Fail)

@Override
public boolean canPass(Node node, int acquireCount, boolean prioritized) {
    // Code 1
    int curCount = avgUsedTokens(node);
    // Code 2
    if (curCount + acquireCount > count) {
        // Code 3 – warm‑up handling (omitted for brevity)
        if (prioritized && grade == RuleConstant.FLOW_GRADE_QPS) {
            long currentTime = TimeUtil.currentTimeMillis();
            long waitInMs = node.tryOccupyNext(currentTime, acquireCount, count);
            if (waitInMs < OccupyTimeoutProperty.getOccupyTimeout()) {
                node.addWaitingRequest(currentTime + waitInMs, acquireCount);
                node.addOccupiedPass(acquireCount);
                sleep(waitInMs);
                throw new PriorityWaitException(waitInMs);
            }
        }
        return false;
    }
    return true;
}

private int avgUsedTokens(Node node) {
    if (node == null) {
        // Default value 0
        return DEFAULT_AVG_USED_TOKENS;
    }
    // Determine based on grade
    return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int) (node.passQps());
}

The controller first obtains the current usage ( curCount ) based on the rule's grade (QPS or thread). It then checks curCount + acquireCount > count . If the limit is exceeded, fast‑fail returns false unless warm‑up logic applies.

Note: The check curCount + acquireCount > count is not atomic; the count is read from a Node while updates happen in StatisticSlot . Under high concurrency this can cause a small deviation, which Sentinel accepts as a trade‑off between performance and strict accuracy.

The article also includes a brief illustration of chain mode using two Spring MVC endpoints ( test‑xx and test‑yy ) that both call the same service method. When the flow rule is configured for test‑yy , only calls to that endpoint are limited, demonstrating how the entry resource name influences chain limiting.

Author: Zhang Yunhe, middleware engineer at Manbang, responsible for the flow‑control platform.

BackendJavaMicroservicesRateLimitingSentinelFlowControl
Manbang Technology Team
Written by

Manbang Technology Team

Manbang Technology Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.