八、sentinel处理链核心源码分析(一)

一、@SentinelResource源码分析

入口在spring-cloud-starter-alibaba-sentinel的jar包,META-INF/spring.factories里的自动配置类:SentinelAutoConfiguration

SentinelAutoConfiguration注册一个SentinelResourceAspect切面,拦截@SentinelResource注解。

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
@EnableConfigurationProperties(SentinelProperties.class)
public class SentinelAutoConfiguration {
....
    //注册一个SentinelResourceAspect切面
	@Bean
	@ConditionalOnMissingBean
	public SentinelResourceAspect sentinelResourceAspect() {
		return new SentinelResourceAspect();
	}
....
}

SentinelResourceAspect切面,拦截注解SentinelResource


@Aspect
public class SentinelResourceAspect extends AbstractSentinelAspectSupport {

	//切入点 拦截注解SentinelResource
    @Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")
    public void sentinelResourceAnnotationPointcut() {
    }

    
    @Around("sentinelResourceAnnotationPointcut()")
    public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {
        Method originMethod = resolveMethod(pjp);

        SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);
        if (annotation == null) {
            // Should not go through here.
            throw new IllegalStateException("Wrong state for SentinelResource annotation");
        }
        String resourceName = getResourceName(annotation.value(), originMethod);
        EntryType entryType = annotation.entryType();
        int resourceType = annotation.resourceType();
        Entry entry = null;
        try {
		    //创建Entry---最核心的方法
            entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());
			//执行业务方法
            Object result = pjp.proceed();
            return result;
        } catch (BlockException ex) {
		    //处理BlockException
            return handleBlockException(pjp, annotation, ex);
        } catch (Throwable ex) {
            Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
            // The ignore list will be checked first.
            if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
                throw ex;
            }
            if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
                traceException(ex);
				//处理Fallback
                return handleFallback(pjp, annotation, ex);
            }

            // No fallback function can handle the exception, so throw it out.
            throw ex;
        } finally {
            if (entry != null) {
			    //entry退出
                entry.exit(1, pjp.getArgs());
            }
        }
    }
}

我们主要分析SphU.entry方法:

public static Entry entry(String name, int resourceType, EntryType trafficType, Object[] args)
	throws BlockException {
	return Env.sph.entryWithType(name, resourceType, trafficType, 1, args);
}

CtSph.entryWithType方法

@Override
public Entry entryWithType(String name, int resourceType, EntryType entryType, int count, boolean prioritized,
						   Object[] args) throws BlockException {
	StringResourceWrapper resource = new StringResourceWrapper(name, entryType, resourceType);
	return entryWithPriority(resource, count, prioritized, args);
}

CtSph.entryWithPriority方法

private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
	throws BlockException {
	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);
	}

	// 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);
	}
    //创建CtEntry
	Entry e = new CtEntry(resourceWrapper, chain, context);
	try {
	    //依次执行处理链路的slot
		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);
	}
	return e;
}

lookProcessChain方法,查看ProcessChain

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

newSlotChain方法,创建处理链

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

	//SPI获取SlotChainBuilder
	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());
	}
	//build构建
	return slotChainBuilder.build();
}

DefaultSlotChainBuilder.build()方法

@Override
public ProcessorSlotChain build() {
    //创建ProcessorSlotChain,初始化一个first,类型是AbstractLinkedProcessorSlot
	ProcessorSlotChain chain = new DefaultProcessorSlotChain();

	// 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;
}

DefaultProcessorSlotChain 类:

public class DefaultProcessorSlotChain extends ProcessorSlotChain {

    //初始化first
    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 指针指向first
    AbstractLinkedProcessorSlot<?> end = first;
	.....
}

META-INF/services下com.alibaba.csp.sentinel.slotchain.ProcessorSlot文件:

# 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

addLast方法:

@Override
public void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {
	end.setNext(protocolProcessor);
	end = protocolProcessor;
}

二、拦截器方式

注入com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration自动配置类,实现 WebMvcConfigurer。

@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 {
    //注入SentinelWebInterceptor拦截器的bean
	@Bean
	@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",
			matchIfMissing = true)
	public SentinelWebInterceptor sentinelWebInterceptor(
			SentinelWebMvcConfig sentinelWebMvcConfig) {
		return new SentinelWebInterceptor(sentinelWebMvcConfig);
	}
    //注册拦截器
	@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 继承AbstractSentinelInterceptor,在preHandle方法同样去执行:

 Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);

三、总结

到此,生成处理链路的主线流程已经清楚了。
使用自动配置类,两种方式处理我们的请求:
一种是注册一个SentinelResourceAspect切面,拦截@SentinelResource注解
另一种是注册一个SentinelWebInterceptor拦截器,在preHandle方法拦截处理。
最终都会调用 SphU.entry,创建处理链并执行。
具体每个slot的处理,我们后续展开看看。

源码分析流程图:

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值