Extract flow rule checker from FlowRule
- Extract flow rule checker from legacy `passCheck` - Remove redundant code - Refactor FlowSlot (some logic moved from FlowRuleManager) - Rename `Controller` to `TrafficShapingController` Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
700d24db4e
commit
d4d63fad95
|
|
@ -15,18 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel.slots.block.flow;
|
package com.alibaba.csp.sentinel.slots.block.flow;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
|
||||||
import com.alibaba.csp.sentinel.context.Context;
|
import com.alibaba.csp.sentinel.context.Context;
|
||||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||||
import com.alibaba.csp.sentinel.node.Node;
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
|
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
|
||||||
|
|
||||||
/***
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Each flow rule is mainly composed of three factors: <strong>grade</strong>,
|
* Each flow rule is mainly composed of three factors: <strong>grade</strong>,
|
||||||
* <strong>strategy</strong> and <strong>controlBehavior</strong>.
|
* <strong>strategy</strong> and <strong>controlBehavior</strong>:
|
||||||
* </p>
|
* </p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>The {@link #grade} represents the threshold type of flow control (by QPS or thread count).</li>
|
* <li>The {@link #grade} represents the threshold type of flow control (by QPS or thread count).</li>
|
||||||
|
|
@ -45,6 +42,12 @@ public class FlowRule extends AbstractRule {
|
||||||
setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
|
setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FlowRule(String resourceName) {
|
||||||
|
super();
|
||||||
|
setResource(resourceName);
|
||||||
|
setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The threshold type of flow control (0: thread count, 1: QPS).
|
* The threshold type of flow control (0: thread count, 1: QPS).
|
||||||
*/
|
*/
|
||||||
|
|
@ -65,7 +68,7 @@ public class FlowRule extends AbstractRule {
|
||||||
private int strategy = RuleConstant.STRATEGY_DIRECT;
|
private int strategy = RuleConstant.STRATEGY_DIRECT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference resource in flow control with relevant resource.
|
* Reference resource in flow control with relevant resource or context.
|
||||||
*/
|
*/
|
||||||
private String refResource;
|
private String refResource;
|
||||||
|
|
||||||
|
|
@ -82,7 +85,10 @@ public class FlowRule extends AbstractRule {
|
||||||
*/
|
*/
|
||||||
private int maxQueueingTimeMs = 500;
|
private int maxQueueingTimeMs = 500;
|
||||||
|
|
||||||
private Controller controller;
|
/**
|
||||||
|
* The traffic shaping (throttling) controller.
|
||||||
|
*/
|
||||||
|
private TrafficShapingController controller;
|
||||||
|
|
||||||
public int getControlBehavior() {
|
public int getControlBehavior() {
|
||||||
return controlBehavior;
|
return controlBehavior;
|
||||||
|
|
@ -102,11 +108,15 @@ public class FlowRule extends AbstractRule {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlowRule setRater(Controller rater) {
|
FlowRule setRater(TrafficShapingController rater) {
|
||||||
this.controller = rater;
|
this.controller = rater;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrafficShapingController getRater() {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
public int getWarmUpPeriodSec() {
|
public int getWarmUpPeriodSec() {
|
||||||
return warmUpPeriodSec;
|
return warmUpPeriodSec;
|
||||||
}
|
}
|
||||||
|
|
@ -154,90 +164,7 @@ public class FlowRule extends AbstractRule {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean passCheck(Context context, DefaultNode node, int acquireCount, Object... args) {
|
public boolean passCheck(Context context, DefaultNode node, int acquireCount, Object... args) {
|
||||||
String limitApp = this.getLimitApp();
|
return true;
|
||||||
if (limitApp == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
String origin = context.getOrigin();
|
|
||||||
Node selectedNode = selectNodeByRequesterAndStrategy(origin, context, node);
|
|
||||||
if (selectedNode == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return controller.canPass(selectedNode, acquireCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Node selectNodeByRequesterAndStrategy(String origin, Context context, DefaultNode node) {
|
|
||||||
// The limit app should not be empty.
|
|
||||||
String limitApp = this.getLimitApp();
|
|
||||||
|
|
||||||
if (limitApp.equals(origin)) {
|
|
||||||
if (strategy == RuleConstant.STRATEGY_DIRECT) {
|
|
||||||
return context.getOriginNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
String refResource = this.getRefResource();
|
|
||||||
if (StringUtil.isEmpty(refResource)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strategy == RuleConstant.STRATEGY_RELATE) {
|
|
||||||
return ClusterBuilderSlot.getClusterNode(refResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strategy == RuleConstant.STRATEGY_CHAIN) {
|
|
||||||
if (!refResource.equals(context.getName())) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp)) {
|
|
||||||
if (strategy == RuleConstant.STRATEGY_DIRECT) {
|
|
||||||
return node.getClusterNode();
|
|
||||||
}
|
|
||||||
String refResource = this.getRefResource();
|
|
||||||
if (StringUtil.isEmpty(refResource)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strategy == RuleConstant.STRATEGY_RELATE) {
|
|
||||||
return ClusterBuilderSlot.getClusterNode(refResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strategy == RuleConstant.STRATEGY_CHAIN) {
|
|
||||||
if (!refResource.equals(context.getName())) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (RuleConstant.LIMIT_APP_OTHER.equals(limitApp)
|
|
||||||
&& FlowRuleManager.isOtherOrigin(origin, getResource())) {
|
|
||||||
if (strategy == RuleConstant.STRATEGY_DIRECT) {
|
|
||||||
return context.getOriginNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
String refResource = this.getRefResource();
|
|
||||||
if (StringUtil.isEmpty(refResource)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (strategy == RuleConstant.STRATEGY_RELATE) {
|
|
||||||
return ClusterBuilderSlot.getClusterNode(refResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strategy == RuleConstant.STRATEGY_CHAIN) {
|
|
||||||
if (!refResource.equals(context.getName())) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (node != null) {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alibaba.csp.sentinel.slots.block.flow;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.context.Context;
|
||||||
|
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||||
|
import com.alibaba.csp.sentinel.node.Node;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
|
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||||
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rule checker for flow control rules.
|
||||||
|
*
|
||||||
|
* @author Eric Zhao
|
||||||
|
*/
|
||||||
|
final class FlowRuleChecker {
|
||||||
|
|
||||||
|
static boolean passCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount) {
|
||||||
|
String limitApp = rule.getLimitApp();
|
||||||
|
if (limitApp == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node);
|
||||||
|
if (selectedNode == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rule.getRater().canPass(selectedNode, acquireCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Node selectReferenceNode(FlowRule rule, Context context, DefaultNode node) {
|
||||||
|
String refResource = rule.getRefResource();
|
||||||
|
int strategy = rule.getStrategy();
|
||||||
|
|
||||||
|
if (StringUtil.isEmpty(refResource)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strategy == RuleConstant.STRATEGY_RELATE) {
|
||||||
|
return ClusterBuilderSlot.getClusterNode(refResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strategy == RuleConstant.STRATEGY_CHAIN) {
|
||||||
|
if (!refResource.equals(context.getName())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
// No node.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean filterOrigin(String origin) {
|
||||||
|
// Origin cannot be `default` or `other`.
|
||||||
|
return !RuleConstant.LIMIT_APP_DEFAULT.equals(origin) && !RuleConstant.LIMIT_APP_OTHER.equals(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Node selectNodeByRequesterAndStrategy(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node) {
|
||||||
|
// The limit app should not be empty.
|
||||||
|
String limitApp = rule.getLimitApp();
|
||||||
|
int strategy = rule.getStrategy();
|
||||||
|
String origin = context.getOrigin();
|
||||||
|
|
||||||
|
if (limitApp.equals(origin) && filterOrigin(origin)) {
|
||||||
|
if (strategy == RuleConstant.STRATEGY_DIRECT) {
|
||||||
|
// Matches limit origin, return origin statistic node.
|
||||||
|
return context.getOriginNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectReferenceNode(rule, context, node);
|
||||||
|
} else if (RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp)) {
|
||||||
|
if (strategy == RuleConstant.STRATEGY_DIRECT) {
|
||||||
|
// Return the cluster node.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FlowRuleChecker() {}
|
||||||
|
}
|
||||||
|
|
@ -26,14 +26,10 @@ import java.util.concurrent.TimeUnit;
|
||||||
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
|
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
|
||||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
import com.alibaba.csp.sentinel.context.Context;
|
|
||||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
|
||||||
import com.alibaba.csp.sentinel.node.metric.MetricTimerListener;
|
import com.alibaba.csp.sentinel.node.metric.MetricTimerListener;
|
||||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController;
|
import com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController;
|
import com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController;
|
||||||
|
|
@ -118,7 +114,7 @@ public class FlowRuleManager {
|
||||||
rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
|
rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller rater = new DefaultController(rule.getCount(), rule.getGrade());
|
TrafficShapingController rater = new DefaultController(rule.getCount(), rule.getGrade());
|
||||||
if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS
|
if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS
|
||||||
&& rule.getControlBehavior() == RuleConstant.CONTROL_BEHAVIOR_WARM_UP
|
&& rule.getControlBehavior() == RuleConstant.CONTROL_BEHAVIOR_WARM_UP
|
||||||
&& rule.getWarmUpPeriodSec() > 0) {
|
&& rule.getWarmUpPeriodSec() > 0) {
|
||||||
|
|
@ -151,16 +147,8 @@ public class FlowRuleManager {
|
||||||
return newRuleMap;
|
return newRuleMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count)
|
static Map<String, List<FlowRule>> getFlowRules() {
|
||||||
throws BlockException {
|
return flowRules;
|
||||||
List<FlowRule> rules = flowRules.get(resource.getName());
|
|
||||||
if (rules != null) {
|
|
||||||
for (FlowRule rule : rules) {
|
|
||||||
if (!rule.passCheck(context, node, count)) {
|
|
||||||
throw new FlowException(rule.getLimitApp());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasConfig(String resource) {
|
public static boolean hasConfig(String resource) {
|
||||||
|
|
@ -232,6 +220,8 @@ public class FlowRuleManager {
|
||||||
return rule.getWarmUpPeriodSec() > 0;
|
return rule.getWarmUpPeriodSec() > 0;
|
||||||
case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:
|
case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:
|
||||||
return rule.getMaxQueueingTimeMs() > 0;
|
return rule.getMaxQueueingTimeMs() > 0;
|
||||||
|
case RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:
|
||||||
|
return rule.getWarmUpPeriodSec() > 0 && rule.getMaxQueueingTimeMs() > 0;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,45 +15,64 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel.slots.block.flow;
|
package com.alibaba.csp.sentinel.slots.block.flow;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.context.Context;
|
import com.alibaba.csp.sentinel.context.Context;
|
||||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
||||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Combined the runtime statistics collected from the previous
|
* Combined the runtime statistics collected from the previous
|
||||||
* slots(NodeSelectorSlot, ClusterNodeBuilderSlot, and StatistcSlot), FlowSlot
|
* slots (NodeSelectorSlot, ClusterNodeBuilderSlot, and StatisticSlot), FlowSlot
|
||||||
* will use pre-set rules to decide whether the incoming requests should be
|
* will use pre-set rules to decide whether the incoming requests should be
|
||||||
* blocked.
|
* blocked.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* {@code SphU.entry (resourceName) }will throw FlowException if any rule is
|
* <p>
|
||||||
* triggered. user can customize his own logic by catching FlowException.
|
* {@code SphU.entry(resourceName)} will throw {@code FlowException} if any rule is
|
||||||
|
* triggered. Users can customize their own logic by catching {@code FlowException}.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* One resource can have multiple flow rules. FlowSlot traverses these rules
|
* One resource can have multiple flow rules. FlowSlot traverses these rules
|
||||||
* until one of them is triggered or all rules have been traversed.
|
* until one of them is triggered or all rules have been traversed.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* Each FlowRule is mainly composed of the 2 factors: grade, strategy, path; we
|
* <p>
|
||||||
|
* Each {@link FlowRule} is mainly composed of these factors: grade, strategy, path. We
|
||||||
* can combine these factors to achieve different effects.
|
* can combine these factors to achieve different effects.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* The grade is defined by the grade field in FlowRule. Here, 0 for thread
|
* <p>
|
||||||
* isolation and 1 for request count shaping. Both thread count and request
|
* The grade is defined by the {@code grade} field in {@link FlowRule}. Here, 0 for thread
|
||||||
|
* isolation and 1 for request count shaping (QPS). Both thread count and request
|
||||||
* count are collected in real runtime, and we can view these statistics by
|
* count are collected in real runtime, and we can view these statistics by
|
||||||
* following command: {@code
|
* following command:
|
||||||
* curl http:// localhost:8719 / tree?type = root`
|
* </p>
|
||||||
* idx id thread pass blocked success total aRt 1m-pass 1m-block 1m-all exeption
|
|
||||||
* 2 abc647 0 460 46 46 1 27 630 276 897 0
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* Thread for the count of threads that is currently processing the resource;
|
* <pre>
|
||||||
* pass for the count of incoming request within one second; blocked for the
|
* curl http://localhost:8719/tree
|
||||||
* count of requests blocked within one second; success for the count of the
|
*
|
||||||
* requests successfully within one second; RT for the average response time of
|
* idx id thread pass blocked success total aRt 1m-pass 1m-block 1m-all exception
|
||||||
* the requests within a second; total for the sum of incoming requests and
|
* 2 abc647 0 460 46 46 1 27 630 276 897 0
|
||||||
* blocked requests within one second; 1m-pass is for the count of incoming
|
* </pre>
|
||||||
* requests within one minute; 1m-block is for the count of a request blocked
|
*
|
||||||
* within one minute; 1m -all is the total of incoming and blocked requests
|
* <ul>
|
||||||
* within 1 minute; exception is for the count of exceptions in one second.
|
* <li>{@code thread} for the count of threads that is currently processing the resource</li>
|
||||||
|
* <li>{@code pass} for the count of incoming request within one second</li>
|
||||||
|
* <li>{@code blocked} for the count of requests blocked within one second</li>
|
||||||
|
* <li>{@code success} for the count of the requests successfully handled by Sentinel within one second</li>
|
||||||
|
* <li>{@code RT} for the average response time of the requests within a second</li>
|
||||||
|
* <li>{@code total} for the sum of incoming requests and blocked requests within one second</li>
|
||||||
|
* <li>{@code 1m-pass} is for the count of incoming requests within one minute</li>
|
||||||
|
* <li>{@code 1m-block} is for the count of a request blocked within one minute</li>
|
||||||
|
* <li>{@code 1m-all} is the total of incoming and blocked requests within one minute</li>
|
||||||
|
* <li>{@code exception} is for the count of business (customized) exceptions in one second</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* This stage is usually used to protect resources from occupying. If a resource
|
* This stage is usually used to protect resources from occupying. If a resource
|
||||||
* takes long time to finish, threads will begin to occupy. The longer the
|
* takes long time to finish, threads will begin to occupy. The longer the
|
||||||
|
|
@ -71,57 +90,79 @@ import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||||
* The benefit of using thread pool is that, it can walk away gracefully when
|
* The benefit of using thread pool is that, it can walk away gracefully when
|
||||||
* time out. But it also bring us the cost of context switch and additional
|
* time out. But it also bring us the cost of context switch and additional
|
||||||
* threads. If the incoming requests is already served in a separated thread,
|
* threads. If the incoming requests is already served in a separated thread,
|
||||||
* for instance, a servelet request, it will almost double the threads count if
|
* for instance, a Servlet HTTP request, it will almost double the threads count if
|
||||||
* using thread pool.
|
* using thread pool.
|
||||||
*
|
*
|
||||||
* ### QPS Shaping ### When qps exceeds the threshold, we will take actions to
|
* <h3>Traffic Shaping</h3>
|
||||||
* control the incoming request, and is configured by "controlBehavior" field in
|
* <p>
|
||||||
* flowrule
|
* When QPS exceeds the threshold, Sentinel will take actions to control the incoming request,
|
||||||
*
|
* and is configured by {@code controlBehavior} field in flow rules.
|
||||||
* 1. immediately reject(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
|
* </p>
|
||||||
*
|
* <ol>
|
||||||
|
* <li>Immediately reject ({@code RuleConstant.CONTROL_BEHAVIOR_DEFAULT})</li>
|
||||||
|
* <p>
|
||||||
* This is the default behavior. The exceeded request is rejected immediately
|
* This is the default behavior. The exceeded request is rejected immediately
|
||||||
* and the FlowException is thrown
|
* and the FlowException is thrown
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* 2. Warmup(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)
|
* <li>Warmup ({@code RuleConstant.CONTROL_BEHAVIOR_WARM_UP})</li>
|
||||||
*
|
* <p>
|
||||||
* If the usage of system has been low for a while, and a large amount of
|
* If the load of system has been low for a while, and a large amount of
|
||||||
* requests comes, the system might not be able to handle all these requests at
|
* requests comes, the system might not be able to handle all these requests at
|
||||||
* once. However if we steady increase the incoming request, the system can warm
|
* once. However if we steady increase the incoming request, the system can warm
|
||||||
* up and finally be able to handle all the requests.If the usage of system has
|
* up and finally be able to handle all the requests.
|
||||||
* been low for a while, and a large amount of requests comes, the system might
|
* This warmup period can be configured by setting the field {@code warmUpPeriodSec} in flow rules.
|
||||||
* not be able to handle all these requests at once. However if we steady
|
* </p>
|
||||||
* increase the incoming request, the system can warm up and finally be able to
|
|
||||||
* handle all the requests. This warmup period can be configured by setting the
|
|
||||||
* field "warmUpPeriodSec" in flow rule.
|
|
||||||
*
|
*
|
||||||
* 3.Rate limiter(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER) This strategy
|
* <li>Uniform Rate Limiting ({@code RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER})</li>
|
||||||
* strictly controls the interval between requests. In other words, it allows
|
* <p>
|
||||||
* requests to pass at a stable rate.
|
* This strategy strictly controls the interval between requests.
|
||||||
* <img src="https://github.com/alibaba/Sentinel/wiki/image/queue.gif" width=
|
* In other words, it allows requests to pass at a stable, uniform rate.
|
||||||
* "300" height="200" /> This strategy is an implement of leaky bucket
|
* </p>
|
||||||
* (https://en.wikipedia.org/wiki/Leaky_bucket). It is used to handle the
|
* <img src="https://raw.githubusercontent.com/wiki/alibaba/Sentinel/image/uniform-speed-queue.png" style="max-width:
|
||||||
* request at a stable rate and is often used in burst traffic. For instance,
|
* 60%;"/>
|
||||||
* Message. When a large number of requests beyond the system’s capacity arrive
|
* <p>
|
||||||
|
* This strategy is an implement of <a href="https://en.wikipedia.org/wiki/Leaky_bucket">leaky bucket</a>.
|
||||||
|
* It is used to handle the request at a stable rate and is often used in burst traffic (e.g. message handling).
|
||||||
|
* When a large number of requests beyond the system’s capacity arrive
|
||||||
* at the same time, the system using this strategy will handle requests and its
|
* at the same time, the system using this strategy will handle requests and its
|
||||||
* fixed rate until all the requests have been processed or time out.
|
* fixed rate until all the requests have been processed or time out.
|
||||||
|
* </p>
|
||||||
|
* </ol>
|
||||||
*
|
*
|
||||||
* @author jialiang.linjl
|
* @author jialiang.linjl
|
||||||
|
* @author Eric Zhao
|
||||||
*/
|
*/
|
||||||
public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
|
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
|
||||||
throws Throwable {
|
throws Throwable {
|
||||||
|
checkFlow(resourceWrapper, context, node, count);
|
||||||
FlowRuleManager.checkFlow(resourceWrapper, context, node, count);
|
|
||||||
|
|
||||||
fireEntry(context, resourceWrapper, node, count, args);
|
fireEntry(context, resourceWrapper, node, count, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count) throws BlockException {
|
||||||
|
// Flow rule map cannot be null.
|
||||||
|
Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRules();
|
||||||
|
|
||||||
|
List<FlowRule> rules = flowRules.get(resource.getName());
|
||||||
|
if (rules != null) {
|
||||||
|
for (FlowRule rule : rules) {
|
||||||
|
if (!canPassCheck(rule, context, node, count)) {
|
||||||
|
throw new FlowException(rule.getLimitApp());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean canPassCheck(FlowRule rule, Context context, DefaultNode node, int count) {
|
||||||
|
return FlowRuleChecker.passCheck(rule, context, node, count);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
|
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
|
||||||
fireExit(context, resourceWrapper, count, args);
|
fireExit(context, resourceWrapper, count, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,18 @@ package com.alibaba.csp.sentinel.slots.block.flow;
|
||||||
import com.alibaba.csp.sentinel.node.Node;
|
import com.alibaba.csp.sentinel.node.Node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A universal interface for traffic shaping controller.
|
||||||
|
*
|
||||||
* @author jialiang.linjl
|
* @author jialiang.linjl
|
||||||
*/
|
*/
|
||||||
public interface Controller {
|
public interface TrafficShapingController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether given resource entry can pass with provided count.
|
||||||
|
*
|
||||||
|
* @param node resource node
|
||||||
|
* @param acquireCount count to acquire
|
||||||
|
* @return true if the resource entry can pass; false if it should be blocked
|
||||||
|
*/
|
||||||
boolean canPass(Node node, int acquireCount);
|
boolean canPass(Node node, int acquireCount);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -17,18 +17,19 @@ package com.alibaba.csp.sentinel.slots.block.flow.controller;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.node.Node;
|
import com.alibaba.csp.sentinel.node.Node;
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.Controller;
|
import com.alibaba.csp.sentinel.slots.block.flow.TrafficShapingController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Default throttling controller (immediately reject strategy).
|
||||||
|
*
|
||||||
* @author jialiang.linjl
|
* @author jialiang.linjl
|
||||||
*/
|
*/
|
||||||
public class DefaultController implements Controller {
|
public class DefaultController implements TrafficShapingController {
|
||||||
|
|
||||||
double count = 0;
|
private double count;
|
||||||
int grade = 0;
|
private int grade;
|
||||||
|
|
||||||
public DefaultController(double count, int grade) {
|
public DefaultController(double count, int grade) {
|
||||||
super();
|
|
||||||
this.count = count;
|
this.count = count;
|
||||||
this.grade = grade;
|
this.grade = grade;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ package com.alibaba.csp.sentinel.slots.block.flow.controller;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.Controller;
|
import com.alibaba.csp.sentinel.slots.block.flow.TrafficShapingController;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.util.TimeUtil;
|
import com.alibaba.csp.sentinel.util.TimeUtil;
|
||||||
import com.alibaba.csp.sentinel.node.Node;
|
import com.alibaba.csp.sentinel.node.Node;
|
||||||
|
|
@ -25,7 +25,7 @@ import com.alibaba.csp.sentinel.node.Node;
|
||||||
/**
|
/**
|
||||||
* @author jialiang.linjl
|
* @author jialiang.linjl
|
||||||
*/
|
*/
|
||||||
public class RateLimiterController implements Controller {
|
public class RateLimiterController implements TrafficShapingController {
|
||||||
|
|
||||||
private final int maxQueueingTimeMs;
|
private final int maxQueueingTimeMs;
|
||||||
private final double count;
|
private final double count;
|
||||||
|
|
|
||||||
|
|
@ -19,17 +19,17 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.util.TimeUtil;
|
import com.alibaba.csp.sentinel.util.TimeUtil;
|
||||||
import com.alibaba.csp.sentinel.node.Node;
|
import com.alibaba.csp.sentinel.node.Node;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.Controller;
|
import com.alibaba.csp.sentinel.slots.block.flow.TrafficShapingController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The principle idea comes from guava. However, the calculation of guava is
|
* <p>
|
||||||
* rate-based, which means that we need to translate rate to qps.
|
* The principle idea comes from Guava. However, the calculation of Guava is
|
||||||
*
|
* rate-based, which means that we need to translate rate to QPS.
|
||||||
* https://github.com/google/guava/blob/master/guava/src/com/google/common/util/concurrent/SmoothRateLimiter.java
|
* </p>
|
||||||
*
|
*
|
||||||
* Requests arriving at the pulse may drag down long idle systems even though it
|
* Requests arriving at the pulse may drag down long idle systems even though it
|
||||||
* has a much larger handling capability in stable period. It usually happens in
|
* has a much larger handling capability in stable period. It usually happens in
|
||||||
* scenarios that require extra time for initialization, for example, db
|
* scenarios that require extra time for initialization, e.g. DB
|
||||||
* establishes a connection; connects to a remote service, and so on.
|
* establishes a connection; connects to a remote service, and so on.
|
||||||
*
|
*
|
||||||
* That’s why we need “warm up”.
|
* That’s why we need “warm up”.
|
||||||
|
|
@ -61,7 +61,7 @@ import com.alibaba.csp.sentinel.slots.block.flow.Controller;
|
||||||
*
|
*
|
||||||
* @author jialiang.linjl
|
* @author jialiang.linjl
|
||||||
*/
|
*/
|
||||||
public class WarmUpController implements Controller {
|
public class WarmUpController implements TrafficShapingController {
|
||||||
|
|
||||||
protected double count;
|
protected double count;
|
||||||
private int coldFactor;
|
private int coldFactor;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue