IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Sentinel源码 -> 正文阅读

[Java知识库]Sentinel源码

一、概述

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

  • SentinelWebAutoConfiguration 针对web项目,即springmvc扩展包,默认把所有的url的入口添加为sentinel资源
  • SentinelAutoConfiguration 针对用户添加@SentinelResource注解的地方添加为sentinel资源

上述2个入口最终都是利用代理方式实现的,并且共用底层的代理类

二、源码

在这里插入图片描述

2.1 SentinelAutoConfiguration

META-INF/spring.factories中包含SentinelAutoConfiguration,会在启动启动的时候进行加载,并且会加载@Bean的方法去实例化对象:

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

SentinelResourceAspect负责创建代理,实际执行的时候,也确实会调用到该类:
在这里插入图片描述

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的归一入口进行讲解。

为什么说是统一入口,即不管你是硬编码还是通过@SentinelResource注解 又或是通过集成alibaba+springmvc,sentinel最终的入口都是SphU.entry

2.1.1 SphU.entry

SphU.entry是Sentinel的归一入口,所有流量在熔断限流前都由此进入,此功能实现了对资源进行规则检查。在SphU.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进行规则检查并返回结果。

2.2 SentinelWebAutoConfiguration

存在新版本合和旧版本之分

SentinelWebAutoConfiguration 针对web项目,即springmvc扩展包,默认把所有的url的入口添加为sentinel资源,这样不用每个url的入口手动添加@SentinelResource标签了

2.2.1 新版本

可以参见 【Sentinel入门】05 springmvc 集成Sentinel & springboot集成Sentinel & 链路模式失效 & WebContextUnify & CommonFilter,里面包含新版本的用法,基于springboot,需要手动引入SentinelWebInterceptor代理,本篇是alibaba源码,通过spi机制,自动实现引入SentinelWebInterceptor代理

至少从 spring cloud alibaba 2.2.1.RELEASE 版本开始,已经采用新版本

特点 是 引入 sentinel-spring-webmvc-dapter 包, 基于 Spring 的 Interceptor 代理拦截资源:

...
@Configuration
public class SentinelWebAutoConfiguration implements WebMvcConfigurer {  //实现WebMvcConfigurer ,准备注册代理

   
    //参数项集合,用于定制拦截器
	@Autowired
	private SentinelProperties properties;

	@Autowired
	private Optional<UrlCleaner> urlCleanerOptional;

	@Autowired
	private Optional<BlockExceptionHandler> blockExceptionHandlerOptional;

	@Autowired
	private Optional<RequestOriginParser> requestOriginParserOptional;

    //step3 注入一个SentinelWebInterceptor实例
	@Autowired
	private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;

   //step4注册拦截器入口
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		if (!sentinelWebInterceptorOptional.isPresent()) {
			return;
		}
		SentinelProperties.Filter filterConfig = properties.getFilter();
		 // step 4.1入参是前面步骤所创建的一个sentinel拦截器
		registry.addInterceptor(sentinelWebInterceptorOptional.get())
				.order(filterConfig.getOrder())
				.addPathPatterns(filterConfig.getUrlPatterns());
		
	}

   //step2 创建一个拦截器
	@Bean
	@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",
			matchIfMissing = true)
	public SentinelWebInterceptor sentinelWebInterceptor(
			SentinelWebMvcConfig sentinelWebMvcConfig) {
		return new SentinelWebInterceptor(sentinelWebMvcConfig);
	}
	
    //step1  创建一个配置,供创建SentinelWebInterceptor拦截器用,用于定制拦截器
	@Bean
	@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",
			matchIfMissing = true)
	public SentinelWebMvcConfig sentinelWebMvcConfig() {
		SentinelWebMvcConfig sentinelWebMvcConfig = new SentinelWebMvcConfig();
		sentinelWebMvcConfig.setHttpMethodSpecify(properties.getHttpMethodSpecify());
		//很重要,影响链路模式
		sentinelWebMvcConfig.setWebContextUnify(properties.getWebContextUnify());

		if (blockExceptionHandlerOptional.isPresent()) {
			blockExceptionHandlerOptional
					.ifPresent(sentinelWebMvcConfig::setBlockExceptionHandler);
		}
		else {
			if (StringUtils.hasText(properties.getBlockPage())) {
				sentinelWebMvcConfig.setBlockExceptionHandler(((request, response,
						e) -> response.sendRedirect(properties.getBlockPage())));
			}
			else {
				sentinelWebMvcConfig
						.setBlockExceptionHandler(new DefaultBlockExceptionHandler());
			}
		}

		urlCleanerOptional.ifPresent(sentinelWebMvcConfig::setUrlCleaner);
		requestOriginParserOptional.ifPresent(sentinelWebMvcConfig::setOriginParser);
		return sentinelWebMvcConfig;
	}

2.2.1 (旧版本)

META-INF/spring.factories中包含SentinelWebAutoConfiguration ,会在启动启动的时候进行加载,并且会加载@Bean的方法去实例化对象:

package com.alibaba.cloud.sentinel;

/*省略部分代码。。。。。。*/
@Configuration
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,这个后面重点研究;
  • Step3将CommonFilter放入到filterChain中。

旧版本中,是利用Filter原理,进行过滤:

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,这是个内置的Filter 实现类,专门用于sentinel的功能:

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,这个后面重点研究。

参考

Sentinel源码(流量入口)

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-11 21:59:56  更:2022-03-11 22:04:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:50:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码