首先我们看一下Sentinel项目的整个结构:version:1.6.0
* sentinel-core 核心模块,限流、降级、系统保护等都在这里实现
* sentinel-dashboard 控制台模块,可以对连接上的sentinel客户端实现可视化的管理
* sentinel-transport 传输模块,提供了基本的监控服务端和客户端的API接口,以及一些基于不同库的实现
* sentinel-extension 扩展模块,主要对DataSource进行了部分扩展实现
* sentinel-adapter 适配器模块,主要实现了对一些常见框架的适配
* sentinel-demo 样例模块,可参考怎么使用sentinel进行限流、降级等
* sentinel-benchmark 基准测试模块,对核心代码的精确性提供基准测试
底层原理:sentinel 入口是 SphU.entry创建
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
throws BlockException {
Context context = ContextUtil.getContext();
if (context instanceof NullContext) {
// The {@link NullContext} indicates that the amount of context has exceeded the threshold,
// so here init the entry only. No rule checking will be done.
return new CtEntry(resourceWrapper, null, context);
}
if (context == null) {
// Using default context.
context = MyContextUtil.myEnter(Constants.CONTEXT\_DEFAULT\_NAME, "", resourceWrapper.getType());
}
// Global switch is close, no rule checking will do.
if (!Constants.ON) {
return new CtEntry(resourceWrapper, null, context);
}
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
/\*
\* Means amount of resources (slot chain) exceeds {@link Constants.MAX\_SLOT\_CHAIN\_SIZE},
\* so no rule checking will be done.
\*/
if (chain == null) {
return new CtEntry(resourceWrapper, null, context);
}
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
e.exit(count, args);
throw e1;
} catch (Throwable e1) {
// This should not happen, unless there are errors existing in Sentinel internal.
RecordLog.info("Sentinel unexpected exception", e1);
}
return e;
}
这个方法可以分为以下几个部分:
* 1.对参数和全局配置项做检测,如果不符合,不会再进行后面的限流检测,否则进入下面的检测流程。
* 2.根据包装过的资源对象获取对应的SlotChain
* 3.执行SlotChain的entry方法
* 3.1.如果SlotChain的entry方法抛出了BlockException,则将该异常继续向上抛出
* 3.2.如果SlotChain的entry方法正常执行了,则最后会将该entry对象返回
* 4.如果上层方法捕获了BlockException,则说明请求被限流了,否则请求能正常执行
其中比较重要的是第2、3两个步骤,我们来分解一下这两个步骤。创建SlotChain默认的实现类为 DefaultProcessorSlotChain:链表实现的责任链
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
throws BlockException {
Context context = ContextUtil.getContext();
if (context instanceof NullContext) {
// The {@link NullContext} indicates that the amount of context has exceeded the threshold,
// so here init the entry only. No rule checking will be done.
return new CtEntry(resourceWrapper, null, context);
}
if (context == null) {
// Using default context.
context = MyContextUtil.myEnter(Constants.CONTEXT\_DEFAULT\_NAME, "", resourceWrapper.getType());
}
// Global switch is close, no rule checking will do.
if (!Constants.ON) {
return new CtEntry(resourceWrapper, null, context);
}
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
/\*
\* Means amount of resources (slot chain) exceeds {@link Constants.MAX\_SLOT\_CHAIN\_SIZE},
\* so no rule checking will be done.
\*/
if (chain == null) {
return new CtEntry(resourceWrapper, null, context);
}
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
e.exit(count, args);
throw e1;
} catch (Throwable e1) {
// This should not happen, unless there are errors existing in Sentinel internal.
RecordLog.info("Sentinel unexpected exception", e1);
}
return e;
}
* NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;
* ClusterBuilderSlot 则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;
* StatistcSlot 则用于记录,统计不同纬度的 runtime 信息;
* FlowSlot 则用于根据预设的限流规则,以及前面 slot 统计的状态,来进行限流;
* AuthorizationSlot 则根据黑白名单,来做黑白名单控制;
* DegradeSlot 则通过统计信息,以及预设的规则,来做熔断降级;
* SystemSlot 则通过系统的状态,例如 load1 等,来控制总的入口流量;
*
执行完业务逻辑处理后,调用了fireEntry()方法,由此触发了下一个节点的entry方法。此时我们就知道了sentinel的责任链就是这样传递的:每个Slot节点执行完自己的业务后,会调用fireEntry来触发下一个节点的entry方法。
网友评论