本篇主要是讲解Sentinel限流逻辑的核心流转是怎么样的,不会过多涉及到具体的算法,更多的是在讨论主线,具体每个规则的后面会说到
先看下官网提供的简单例子,使用的是流控规则,代码如下,可以看到入口其实就是Sphu.entry这个地方,也是最核心的逻辑
public class SentinelHelloTest {
public static void main(String[] args) throws Exception{
// 配置规则.
initFlowRules();
while (true) {
// 1.5.0 版本开始可以直接利用 try-with-resources 特性
//SphU.entry进入资源,成功则执行内部逻辑
try (Entry entry = SphU.entry("HelloWorld")) {
Thread.sleep(10000 * 6);
// 被保护的逻辑
System.out.println("hello world");
} catch (BlockException ex) {
// 处理被流控的逻辑, 已经超出限制则抛出异常
System.out.println("blocked!" + ex.getMessage());
}
}
}
/**
* 初始化规则
*/
private static void initFlowRules(){
List rules = new ArrayList<>();
//限流规则
FlowRule rule = new FlowRule();
//资源名字
rule.setResource("HelloWorld");
//限制类型,有qps和线程数,这里是线程数
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
//限制1秒最多只能进20个请求
rule.setCount(20);
rules.add(rule);
//加载规则,在内存中进行维护的
FlowRuleManager.loadRules(rules);
}
}
//入口
//com.alibaba.csp.sentinel.SphU#entry(java.lang.String)
//接着到
//com.alibaba.csp.sentinel.CtSph#entry()
@Override
public Entry entry(String name, EntryType type, int count, Object... args) throws BlockException {
//对资源进行一个bao包装
StringResourceWrapper resource = new StringResourceWrapper(name, type);
//调用重载的方法
return entry(resource, count, args);
}
//最终会到这里来
//com.alibaba.csp.sentinel.CtSph#entryWithPriority(com.alibaba.csp.sentinel.slotchain.ResourceWrapper, int, boolean, java.lang.Object...)
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
throws BlockException {
//分支逻辑,判断Context是否为空
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 = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
}
//如果关闭则不会执行校验规则,默认是开启的
// Global switch is close, no rule checking will do.
if (!Constants.ON) {
return new CtEntry(resourceWrapper, null, context);
}
//核心方法,构建处理链表,封装了一条链,然后使用双向链表的方式连接起来
ProcessorSlot
构建链条的方法的方法,里面涉及到链条的构建与其对应的扩展
//com.alibaba.csp.sentinel.CtSph#lookProcessChain
ProcessorSlot
责任链设计模式调用入口,看这个只要了解这个设计模式基本就可以知道了,所以下面要去看一下ProcessorSlot的结构和其他几个slot基本的一个情况
public interface ProcessorSlot {
/**
* Entrance of this slot.
* 进入该slot
*/
void entry(Context context, ResourceWrapper resourceWrapper, T param, int count, boolean prioritized,
Object... args) throws Throwable;
/**
* Means finish of {@link #entry(Context, ResourceWrapper, Object, int, boolean, Object...)}.
*
* 循环进入slot
*/
void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized,
Object... args) throws Throwable;
/**
* Exit of this slot.
* 退出该slot
*/
void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
/**
* Means finish of {@link #exit(Context, ResourceWrapper, int, Object...)}.
* 循环退出slot
*/
void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
}
抽象的实现,后面其他的校验规则都会继承这个类,所以这个抽象类还是很重要的,来看一下他的大概结构有哪些内容
public abstract class AbstractLinkedProcessorSlot implements ProcessorSlot {
//下个slot
private AbstractLinkedProcessorSlot> next = null;
@Override
public void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
throws Throwable {
//下一个不为空,则进行调用
if (next != null) {
next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);
}
}
@SuppressWarnings("unchecked")
void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)
throws Throwable {
T t = (T)o;
entry(context, resourceWrapper, t, count, prioritized, args);
}
@Override
public void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
//下一个不为空,则进行调用
if (next != null) {
next.exit(context, resourceWrapper, count, args);
}
}
//对下一个的基本操作
public AbstractLinkedProcessorSlot> getNext() {
return next;
}
public void setNext(AbstractLinkedProcessorSlot> next) {
this.next = next;
}
}
默认的实现类,这是调用的源头,也是比较重要的,用类似于双向链表的思路构建了一条链,可以看看下面的逻辑
public class DefaultProcessorSlotChain extends ProcessorSlotChain {
//第一个slot
AbstractLinkedProcessorSlot> first = new AbstractLinkedProcessorSlot
其他的就是一些细项规则了,比如
FlowSlot:限流slot
LogSlot:日志slot
等等,具体的slot在com.alibaba.csp.sentinel.slotchain.ProcessorSlot文件中
# Sentinel default ProcessorSlots com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot com.alibaba.csp.sentinel.slots.logger.LogSlot com.alibaba.csp.sentinel.slots.statistic.StatisticSlot com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot com.alibaba.csp.sentinel.slots.system.SystemSlot com.alibaba.csp.sentinel.slots.block.flow.FlowSlot com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot至于前后顺序在每个slot上面会有@Spi(order = Constants.ORDER_STATISTIC_SLOT)进行指定,值越小就排的越前
接下来就是后面的调用处理了,如下
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
//责任链设计模式进入一个个slot调用
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
//责任链设计模式退出一个个slot调用
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;
返回的chain就是DefaultProcessorSlotChain,一调用它就会一个个的slot进行调用,从而实现拦截的功能,同理,调用e.exit就会一个个solt去调用对应的方法,直到结束
本篇文章只是记录整体的逻辑,这里使用的是责任链的设计模式,这个模式在很多地方都会用到,大家可以认真学习下这种思路,这篇文章就只讲到这里,后面的文章会再说下具体规则的实现与其校验