文章目录
原理图
官网的Slot链顺序存在问题,此处slot执行顺序同实际顺序
源码分析一入口CtSph.entryWithPriority
- 通过DefaultProcessorSlotChain调用责任链
- 所有的责任链工作完毕,发生限流则抛出异常
- 未限流返回Entry栈链表
注意: 同一个资源在不同ContextName的上下文中,DefaultNode不同;
注意: 每一个资源只有一个ClusterNode 但可以有多个DefaultNode
try {
每个ProcessorSlot 的entry()方法负责真正的业务处理部分
NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级
ClusterBuilderSlot 则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;
StatistcSlot 则用于记录,统计不同纬度的 runtime 信息;
FlowSlot 则用于根据预设的限流规则,以及前面 slot 统计的状态,来进行限流;
AuthorizationSlot 则根据黑白名单,来做黑白名单控制;
DegradeSlot 则通过统计信息,以及预设的规则,来做熔断降级;
SystemSlot 则通过系统的状态,例如 load1 等,来控制总的入口流量;
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
限流了,往上抛,这里exit了,而外界finally需要判空再exit
e.exit(count, args);
throw e1;
} catch (Throwable e1) {
防御性编程 用来处理异常
RecordLog.info("Sentinel unexpected exception", e1);
}
源码分析一NodeSelectorSlot
- 负责构建限流链路
- DefaultNode根据context.getName构建,也就是同一个资源可以对应多个DefaultNode
- 构建下图关系
-
public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {
private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
throws Throwable {
根据「上下文」的名称获取DefaultNode
多线程环境下,每个线程都会创建一个context,但是一个contextName可能用于多个线程上下文
DefaultNode node = map.get(context.getName());
if (node == null) {
synchronized (this) {
node = map.get(context.getName());
if (node == null) {
如果当前「上下文」中没有该节点,则创建一个DefaultNode节点
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作为「上下文」的最后一个节点的子节点添加进去
如果context的curEntry.parent.curNode为null,则添加到entranceNode中去
否则添加到context的curEntry.parent.curNode中去
((DefaultNode) context.getLastNode()).addChild(node);
}
}
}
context.setCurNode(node);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
}
源码分析一ClusterBuilderSlot
- 构建存储限流统计数据的clusterNode
- 将clusterNode设置到限流链路当前的DefaultNode
- 针对origin构建额外的StatisticNode
@SpiOrder(-9000)
public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
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 {
if (clusterNode == null) {
synchronized (lock) {
if (clusterNode == null) {
根据资源名构建clusterNode 也就是说一个资源一定对应且只能对应一个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;
}
}
}
node.setClusterNode(clusterNode);
如果context设置了Origin,则为origin创建单独的统计节点StatisticNode
Origin一般用于区分环境,或者应用名,ip等,具体维度不定,比如区分不同的应用,则构建applicationname1,applicationname2作为Origin
if (!"".equals(context.getOrigin())) {
Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin());
context.getCurEntry().setOriginNode(originNode);
}
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
}
源码分析一StatisticSlot
- 先调用下游限流节点
- 如果未被限流则执行数据统计计算工作
功能概述 |
---|
负责资源限流数据的构建 |
线程: entry时增加线程数,exit 减少线程数,【不同于hystrix,其并没有通过线程池进行隔离,而是通过数值统计当前被使用的线程数,上下文切换更少】 |
qps: 只负责增加,通过样本窗口大小进行限流处理,exit无需减少 |
@SpiOrder(-7000)
实时统计的处理槽
资源ID对应clusternode的统计
OriginNode对应的statisticsNode统计
EntryType.IN入站类型的额外统计
所有entranceNode的统计
public class StatisticSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
try {
【限流 降级 系统 授权等】规则检测 通过后正式统计
检测完毕进行统计
fireEntry(context, resourceWrapper, node, count, prioritized, args);
通过DefaultNode为clusterNode增加线程数
node.increaseThreadNum();
增加qps count一般是1
node.addPassRequest(count);
if (context.getCurEntry().getOriginNode() != null) {
设置origin则处理StatisticNode
context.getCurEntry().getOriginNode().increaseThreadNum();
context.getCurEntry().getOriginNode().addPassRequest(count);
}
if (resourceWrapper.getEntryType() == EntryType.IN) {
全局统计添加节点添加线程数 qps
Constants.ENTRY_NODE.increaseThreadNum();
Constants.ENTRY_NODE.addPassRequest(count);
}
通过限流后则执行回调
for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onPass(context, resourceWrapper, node, count, args);
}
} catch (PriorityWaitException ex) {
node.increaseThreadNum();
...... 删除其他代码
处理回调事件
for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onPass(context, resourceWrapper, node, count, args);
}
} catch (BlockException e) {
发生限流设置错误
context.getCurEntry().setBlockError(e);
被限流依旧添加qps
node.increaseBlockQps(count);
if (context.getCurEntry().getOriginNode() != null) {
context.getCurEntry().getOriginNode().increaseBlockQps(count);
}
if (resourceWrapper.getEntryType() == EntryType.IN) {
处理统计数据BlockQps
Constants.ENTRY_NODE.increaseBlockQps(count);
}
处理回调
for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onBlocked(e, context, resourceWrapper, node, count, args);
}
throw e;
} catch (Throwable e) {
context.getCurEntry().setError(e);
throw e;
}
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
Node node = context.getCurNode();
if (context.getCurEntry().getBlockError() == null) {
step-1 计算rt响应时长
long completeStatTime = TimeUtil.currentTimeMillis();
context.getCurEntry().setCompleteTimestamp(completeStatTime);
long rt = completeStatTime - context.getCurEntry().getCreateTimestamp();
Throwable error = context.getCurEntry().getError();
step-2 减少线程数 处理一些数据统计工作
recordCompleteFor(node, count, rt, error);
recordCompleteFor(context.getCurEntry().getOriginNode(), count, rt, error);
if (resourceWrapper.getEntryType() == EntryType.IN) {
recordCompleteFor(Constants.ENTRY_NODE, count, rt, error);
}
}
step-3 处理回调
Collection<ProcessorSlotExitCallback> exitCallbacks = StatisticSlotCallbackRegistry.getExitCallbacks();
for (ProcessorSlotExitCallback handler : exitCallbacks) {
handler.onExit(context, resourceWrapper, count, args);
}
fireExit(context, resourceWrapper, count);
}
private void recordCompleteFor(Node node, int batchCount, long rt, Throwable error) {
if (node == null) {
return;
}
node.addRtAndSuccess(rt, batchCount);
减少线程数
node.decreaseThreadNum();
if (error != null && !(error instanceof BlockException)) {
node.increaseExceptionQps(batchCount);
}
}
}
源码分析一SystemSlot
-通过SystemRuleManager检查系统限流
@SpiOrder(-5000)
public class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
检查系统规则是否触发限流 触发抛出异常
SystemRuleManager.checkSystem(resourceWrapper);
触发参数流控槽ParamFlowSlot执行
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
}
系统规则
检查维度 |
---|
qps限流 |
线程数限流 |
基于平均rt 限流 |
系统负载过高并且线程数超过1,并且线程数超过平均qps |
cpu使用率过高 |
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");
}
基于平均rt 限流
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) {
线程数超过1,并且线程数超过平均qps
if (!checkBbr(currentThread)) {
throw new SystemBlockException(resourceWrapper.getName(), "load");
}
}
cpu使用率过高
if (highestCpuUsageIsSet && getCurrentCpuUsage() > highestCpuUsage) {
throw new SystemBlockException(resourceWrapper.getName(), "cpu");
}
}
源码分析一ParamFlowSlot
public class ParamFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
热点参数规则管理器中不存在参数规则 不进行限流检查
if (!ParamFlowRuleManager.hasRules(resourceWrapper.getName())) {
fireEntry(context, resourceWrapper, node, count, prioritized, args);
return;
}
检查限流
checkFlow(resourceWrapper, count, args);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
}
参数限流检查checkFlow
- 根据资源名获取资源对应的限流规则
- 初始化参数Metric限流统计数据容器
- 如果检测不通过则抛出限流异常
- 具体的检测算法参见下一篇文章
void checkFlow(ResourceWrapper resourceWrapper, int count, Object... args) throws BlockException {
if (args == null) {
return;
}
if (!ParamFlowRuleManager.hasRules(resourceWrapper.getName())) {
return;
}
// 根据资源名获取资源对应的限流规则
List<ParamFlowRule> rules = ParamFlowRuleManager.getRulesOfResource(resourceWrapper.getName());
for (ParamFlowRule rule : rules) {
applyRealParamIdx(rule, args.length);
// 初始化参数Metric限流统计数据容器
ParameterMetricStorage.initParamMetricsFor(resourceWrapper, rule);
如果检测不通过则抛出限流异常
if (!ParamFlowChecker.passCheck(resourceWrapper, rule, count, args)) {
String triggeredParam = "";
if (args.length > rule.getParamIdx()) {
Object value = args[rule.getParamIdx()];
triggeredParam = String.valueOf(value);
}
throw new ParamFlowException(resourceWrapper.getName(), triggeredParam, rule);
}
}
}
源码分析一FlowSlot
- 获取资源对应的规则
- 通过规则检测器检测是否发生限流
- 具体限流算法参见下一篇
@SpiOrder(-2000)
public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
private final FlowRuleChecker checker;
@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);
}
}
源码分析一DegradeSlot
- 获取所有的熔断器规则
- 检测是否发生熔断
- 发生熔断则报错
- 具体算法参见下一篇
@SpiOrder(-1000)
public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
熔断检查
performChecking(context, resourceWrapper);
结尾节点无下一个节点,结束责任链工作
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
void performChecking(Context context, ResourceWrapper r) throws BlockException {
获取熔断器规则
List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
if (circuitBreakers == null || circuitBreakers.isEmpty()) {
return;
}
for (CircuitBreaker cb : circuitBreakers) {
未通过熔断检测则熔断
if (!cb.tryPass(context)) {
throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());
}
}
}
}
总结
- 概述了所有处理器槽的工作
- 其中日志槽和授权槽相对简答,没有分析源码,一个负责日志,一个负责黑白名单拦截
- 参数流控和资源流控以及熔断具体算法实现参见下一篇,包含了集群模式和单机模式的处理
- 整个责任链先处理簇点链路以及统计数据节点的构成,随后触发限流规则检查,最后在StatisticSlot处完成数据统计