Sentinel之插槽链的设计分析

一、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;


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

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 小确幸:1.开卷有益2.参加线下活动3.下雨,下车就停了 怎样提高的对自己的认知呢?试想,我们都是如何知道自己长什...
    倪倪_NRY阅读 49评论 0 0
  • 在去接孩子的高速上,忽然听到收音机里传来的歌声,“就像孤独的鸵鸟努力在奔跑”这句歌词给我了莫大的感动,下了高...
    爱跑步的煎鱼片阅读 1,850评论 0 0
  • 白日里的光,越来越少 很快就没入了幽暗的湖里 我发现了你的降临 眼前,头顶乃至背后 在我的周围 你无处不在 我只得...
    悟实先生阅读 1,468评论 0 2
  • 好久没有见到那些曾经天天简单的人了,好久没有想起曾经那些凄美的回忆了。以后再也不像爱你那样爱别人了...
    雨中的泡沫阅读 4,626评论 0 0

友情链接更多精彩内容