首先咱们看一下Sentinel项目的整个结构:version:1.6.0app
* 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; }
这个方法能够分为如下几个部分:less
* 1.对参数和全局配置项作检测,若是不符合,不会再进行后面的限流检测,不然进入下面的检测流程。 * 2.根据包装过的资源对象获取对应的SlotChain * 3.执行SlotChain的entry方法 * 3.1.若是SlotChain的entry方法抛出了BlockException,则将该异常继续向上抛出 * 3.2.若是SlotChain的entry方法正常执行了,则最后会将该entry对象返回 * 4.若是上层方法捕获了BlockException,则说明请求被限流了,不然请求能正常执行
其中比较重要的是第二、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方法。ui