Sentinel之插槽链的设计分析

一、Overview

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

Sentinel之插槽链的设计分析_第1张图片
总体框架图(来源于网络)

二、源码分析

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


Sentinel之插槽链的设计分析_第2张图片
chain

主要看lookProcessChain方法。

ProcessorSlot 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 newMap = new HashMap(
                        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 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 list = new ArrayList();
        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

Sentinel之插槽链的设计分析_第3张图片
image.png

下面具体分析:

第一步

初始化DefaultProcessorSlotChain的类。

public class DefaultProcessorSlotChain extends ProcessorSlotChain {
      //初始化一个first节点,AbstractLinkedProcessorSlot有个next节点属性
    AbstractLinkedProcessorSlot first = new AbstractLinkedProcessorSlot() {

        @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的构造如下:


Sentinel之插槽链的设计分析_第4张图片
soltChain

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

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

这个时候slotChain的构造如下:


Sentinel之插槽链的设计分析_第5张图片
slotchain

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


Sentinel之插槽链的设计分析_第6张图片
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之插槽链的设计分析)