0. 想要个半个月的旅游
最近发现算法比较有意思一些,什么企业框架都是看不完的…
书接 FlowSlot
1. FlowRuleChecker.checkFlow() : 配置的规则校验类
sentinel 并没有对这个Checker进行抽象的设计,第一次看有些别扭…
package com.alibaba.csp.sentinel.slots.block.flow;
/**
* Rule checker for flow control rules.
*/
public class FlowRuleChecker {
public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource,
Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {
if (ruleProvider == null || resource == null) {
return;
}
// 根据给定的资源名,获取对应的规则(即 DefaultController、RateLimiteController这些)
Collection<FlowRule> rules = ruleProvider.apply(resource.getName());
if (rules != null) {
for (FlowRule rule : rules) {
// step into ...
if (!canPassCheck(rule, context, node, count, prioritized)) {
throw new FlowException(rule.getLimitApp(), rule);
}
}
}
}
public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node,
int acquireCount) {
// step into ...
return canPassCheck(rule, context, node, acquireCount, false);
}
public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount,
boolean prioritized) {
String limitApp = rule.getLimitApp();
if (limitApp == null) {
return true;
}
// 支持两种流控模式: 集群、本地
if (rule.isClusterMode()) {
return passClusterCheck(rule, context, node, acquireCount, prioritized);
}
// step into ...
return passLocalCheck(rule, context, node, acquireCount, prioritized);
}
private static boolean passLocalCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount,
boolean prioritized) {
// step into ...
// 根据请求(指定的调用者)+策略 选择需要流控的节点实例
Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node);
if (selectedNode == null) {
return true;
}
// step into ...
// 调用TrafficShapingController实现类的流控算法
return rule.getRater().canPass(selectedNode, acquireCount, prioritized);
}
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();
// 如果规则指定的调用者(针对来源)非 default、其他
if (limitApp.equals(origin) && filterOrigin(origin)) {
// 直接策略: 直接对来源节点进行流控
if (strategy == RuleConstant.STRATEGY_DIRECT) {
// Matches limit origin, return origin statistic node.
return context.getOriginNode();
}
// step into ...
// 反之则是 关联、链路 管控
return selectReferenceNode(rule, context, node);
}
// 如果规则未指定的调用者(针对来源),即 default
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 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 selectReferenceNode(FlowRule rule, Context context, DefaultNode node) {
String refResource = rule.getRefResource();
int strategy = rule.getStrategy();
if (StringUtil.isEmpty(refResource)) {
return null;
}
// 可以看出来,这个方法不仅仅支持 关联模式(策略),还支持 链路模式
// 关联模式,取其 ClusterNode 集群节点
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;
}
}
2. 为了更加生动一些,这里补充一些dashboard的配置
- 资源名: resource的name
- 针对来源: 服务调用方,即 origin
- 阈值类型:即 grade 整型变量指代
- 单机阈值: 即 左边所选的 阈值类型 对应的阈值数值
- 是否集群: FlowRuleChecker的passLocalCheck()、passClusterCheck()提供支持,如果集群流控突然挂掉,将自动切换成本地的流控支持
- 流控模式:将决定访问流量的审计、管控的节点
- 流控效果:这里即我们前面提及的算法部分
实现 流控效果 的XxxController ,请移步 流控 随笔 0-算法
2.1 规则常量
package com.alibaba.csp.sentinel.slots.block;
public final class RuleConstant {
// 阈值类型
public static final int FLOW_GRADE_THREAD = 0;
public static final int FLOW_GRADE_QPS = 1;
// 降级配置
public static final int DEGRADE_GRADE_RT = 0;
/**
* Degrade by biz exception ratio in the current {@link IntervalProperty#INTERVAL} second(s).
*/
public static final int DEGRADE_GRADE_EXCEPTION_RATIO = 1;
/**
* Degrade by biz exception count in the last 60 seconds.
*/
public static final int DEGRADE_GRADE_EXCEPTION_COUNT = 2;
public static final int DEGRADE_DEFAULT_SLOW_REQUEST_AMOUNT = 5;
public static final int DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT = 5;
// 规则支持配置 黑白名单
public static final int AUTHORITY_WHITE = 0;
public static final int AUTHORITY_BLACK = 1;
// 流控策略(流控模式)
public static final int STRATEGY_DIRECT = 0;
public static final int STRATEGY_RELATE = 1;
public static final int STRATEGY_CHAIN = 2;
// 流控行为(流控效果)
public static final int CONTROL_BEHAVIOR_DEFAULT = 0;
public static final int CONTROL_BEHAVIOR_WARM_UP = 1;
public static final int CONTROL_BEHAVIOR_RATE_LIMITER = 2;
public static final int CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER = 3;
public static final int DEFAULT_BLOCK_STRATEGY = 0;
public static final int TRY_AGAIN_BLOCK_STRATEGY = 1;
public static final int TRY_UNTIL_SUCCESS_BLOCK_STRATEGY = 2;
public static final int DEFAULT_RESOURCE_TIMEOUT_STRATEGY = 0;
public static final int RELEASE_RESOURCE_TIMEOUT_STRATEGY = 1;
public static final int KEEP_RESOURCE_TIMEOUT_STRATEGY = 2;
// 针对特定的调用者的规则
public static final String LIMIT_APP_DEFAULT = "default";
public static final String LIMIT_APP_OTHER = "other";
public static final int DEFAULT_SAMPLE_COUNT = 2;
public static final int DEFAULT_WINDOW_INTERVAL_MS = 1000;
private RuleConstant() {}
}