Sentinel进阶(二)

一、Sentinel的原理(源码分析)

1.1. 源码入口
Entry entry = SphU.entry(resourceName);

围绕两点展开:

  1. 寻找入口SphU.entry()
  2. 初始化流控规则的入口

自动装配:
在这里插入图片描述
最核心的配置类: com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration

  • SentinelWebAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
@ConditionalOnClass(SentinelWebInterceptor.class)
@EnableConfigurationProperties(SentinelProperties.class)
public class SentinelWebAutoConfiguration implements WebMvcConfigurer {

   @Autowired
   private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;
   
   @Override
	public void addInterceptors(InterceptorRegistry registry) {
		/**
	     * 定义拦截器
	     */
		if (!sentinelWebInterceptorOptional.isPresent()) {
			return;
		}
		SentinelProperties.Filter filterConfig = properties.getFilter();
		registry.addInterceptor(sentinelWebInterceptorOptional.get())
				.order(filterConfig.getOrder())
				.addPathPatterns(filterConfig.getUrlPatterns());
		log.info(
				"[Sentinel Starter] register SentinelWebInterceptor with urlPatterns: {}.",
				filterConfig.getUrlPatterns());
	}
}
  • SentinelWebInterceptor
public class SentinelWebInterceptor extends AbstractSentinelInterceptor {
}
  • AbstractSentinelInterceptor
public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {
    //请求进来之后拦截,首先进入这个方法
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        //由此可见,不管是api的方式还是 继承SpringBoot入口都是SphU.entry
        Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
    }
}
1.2. 初始化流控规则的入口

SPI 机制加载SentinelAutoConfiguration, 初始化流控规则的入口InitExecutor#doInit

在这里插入图片描述

public class SentinelAutoConfiguration {
    @PostConstruct
    private void init() {
        // earlier initialize
        if (properties.isEager()) {
             InitExecutor.doInit();
        }
    }
}

public static void doInit() {
     if (!initialized.compareAndSet(false, true)) {
         return;
     }
     try {
         ServiceLoader<InitFunc> loader = ServiceLoaderUtil.getServiceLoader(InitFunc.class);
         List<OrderWrapper> initList = new ArrayList<OrderWrapper>();
         for (InitFunc initFunc : loader) {
             RecordLog.info("[InitExecutor] Found init func: " + initFunc.getClass().getCanonicalName());
             insertSorted(initList, initFunc);
         }
         for (OrderWrapper w : initList) {
             w.func.init();
             RecordLog.info(String.format("[InitExecutor] Executing %s with order %d",
                 w.func.getClass().getCanonicalName(), w.order));
         }
     } catch (Exception ex) {
         RecordLog.warn("[InitExecutor] WARN: Initialization failed", ex);
         ex.printStackTrace();
     } catch (Error error) {
         RecordLog.warn("[InitExecutor] ERROR: Initialization failed with fatal error", error);
         error.printStackTrace();
     }
 }
1.3. Sentinel 总体架构

在这里插入图片描述
【注意】:最新框架图可能不是上述的框架图;

解析:首先从 Entry entry = SphU.entry(resourceName); 入口进入调用NodeSelectorSlot(从Root节点构建树形结构)->…

1.4. Sentinel 工作主流程

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

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

【NOTE】 :
context:上下文,每个线程都有自己的上下文;
Entry: 每一个请求都会有一个Entry(资源访问的请求)在 Sentinel 里面,所有的资源都对应一个资源名称以及一个 Entry

1.4.1. Sentinel添加自定义功能(扩展点)

在这里插入图片描述

  • ProcessorSlot
    在这里插入图片描述
1.4.2. 源码调用链

SentinelWebInterceptor # addInterceptors
=> SentinelWebInterceptor extends AbstractSentinelInterceptor {
=> AbstractSentinelInterceptor implements HandlerInterceptor =>
=> AbstractSentinelInterceptor#preHandle
=>Entry entry = SphU.entry(resourceName);
=>CtSph#entryWithType

  • CtSph#entryWithType
//name:资源名, entryType: in(请求进来),out(请求出去) EntryType.IN
//Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
//目的:封装成ResourceWrapper
StringResourceWrapper resource = new StringResourceWrapper(name, entryType, resourceType);
//该方法主要是获取到本资源所对应的资源处理链(即架构图中资源处理链路)
//核心方法有:
//1. InternalContextUtil.internalEnter(生成上下文环境)
//2. lookProcessChain(resourceWrapper)(构建一个调用链)
//3. chain.entry(开始检测限流规则)    
return entryWithPriority(resource, count, prioritized, args);
  • entryWithPriority
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
    throws BlockException {
    //获取上下文环境 【见】ContextUtil #getContext
    Context context = ContextUtil.getContext();
    if (context instanceof NullContext) {
        //返回一个新的CtEntry且不做限流处理
        return new CtEntry(resourceWrapper, null, context);
    }

    if (context == null) {
        //生成context【见】 InternalContextUtil.internalEnter
        context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
    }

    //全局限流是否开启,关闭的话,则不进行限流规则检查
    if (!Constants.ON) {
        return new CtEntry(resourceWrapper, null, context);
    }
    //构建一个调用链【见】lookProcessChain
    ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);

    if (chain == null) {
        return new CtEntry(resourceWrapper, null, context);
    }
    //生成CtEntry
    Entry e = new CtEntry(resourceWrapper, chain, context);
    try {
        chain.entry(context, resourceWrapper, null, count, prioritized, args);
    } catch (BlockException e1) {
        //限流了,必须要退出e.exit()
        e.exit(count, args);
        throw e1;
    } catch (Throwable e1) {
        RecordLog.info("Sentinel unexpected exception", e1);
    }
    return e;
}
  • ContextUtil #getContext
private static ThreadLocal<Context> contextHolder = new ThreadLocal<>();
public static Context getContext() {
    return contextHolder.get();
}
  • InternalContextUtil.internalEnter
static Context internalEnter(String name, String origin) {
    //【见】trueEnter
    return trueEnter(name, origin);
}
  • trueEnter
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) {
            if (localCacheNameMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
                setNullContext();
                return NULL_CONTEXT;
            } else {
                LOCK.lock();
                try {
                    node = contextNameNodeMap.get(name);
                    if (node == null) {
                        if (contextNameNodeMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
                            setNullContext();
                            return NULL_CONTEXT;
                        } else {
                            //这里的name是默认的名称
                            node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null);
                            // Add entrance node.
                            Constants.ROOT.addChild(node);
                            //创建一个EntranceNode节点,并将该节点放入contextNameNodeMap中
                            Map<String, DefaultNode> newMap = new HashMap<>(contextNameNodeMap.size() + 1);
                            newMap.putAll(contextNameNodeMap);
                            newMap.put(name, node);
                            contextNameNodeMap = newMap;
                        }
                    }
                } finally {
                    LOCK.unlock();
                }
            }
        }
        //创建context, 并将上一步的node节点包装进context, 将context放入
        //ThreadLocal中并返回
        context = new Context(node, name);
        context.setOrigin(origin);
        contextHolder.set(context);
    }

    return context;
}

context : 表示上下文,一个线程对应一个context, 其中包含一些属性如下:

  • name: 名字
  • entranceNode: 调用链入口
  • curEntry: 当前entry
  • origin: 调用者来源
  • async: 异步

Node: 表示一个节点,这个节点会保存某个资源的各个实时统计数据, 通过访问某个节点,就可以获取对应资源的实时状态,根据这个消息来进行限流和降级, 它有几种节点类型

  • statisticNode: 实现了Node接口,封装了基础的流量统计和获取方法
  • defaultNode: 默认节点, NodeSelectorSlot中创建的就是这个节点; 代表同个资源在不同上下文中各自的流量情况;
  • clusterNode: 集群节点, 代表同个资源在不同上下文中总体的流量情况
  • EntranceNode: 该节点表示一棵调用链树的入口节点,通过他可以获取调用链树中所有的子节点; 每个上下文都会有一个入口节点,用来统计当前上下文的总体流量情况
  • OriginNode: 是一个StatisticNode类型的节点,代表了同个资源请求来源的流量情况;
  • CtSph#lookProcessChain

chainMap存放所有资源的所有链路, 最多只能存放6000个资源

private static volatile Map<ResourceWrapper, ProcessorSlotChain> chainMap
    = new HashMap<ResourceWrapper, ProcessorSlotChain>();
public final static int MAX_SLOT_CHAIN_SIZE = 6000;
ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
    //chainMap存放所有资源的所有链路, 最多只能存放6000个资源
    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;
                }
                //构建链路,利用SPI机制
                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;
}
  • SlotChainProvider#newSlotChain
private static volatile SlotChainBuilder slotChainBuilder = null;
public static ProcessorSlotChain newSlotChain() {
    if (slotChainBuilder != null) {
        return slotChainBuilder.build();
    }

    // Resolve the slot chain builder SPI.
    slotChainBuilder = SpiLoader.loadFirstInstanceOrDefault(SlotChainBuilder.class, DefaultSlotChainBuilder.class);

    if (slotChainBuilder == null) {
        // Should not go through here.
        RecordLog.warn("[SlotChainProvider] Wrong state when resolving slot chain builder, using default");
        slotChainBuilder = new DefaultSlotChainBuilder();
    } else {
        RecordLog.info("[SlotChainProvider] Global slot chain builder resolved: "
            + slotChainBuilder.getClass().getCanonicalName());
    }
    return slotChainBuilder.build();
}
  • SlotChainBuilder
public interface SlotChainBuilder {

    ProcessorSlotChain build();
}

在这里插入图片描述

  • DefaultSlotChainBuilder
public class DefaultSlotChainBuilder implements SlotChainBuilder {

    @Override
    public ProcessorSlotChain build() {
        ProcessorSlotChain chain = new DefaultProcessorSlotChain();

        // Note: the instances of ProcessorSlot should be different, since they are not stateless.
        //SPI机制【见】 ProcessorSlot
        List<ProcessorSlot> sortedSlotList = SpiLoader.loadPrototypeInstanceListSorted(ProcessorSlot.class);
        for (ProcessorSlot slot : sortedSlotList) {
            if (!(slot instanceof AbstractLinkedProcessorSlot)) {
                RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain");
                continue;
            }

            chain.addLast((AbstractLinkedProcessorSlot<?>) slot);
        }

        return chain;
    }
}
  • ProcessorSlot
public interface ProcessorSlot<T> {
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • NodeSelectorSlot
@SpiOrder(-10000)
public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {
}

按照@SpiOrder注解大小升序的顺序进行加载

如上:上下文及请求链路也构建完成!!!

  • CtSph#entryWithPriority
Entry e = new CtEntry(resourceWrapper, chain, context);


CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot<Object> chain, Context context) {
    super(resourceWrapper);
    this.chain = chain;
    this.context = context;

    setUpEntryFor(context);
}
  • CtSph#setUpEntryFor
private void setUpEntryFor(Context context) {
    // The entry should not be associated to NullContext.
    if (context instanceof NullContext) {
        return;
    }
    this.parent = context.getCurEntry();
    if (parent != null) {
        ((CtEntry) parent).child = this;
    }
    context.setCurEntry(this);
}

如下图所示:
在这里插入图片描述

  • CtSph#entryWithPriority
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 {
    //首先进入的NodeSelectorSlot
    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);
}
  • NodeSelectorSlot #entry
@SpiOrder(-10000)
public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {

    private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);

    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
        throws Throwable {
       
        DefaultNode node = map.get(context.getName());
        if (node == null) {
            synchronized (this) {
                node = map.get(context.getName());
                if (node == null) {
                    node = new DefaultNode(resourceWrapper, null);
                    HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
                    cacheMap.putAll(map);
                    cacheMap.put(context.getName(), node);
                    map = cacheMap;
                    // Build invocation tree
                    ((DefaultNode) context.getLastNode()).addChild(node);
                }
            }
        }
        context.setCurNode(node);
        fireEntry(context, resourceWrapper, node, count, prioritized, args);
    }
}
  • Context #getLastNode
private DefaultNode entranceNode;
//此时curEntry的lastNode==null;则返回entranceNode
public Node getLastNode() {
    if (curEntry != null && curEntry.getLastNode() != null) {
        return curEntry.getLastNode();
    } else {
        return entranceNode;
    }
}

如上代码目的如下图:
在这里插入图片描述

((DefaultNode) context.getLastNode()).addChild(node);
即EntranceNode指向Default Node

结果如下图:
在这里插入图片描述
在这里插入图片描述

  • StatisticSlot#entry最繁琐的
  1. 先去调用后面的所有slot(执行到上图中StatisticSlot后暂停,先执行后面所有的链路), 如果限流或者异常,则直接抛,如果规则都通过了则向下执行
  2. 调用node.addPassRequest(count)增加请求数
  3. 调用父类(StaticNode) 来进行统计,根据ClusteroNote汇总统计(背后也是调用父类StaticNode)
@SpiOrder(-7000)
public class StatisticSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args) throws Throwable {
        try {
           //StatisticsSlot后面所有的链路,一旦后面的链路一旦有限流就抛异常
           //调用后面所有的链路
            fireEntry(context, resourceWrapper, node, count, prioritized, args);
    
           //若是没有限流,滑动窗口记录增加请求数
           //增加线程数,存放在node里面
            node.increaseThreadNum();
            //增加通过的请求【见】node.addPassRequest(count);
            node.addPassRequest(count);
    
            if (context.getCurEntry().getOriginNode() != null) {
                context.getCurEntry().getOriginNode().increaseThreadNum();
                context.getCurEntry().getOriginNode().addPassRequest(count);
            }
    
            if (resourceWrapper.getEntryType() == EntryType.IN) {
                // Add count for global inbound entry node for global statistics.
                Constants.ENTRY_NODE.increaseThreadNum();
                Constants.ENTRY_NODE.addPassRequest(count);
            }
    
            for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
                handler.onPass(context, resourceWrapper, node, count, args);
            }
        } catch (PriorityWaitException ex) {
            node.increaseThreadNum();
            if (context.getCurEntry().getOriginNode() != null) {
                context.getCurEntry().getOriginNode().increaseThreadNum();
            }
    
            if (resourceWrapper.getEntryType() == EntryType.IN) {
                Constants.ENTRY_NODE.increaseThreadNum();
            }
            for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
                handler.onPass(context, resourceWrapper, node, count, args);
            }
        } catch (BlockException e) {
            context.getCurEntry().setBlockError(e);
            node.increaseBlockQps(count);
            if (context.getCurEntry().getOriginNode() != null) {
                context.getCurEntry().getOriginNode().increaseBlockQps(count);
            }
    
            if (resourceWrapper.getEntryType() == EntryType.IN) {
                Constants.ENTRY_NODE.increaseBlockQps(count);
            }
            for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
                handler.onBlocked(e, context, resourceWrapper, node, count, args);
            }
    
            throw e;
        } catch (Throwable e) {
            context.getCurEntry().setError(e);
            throw e;
        }
    }
}
  • StatisticNode#addPassRequest
//SampleCountProperty.SAMPLE_COUNT = 2
//IntervalProperty.INTERVAL = 1000
【见】ArrayMetric # ArrayMetric()
private transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT,
    IntervalProperty.INTERVAL);

/**
 * rollingCounterInSecond按照秒来统计,分成两个窗口,每个窗口500ms,用来统计QPS
 * rollingCounterInMinute按照分钟统计,生成60个窗口,每个窗口1000ms
 */
@Override
public void addPassRequest(int count) {
    //【见】ArrayMetric#addPass
    rollingCounterInSecond.addPass(count);
    rollingCounterInMinute.addPass(count);
}
  • ArrayMetric # ArrayMetric()
public ArrayMetric(int sampleCount, int intervalInMs) {
    this.data = new OccupiableBucketLeapArray(sampleCount, intervalInMs);
}

public OccupiableBucketLeapArray(int sampleCount, int intervalInMs) {
    // 【见】LeapArray#LeapArray()
    super(sampleCount, intervalInMs);
    this.borrowArray = new FutureBucketLeapArray(sampleCount, intervalInMs);
}
  • LeapArray#LeapArray()
public abstract class LeapArray<T> {
    public LeapArray(int sampleCount, int intervalInMs) {
        //窗口的长度:1000/2
        this.windowLengthInMs = intervalInMs / sampleCount;
        this.intervalInMs = intervalInMs;
        this.sampleCount = sampleCount;
        this.array = new AtomicReferenceArray<>(sampleCount);
    }
}
  • ArrayMetric#addPass
//滑动窗口最新给到data数组
private final LeapArray<MetricBucket> data;
@Override
public void addPass(int count) {
    //【见】LeapArray#currentWindow
    WindowWrap<MetricBucket> wrap = data.currentWindow();
    wrap.value().addPass(count);
}
  • LeapArray#currentWindow 滑动窗口的算法
public WindowWrap<T> currentWindow() {
    return currentWindow(TimeUtil.currentTimeMillis());
}
public WindowWrap<T> currentWindow(long timeMillis) {
    if (timeMillis < 0) {
        return null;
    }
    
    int idx = calculateTimeIdx(timeMillis);
    // Calculate current bucket start time.
    //启动时间
    long windowStart = calculateWindowStart(timeMillis);
}

private int calculateTimeIdx(/*@Valid*/ long timeMillis) {
    //计算当前处于哪个滑动窗口里面
    //当前时间 / 500
    long timeId = timeMillis / windowLengthInMs;
    // Calculate current index so we can map the timestamp to the leap array.
    return (int)(timeId % array.length());
}

protected long calculateWindowStart(/*@Valid*/ long timeMillis) {
    //如下图
    return timeMillis - timeMillis % windowLengthInMs;
}

在这里插入图片描述

  • FlowSlot#entry
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                  boolean prioritized, Object... args) throws Throwable {
    //【见】FlowRuleChecker#checkFlow                  
    checkFlow(resourceWrapper, context, node, count, prioritized);
    //往后执行下一个链
    fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
  • FlowRuleChecker#checkFlow
public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource,
                      Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {
    if (ruleProvider == null || resource == null) {
        return;
    }
    //获取规则,并且循环遍历规则
    Collection<FlowRule> rules = ruleProvider.apply(resource.getName());
    if (rules != null) {
        for (FlowRule rule : rules) {
            //【见】FlowRuleChecker#canPassCheck
            if (!canPassCheck(rule, context, node, count, prioritized)) {
                throw new FlowException(rule.getLimitApp(), rule);
            }
        }
    }
}
  • FlowRuleChecker#canPassCheck
public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount,
                                                boolean prioritized) {
    //对资源进行限流                                                
    String limitApp = rule.getLimitApp();
    if (limitApp == null) {
        return true;
    }
    //判断当前是否是集群模式【见下图】
    if (rule.isClusterMode()) {
        return passClusterCheck(rule, context, node, acquireCount, prioritized);
    }
    //【见】FlowRuleChecker#passLocalCheck
    return passLocalCheck(rule, context, node, acquireCount, prioritized);
}

在这里插入图片描述

private static boolean passLocalCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount,
                                      boolean prioritized) {
    Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node);
    if (selectedNode == null) {
        return true;
    }

    return rule.getRater().canPass(selectedNode, acquireCount, prioritized);
}
  • FlowRuleChecker # selectNodeByRequesterAndStrategy
static Node selectNodeByRequesterAndStrategy(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node) {
    // The limit app should not be empty.
    String limitApp = rule.getLimitApp();
    //默认直连=0(直连、关联、链路)
    int strategy = rule.getStrategy();
    //默认为""
    String origin = context.getOrigin();

    if (limitApp.equals(origin) && filterOrigin(origin)) {
        if (strategy == RuleConstant.STRATEGY_DIRECT) {
            return context.getOriginNode();
        }

        return selectReferenceNode(rule, context, node);
    } else if (RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp)) {
        //判断是否是直连,直连返回clusterNode
        if (strategy == RuleConstant.STRATEGY_DIRECT) {
            return node.getClusterNode();
        }
        //非直连返回referenceNode
        return selectReferenceNode(rule, context, node);
    } else if (RuleConstant.LIMIT_APP_OTHER.equals(limitApp)
        && FlowRuleManager.isOtherOrigin(origin, rule.getResource())) {
        if (strategy == RuleConstant.STRATEGY_DIRECT) {
            return context.getOriginNode();
        }

        return selectReferenceNode(rule, context, node);
    }

    return null;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值