sentinel设计思想

2020-03-06  本文已影响0人  自由人_ded7

首先我们看一下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方法。

上一篇下一篇

猜你喜欢

热点阅读