美文网首页技术干货
Sentinel之插槽链的设计分析

Sentinel之插槽链的设计分析

作者: 橘子_好多灰 | 来源:发表于2018-12-16 16:27 被阅读0次

    一、Overview

    在 Sentinel 里面,所有的资源都对应一个资源名称以及一个 Entry,Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 API 显式创建;每一个 Entry 创建的时候,同时也会创建一系列功能插槽(slot chain)。
    总体框架如图:

    总体框架图(来源于网络)

    二、源码分析

    资源获取对应Entry会在CtSph类中完成,在CtSph中也完成了对SlotChain的初始化。


    chain

    主要看lookProcessChain方法。

    ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
            //1、先从chainMap获取,若是存在,则直接返回
            ProcessorSlotChain chain = chainMap.get(resourceWrapper);
            if (chain == null) {
                synchronized (LOCK) {
                    chain = chainMap.get(resourceWrapper);
                    if (chain == null) {
                        // Entry size limit.
                        if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
                            return null;
                        }
                        //2、通过SlotChainProvider获取slotChain
                        chain = SlotChainProvider.newSlotChain();
                        Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
                            chainMap.size() + 1);
                        newMap.putAll(chainMap);
                        //3、添加到map中
                        newMap.put(resourceWrapper, chain);
                        chainMap = newMap;
                    }
                }
            }
            return chain;
        }
    

    接着看SlotChainProvider

         private static volatile SlotChainBuilder builder = null;
          
        //SPI机制获取实现了SlotChainBuilder的类 
        private static final ServiceLoader<SlotChainBuilder> LOADER = ServiceLoader.load(SlotChainBuilder.class);
    
    public static ProcessorSlotChain newSlotChain() {
            //1、首次加载时builder为空
            if (builder != null) {
                return builder.build();
            }
    
            //2、调用resolveSlotChainBuilder
            resolveSlotChainBuilder();
    
            if (builder == null) {
                RecordLog.warn("[SlotChainProvider] Wrong state when resolving slot chain builder, using default");
                builder = new DefaultSlotChainBuilder();
            }
            return builder.build();
        }
    
        private static void resolveSlotChainBuilder() {
            List<SlotChainBuilder> list = new ArrayList<SlotChainBuilder>();
            boolean hasOther = false;
            //循环加载
            for (SlotChainBuilder builder : LOADER) {
                //获取实现其他的链类
                if (builder.getClass() != DefaultSlotChainBuilder.class) {
                    hasOther = true;
                    list.add(builder);
                }
            }
            if (hasOther) {
                //取第一个
                builder = list.get(0);
            } else {
                // No custom builder, using default.
                builder = new DefaultSlotChainBuilder();
            }
    
            RecordLog.info("[SlotChainProvider] Global slot chain builder resolved: "
                + builder.getClass().getCanonicalName());
        }
    

    实现SlotChainBuilder的类有两个:DefaultSlotChainBuilder和HotParamSlotChainBuilder,DefaultSlotChainBuilder是默认SlotChainBuilder,
    HotParamSlotChainBuilder是热点参数Builder,比DefaultSlotChainBuilder多了一个ParamFlowSlot插槽,如果使用热点参数即使用这个插槽。

    Sentinel 将 SlotChainBuilder 作为 SPI 接口进行扩展,使得 Slot Chain 具备了扩展的能力。用户可以自行加入自定义的 slot 并编排 slot 间的顺序,从而可以给 Sentinel 添加自定义的功能。

    SlotChainProvider是用来获取ProcessorSlotChain的,是通过SlotChainBuilder的build方法实现的。

    HotParamSlotChainBuilder

    image.png

    下面具体分析:

    第一步

    初始化DefaultProcessorSlotChain的类。

    public class DefaultProcessorSlotChain extends ProcessorSlotChain {
          //初始化一个first节点,AbstractLinkedProcessorSlot有个next节点属性
        AbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {
    
            @Override
            public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)
                throws Throwable {
                super.fireEntry(context, resourceWrapper, t, count, prioritized, args);
            }
    
            @Override
            public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
                super.fireExit(context, resourceWrapper, count, args);
            }
    
        };
          //赋值end节点
        AbstractLinkedProcessorSlot<?> end = first;
    
        //下面代码省略
    }
    

    这个时候slotChain的构造如下:


    soltChain

    first和end都为DefaultProcessorSlotChain,next为空。
    第二步
    chain.addLast(new NodeSelectorSlot());
    调用的addLast方法

    @Override
        public void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {
            //设置next链
            end.setNext(protocolProcessor);
          //重新设置end
            end = protocolProcessor;
        }
    

    这个时候slotChain的构造如下:


    slotchain

    接下来继续构建,最后SoltChain链如下:


    soltChain

    这就是整个插槽链的构造过程了。

    三、SoltChain介绍

    • NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结-构存储起来,用于根据调用路径来限流降级;
    • ClusterBuilderSlot 则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;
    • StatisticSlot 则用于记录、统计不同纬度的 runtime 指标监控信息;
    • LogSlot 则用于记录blockException信息的日志信息,会写入的日志文件中;
    • ParamFlowSlot 则用于根据热点参数进行限流控制的;
    • SystemSlot 则通过系统的状态,例如 load1 等,来控制总的入口流量;
    • AuthoritySlot 则根据配置的黑白名单和调用来源信息,来做黑白名单控制;
    • FlowSlot 则用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制;
    • DegradeSlot 则通过统计信息以及预设的规则,来做熔断降级;

    后续会详细介绍每个插槽的功能。

    四、我的总结

    1、本文介绍的插槽的初始化过程链的构造过程;
    2、插槽链的构造中用到的是设计模式中的责任链模式;
    3、sentinel默认实现了两个SlotChainBuilder,通常情况下使用的默认的DefaultSlotChainBuilder;


    以上内容,如有不当之处,请指正

    相关文章

      网友评论

        本文标题:Sentinel之插槽链的设计分析

        本文链接:https://www.haomeiwen.com/subject/dteahqtx.html