Sentinel源码(流量入口)

发布于 2021-09-05 00:36

一、概述
在SpringBoot启动过程中,会加载外部依赖中META-INF/spring.factories声明的类。其中最重要的两个类分别为SentinelWebAutoConfiguration和SentinelAutoConfiguration。下面针对这两个类进行分析,逐渐深入理解流量是如何被Sentinel拦截的。
图1  spring.factories

二、深入理解

图2  流程图
SentinelWebAutoConfiguration

package com.alibaba.cloud.sentinel;

/*省略部分代码。。。。。。*/
public class SentinelWebAutoConfiguration {
        /*省略部分代码。。。。。。*/
  @Bean
  @ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", matchIfMissing = true)
  public FilterRegistrationBean sentinelFilter() {
        /*Step 1 : 创建一个FilterRegistrationBean*/
    FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();

    SentinelProperties.Filter filterConfig = properties.getFilter();

    if (filterConfig.getUrlPatterns() == null
        || filterConfig.getUrlPatterns().isEmpty()) {
      List<String> defaultPatterns = new ArrayList<>();
      defaultPatterns.add("/*");
      filterConfig.setUrlPatterns(defaultPatterns);
    }

    registration.addUrlPatterns(filterConfig.getUrlPatterns().toArray(new String[0]));
         /*重点Step 2 :创建一个CommonFilter*/
    Filter filter = new CommonFilter();
         /*Step 3 : 将CommonFilter放入FilterRegistrationBean*/
    registration.setFilter(filter);
    registration.setOrder(filterConfig.getOrder());
    registration.addInitParameter("HTTP_METHOD_SPECIFY",
    String.valueOf(properties.getHttpMethodSpecify()));
         /*省略部分代码。。。。。。*/
    return registration;
  }
}


在SentinelWebAutoConfiguration被加载时,注入了一个FilterRegistrationBean的Bean对象。Step1创建一个注册Filter的FilterRegistrationBean对象registration;Step2创建了一个CommonFilter,这个后面重点研究;Step3CommonFilter放入到filterChain中。

CommonFilter

package com.alibaba.csp.sentinel.adapter.servlet;

public class CommonFilter implements Filter {
             /*省略部分代码。。。。。。*/
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
{
             /*省略部分代码。。。。。。*/
        try {
             /*Step 1 :截取请求URL作为资源名赋予target */
            String target = FilterUtil.filterTarget(sRequest);
             /*省略部分代码。。。。。。*/
             /*Step 2 :创建一个target名称的上下文*/
            ContextUtil.enter(target, origin);
             /*Step 3 :执行Sentinel的核心功能*/
            entry = SphU.entry(target, EntryType.IN);
             /*省略部分代码。。。。。。*/
            chain.doFilter(request, response);
        }
             /*省略部分代码。。。。。。*/
            ContextUtil.exit();
        }
    }
             /*省略部分代码。。。。。。*/
}

CommonFilter会拦截所有进入Tomcat的请求,Step1截取请求中的URL作为“资源名”;Step2创建一个name为“资源名”的Context;Step3执行Sentinel的核心功能SphU.entry,这个后面重点研究。

SentinelAutoConfiguration

package com.alibaba.cloud.sentinel.custom;
             /*省略部分代码。。。。。。*/
public class SentinelAutoConfiguration {
          /*省略部分代码。。。。。。*/
          /*注入切面SentinelResourceAspect*/
  @Bean
  @ConditionalOnMissingBean
  public SentinelResourceAspect sentinelResourceAspect() {
    return new SentinelResourceAspect();
  }
}


图3  SentinelResourceAspect

package com.alibaba.csp.sentinel.annotation.aspectj;

@Aspect
public class SentinelResourceAspect extends AbstractSentinelAspectSupport {

    @Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")
    public void sentinelResourceAnnotationPointcut() {
    }

    @Around("sentinelResourceAnnotationPointcut()")
    public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {
        Method originMethod = resolveMethod(pjp);
        /*Step 1 :获取@SentinelResource注解*/
        SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);
        /*Step 2 :获取@SentinelResource中的资源名*/
        String resourceName = getResourceName(annotation.value(), originMethod);
        EntryType entryType = annotation.entryType();
        Entry entry = null;
        try {
        /*Step 3 :执行Sentinel核心功能*/
            entry = SphU.entry(resourceName, entryType, 1, pjp.getArgs());
            Object result = pjp.proceed();
            return result;
        } catch (BlockException ex) {
            return handleBlockException(pjp, annotation, ex);
        } catch (Throwable ex) {
            traceException(ex, annotation);
            throw ex;
        } finally {
            if (entry != null) {
                entry.exit(1, pjp.getArgs());
            }
        }
    }
}

SentinelResourceAspect是Sentinel中的核心切面,在SentinelAutoConfiguration被加载时,注入了一个SentinelResourceAspect的Bean对象,aspect的@around拦截标注有@SentinelResource的注解。Step1获取@SentinelResource注解;Step2获取@SentinelResource中的资源名;Step3执行Sentinel核心功能

通过上面SentinelWebAutoConfiguration和SentinelAutoConfiguration两个类的加载,成功将流量引入Sentinel的核心入口SphU.entry,下面我们对Sentinel的归一入口进行讲解。

SphU.entry

SphU.entry是Sentinel的归一入口,所有流量在熔断限流前都由此进入,此功能实现了对资源进行规则检查。在SphU.entry中的核心功能是entryWithPriority。

com.alibaba.csp.sentinel.SphU
图4  Env.sph.entry

entryWithPriority

com.alibaba.csp.sentinel.CtSph

private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
        throws BlockException
{
               /*Step 1-1 :创建上下文,如果在ThreadLocal中已存在,则直接返回上下文*/
        Context context = ContextUtil.getContext();
               /*省略部分代码。。。。。。*/
               /*Step 1-2 :创建一条Slot Chain*/
        ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
               /*省略部分代码。。。。。。*/
               /*Step 2 :将Chain和当前Context结合*/
        Entry e = new CtEntry(resourceWrapper, chain, context);
        try {
               /*Step 3 :Slot Chain入口,开始规则检查*/
            chain.entry(context, resourceWrapper, null, count, prioritized, args);
        } catch (BlockException e1) {
            e.exit(count, args);
            throw e1;
        } catch (Throwable e1) {
            RecordLog.info("Sentinel unexpected exception", e1);
        }
        return e;
    }

entryWithPriority是Sentinel的核心功能,Step1创建上下文Context、Slot Chain;Step2结合Context和Slot Chain;Step3入Slot Chain进行规则检查并返回结果。

三、总结

Step1所有请求经过sentinel自定义的拦截器Sentinel CommonFilter;

Step2该拦截器从请求中提取URL作为资源名;

Step3进入Sentinel 核心的代码段entry.enter,至此引流工作已完成,后续交给Sentinel通用核心功能entryWithPriority

Step4执行HandlerMapping映射处理机制,从上下文中挑选合适的HandlerMethod,即Controller中的@RequestMapping方法;

Step5进入方法前@SentinelResource拦截请求,利用AOP技术实现请求到被资源保护代码前的熔断限流功能;



本文来自网络或网友投稿,如有侵犯您的权益,请发邮件至:aisoutu@outlook.com 我们将第一时间删除。

相关素材