Spring Cloud Alibaba-Sentinel源码阅读(二)-流控的主流程

本文深入探讨了Sentinel流量控制的主流程,从SphU#entry方法开始,详细分析了CtSph#entryWithPriority方法的执行路径,包括资源上下文、处理器链的创建和执行。ProcessorSlot的角色如NodeSelectorSlot、ClusterBuilderSlot、LogSlot等在流控中的作用也被提及,为理解Sentinel的内部工作机制提供了关键信息。
摘要由CSDN通过智能技术生成

一、流控的主流程

1、流控的入口

从上一篇《Spring Cloud Alibaba-Sentinel源码阅读(一)-Sentinel的使用》中可知,Sentinel流控的入口是SphU#entry方法,而SphU类据说是semaphore 信号量的缩写,这个SphU#entry方法的大概就是进入流控了,如果方法抛BlockException,则代表被流控了,如果正常返回一个Entry则正常放行。

SphU#entry(java.lang.String, int, com.alibaba.csp.sentinel.EntryType, java.lang.Object[])
	-> SphResourceTypeSupport#entryWithType(java.lang.String, int, com.alibaba.csp.sentinel.EntryType, int, java.lang.Object[])
		-> CtSph#entryWithType(java.lang.String, int, com.alibaba.csp.sentinel.EntryType, int, java.lang.Object[])
			-> CtSph#entryWithPriority(com.alibaba.csp.sentinel.slotchain.ResourceWrapper, int, boolean, java.lang.Object...)

CtSph#entryWithPriority

private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
	throws BlockException {
	
	// 获取方法调用的上下文环境,上下环境对象存储在线程本地变量:ThreadLocal 中,上下文环境中存储的是整个调用链,
	Context context = ContextUtil.getContext();
	if (context instanceof NullContext) {
		// The {@link NullContext} indicates that the amount of context has exceeded the threshold,
		// so here init the entry only. No rule checking will be done.
		return new CtEntry(resourceWrapper, null, context);
	}

	if (context == null) {
		// Using default context.
		context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
	}
	
	// 全局关闭的开关,如果关闭,返回的 CtEntry 中的 chain 为空,如果 chain 为空,则不会触发 Sentinel 流控相关的逻辑
	// Global switch is close, no rule checking will do.
	if (!Constants.ON) {
		return new CtEntry(resourceWrapper, null, context);
	}

	// 为资源加载处理链链,这里是最最重要的方法
	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 {
	    
		// 调用 chain 的 entry 方法。
		chain.entry(context, resourceWrapper, null, count, prioritized, args);
	} catch (BlockException e1) {
		
		// 如果出现 BlockException ,调用 CtEntry 的 exit 方法。
		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);
	}
	return e;
}

说下上面方法的几个入参:

  • ResourceWrapper resourceWrapper 资源的包装类型,可以是字符串类型的资源描述,也可以是方法类的。
  • int count 此次需要消耗的令牌,正常是1
  • boolean prioritized 是否有优先级。

CtSph#lookProcessChain

ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
	// chainMap 一个全局的缓存表,即同一个资源 ResourceWrapper (同一个资源名称) 会共同使用同一个 ProcessorSlotChain ,
	// 不同的线程在访问同一个资源保护的代码时,这些线程将共用 ProcessorSlotChain 中的各个 ProcessorSlot
	// 注意留意 ResourceWrapper 的 equals 方法与 hashCode 方法。
    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;
                }
				
				// 通过 SlotChainProvider 创建对应的处理链
                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

public static ProcessorSlotChain newSlotChain() {
	if (slotChainBuilder != null) {
		return slotChainBuilder.build();
	}

	// 通过SPI机制,默认是DefaultSlotChainBuilder
	// Resolve the slot chain builder SPI.
	slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault();

	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();
}

DefaultSlotChainBuilder#build

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

	// 通过SPI机制,加载多个ProcessorSlot
	List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();
	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的各自含义:

  • NodeSelectorSlot 主要用于构建调用链。
  • ClusterBuilderSlot 用于集群限流、熔断。
  • LogSlot用于记录日志。
  • StatisticSlot 用于实时收集实时信息。
  • AuthoritySlot 用于权限校验的。
  • SystemSlot 用于验证系统级别的规则。
  • FlowSlot 实现限流机制。
  • DegradeSlot 实现熔断机制。

下一篇将介绍Sentinel滑动时间窗口源码,重点就是StatisticSlot 、FlowSlot 。

二、流控的主流程图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值