zoukankan      html  css  js  c++  java
  • Sentinel笔记--Slotchain

     Sentinel 中的责任链模式

    Sentinel 中的 ProcessorSlot

    ProcessorSlot 直译就是处理器插槽,是 Sentinel 实现限流降级、熔断降级、系统自适应降级等功能的切入点。

    Sentinel 的核心骨架,将不同的 Slot 按照顺序串在一起(责任链模式),从而将不同的功能(限流、降级、系统保护)组合在一起。slot chain 其实可以分为两部分:统计数据构建部分(statistic)和判断部分(rule checking)。核心结构:

    sentinel-slot-chain

    目前的设计是 one slot chain per resource,因为某些 slot 是 per resource 的(比如 NodeSelectorSlot)。

      

    Sentinel 提供的 ProcessorSlot 可以分为两类,一类是辅助完成资源指标数据统计的切入点,一类是实现降级功能的切入点。

    辅助资源指标数据统计的 ProcessorSlot:
    • NodeSelectorSlot:为当前资源创建 DefaultNode,并且将 DefaultNode 赋值给 Context.curEntry.curNode;
      • 如果当前调用链路上只出现过一次 SphU#entry 的情况,将该 DefaultNode 添加到的 Context.entranceNode 的子节点,否则添加到 Context.curEntry.parent 的子节点(childList)。
    • ClusterBuilderSlot:如果当前资源未创建 ClusterNode,则为资源创建 ClusterNode;
      • 将 ClusterNode 赋值给当前资源的 DefaultNode.clusterNode;如果调用来源(origin)不为空,则为调用来源创建 StatisticNode,用于实现按调用来源统计资源的指标数据,ClusterNode 持有每个调用来源的 StatisticNode。
    • StatisticSlot:这是 Sentinel 最为重要的类之一,用于实现指标数据统计。先是调用后续的 ProcessorSlot#entry 判断是否放行请求,再根据判断结果进行相应的指标数据统计操作。

    这些辅助ProcessorSlot需要严格的顺序执行

    NodeSelectorSlot->ClusterBuilderSlot->StatisticSlot

     实现降级功能的 ProcessorSlot:
    • AuthoritySlot:实现黑白名单降级
    • SystemSlot:实现系统自适应降级
    • FlowSlot:实现限流降级
    • DegradeSlot:实现熔断降级 

    Sentinel 会为每个资源创建且仅创建一个 ProcessorSlotChain,只要名称相同就认为是同一个资源。ProcessorSlotChain 被缓存在 CtSph.chainMap 静态字段,key 为资源 ID.

     

    Sentinel 的整体工作流程

    如果不借助 Sentinel 提供的适配器,我们可以这样使用 Sentinel。 

    ContextUtil.enter("上下文名称,例如:sentinel_spring_web_context");
    Entry entry = null;
    try {
         entry = SphU.entry("资源名称,例如:/rpc/openfein/demo", EntryType.IN (或者 EntryType.OUT));
         // 执行业务方法
           return doBusiness();
    } catch (Exception e) {
         if (!(e instanceof BlockException)) {
              Tracer.trace(e);
         }
         throw e;
    } finally {
         if (entry != null) {
             entry.exit(1);
         }
         ContextUtil.exit();
    }

     此流程分5个部分

    1. 调用 ContextUtil#enter 方法;
    2. 调用 SphU#entry 方法;
    3. 如果抛出异常,且异常类型非 BlockException 异常,则调用 Tracer#trace 方法记录异常;
    4. 调用 Entry#exit 方法;
    5. 调用 ContextUtil#exit 方法。

     

    ContextUtil.enter流程

    ContextUtil#enter 方法负责为当前调用链路创建 Context,以及为 Conetxt 创建 EntranceNode
    private static volatile Map<String, DefaultNode> contextNameNodeMap = new HashMap<>();
    //资源入口,生成EntranceNode,如果没有Context的话,生成context并写入到ContextHolder中
    protected static Context trueEnter(String name, String origin) {
        Context context = contextHolder.get();
        if (context == null) {
            Map<String, DefaultNode> localCacheNameMap = contextNameNodeMap;
            DefaultNode node = localCacheNameMap.get(name);
            if (node == null) {
            //生成ResourceWrapper,生成EntranceNode
                node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null);
                // Add entrance node.
                Constants.ROOT.addChild(node);
                Map<String, DefaultNode> newMap = new HashMap<>(contextNameNodeMap.size() + 1);
                newMap.putAll(contextNameNodeMap);
                newMap.put(name, node);
                contextNameNodeMap = newMap;
            }
            context = new Context(node, name);
            context.setOrigin(origin);
            contextHolder.set(context);
        }
        return context;
    }

    SphU.entry流程

    Sentinel 的核心骨架是 ProcessorSlotChain,所以核心的流程是一次 SphU#entry 方法的调用以及一次 CtEntry#exit 方法的调用。
    • SphU#entry 方法调用 CtSph#entry 方法,
    • CtSph 负责为资源创建 ResourceWrapper 对象并为资源构造一个全局唯一的 ProcessorSlotChain、
    • 为资源创建 CtEntry 并将 CtEntry 赋值给当前调用链路的 Context.curEntry、
    • 最后调用 ProcessorSlotChain#entry 方法完成一次单向链表的 entry 方法调用
    public Entry entry(String name, EntryType type, int count, Object... args) throws BlockException {
       //生成resource
        StringResourceWrapper resource = new StringResourceWrapper(name, type);
        return entry(resource, count, args);
    }
    
    private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
        throws BlockException {
        Context context = ContextUtil.getContext();
        //核心,生成SlotChain
        ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
        //生成entry
        Entry e = new CtEntry(resourceWrapper, chain, context);
        //开始处理具体逻辑
        chain.entry(context, resourceWrapper, null, count, prioritized, args);
        return e;
    }
    
    //生成SlotChain,默认使用SPI机制,加载
    ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
        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;
                    }
    
                    chain = SlotChainProvider.newSlotChain();
                    Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
                        chainMap.size() + 1);
                    newMap.putAll(chainMap);
                    newMap.put(resourceWrapper, chain);
                    chainMap = newMap;
                }
            }
        }
        return chain;
    }

     SPI Slot加载顺序如下

    # Sentinel default ProcessorSlots
    com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot
    com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot
    com.alibaba.csp.sentinel.slots.logger.LogSlot
    com.alibaba.csp.sentinel.slots.statistic.StatisticSlot
    com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot
    com.alibaba.csp.sentinel.slots.system.SystemSlot
    com.alibaba.csp.sentinel.slots.block.flow.FlowSlot
    com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot
     

    Entry#exit流程

    protected void exitForContext(Context context, int count, Object... args) throws ErrorEntryFreeException {
        if (context != null) {
                //......
                // 1、调用 ProcessorSlotChain 的 exit 方法
                if (chain != null) {
                    chain.exit(context, resourceWrapper, count, args);
                }
                // 2、将当前 CtEntry 的父节点设置为 Context 的当前节点
                context.setCurEntry(parent);
                if (parent != null) {
                    ((CtEntry)parent).child = null;
                }
                // .....
        }
     }
     
    CtSph 在创建 CtEntry 时,将资源的 ProcessorSlotChain 赋值给了 CtEntry,所以在调用 CtEntry#exit 方法时,CtEntry 能够拿到当前资源的 ProcessorSlotChain,并调用 ProcessorSlotChain 的 exit 方法完成一次单向链表的 exit 方法调用。其过程与 ProcessorSlotChain 的一次 entry 方法的调用过程一样。
    CtEntry 在退出时还会还原 Context.curEntry。CtEntry 用于维护父子 Entry,每一次调用 SphU#entry 都会创建一个 CtEntry,如果应用处理一次请求的路径上会多次调用 SphU#entry,那么这些 CtEntry 会构成一个双向链表。在每次创建 CtEntry,都会将 Context.curEntry 设置为这个新的 CtEntry,双向链表的作用就是在调用 CtEntry#exit 方法时,能够将 Context.curEntry 还原为上一个 CtEntry。
     

    ContextUtil 的 exit 流程

    public static void exit() {
            Context context = contextHolder.get();
            if (context != null && context.getCurEntry() == null) {
                contextHolder.set(null);
            }
      }
     
    如果 Context.curEntry 为空,则说明所有 SphU#entry 都对应执行了一次 Entry#exit 方法,此时就可以将 Context 从 ThreadLocal 中移除。

    Plus:

    //所有树的根结点
    Constants.ROOT=new EntranceNode("machine-root",new ClusterNode("machine-root"));

    //为了SystemRule服务,用来统计全局metric时间窗口信息。
    Constants.ENTRY_NODE: new ClusterNode("__total_inbound_traffic__", 0)

    每次请求都会生成一个ResourceWrapper
    (res->ProcessorSlotChain)静态类,永驻内存。 最多6W个Chain,即最多6W个资源(接口)

    静态属性ContextUtil.contextNameNodeMap存储(contextName -> EntranceNode)
    静态属性CtSph.chainMap存储(res->ProcessorSlotChain)
    静态属性ClusterBuilderSlot.clusterNodeMap存储(res->clusterNode),
    实例属性clusterNode.originCountMap存储(origin->StatisticNode)


    默认Resource整个请求过程中,统计信息都放在clusterNode或者originNode中,
    具体统计计算是在StatisticNode中完成,rollingCounterInSecond只存1s数据,rollingCounterInMinute存储1分钟的数据。

    StatisticSlot

    StatisticSlot 是 Sentinel 最为重要的类之一,用于根据规则判断结果进行相应的统计操作。

    entry 的时候:依次执行后面的判断 slot。每个 slot 触发流控的话会抛出异常(BlockException 的子类)。若有 BlockException 抛出,则记录 block 数据;若无异常抛出则算作可通过(pass),记录 pass 数据。

    exit 的时候:若无 error(无论是业务异常还是流控异常),记录 complete(success)以及 RT,线程数-1。

    记录数据的维度:线程数+1、记录当前 DefaultNode 数据、记录对应的 originNode 数据(若存在 origin)、累计 IN 统计数据(若流量类型为 IN)。

  • 相关阅读:
    atitit.nfc 身份证 银行卡 芯片卡 解决方案 attilax总结
    atitit.php 流行框架 前三甲为:Laravel、Phalcon、Symfony2 attilax 总结
    Atitit.执行cmd 命令行 php
    Atitit. 图像处理jpg图片的压缩 清理垃圾图片 java版本
    atitit。企业组织与软件工程的策略 战略 趋势 原则 attilax 大总结
    atitit. 管理哲学 大毁灭 如何防止企业的自我毁灭
    Atitit.java的浏览器插件技术 Applet japplet attilax总结
    Atitit.jquery 版本新特性attilax总结
    Atitit. 软件开发中的管理哲学一个伟大的事业必然是过程导向为主 过程导向 vs 结果导向
    (转)获取手机的IMEI号
  • 原文地址:https://www.cnblogs.com/snow-man/p/15500847.html
Copyright © 2011-2022 走看看