欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring cloud alibaba整合sentinel之webmvc拦截器方式源码简析

程序员文章站 2022-06-28 18:14:48
spring cloud alibaba提供的Sentinel支持方式如下:1、使用webmvc方式,即使用SentinelWebInterceptor拦截器2、对Feign的支持,需开启feign.sentinel.enabled参数3、对RestTemplate的支持,使用@SentinelRestTemplate注解本文主要说明使用webmvc方式的源码简析。Spring Cloud Alib...

spring cloud alibaba提供的Sentinel支持方式如下:

  1. 使用webmvc方式,即使用SentinelWebInterceptor拦截器
  2. 对Feign的支持,需开启feign.sentinel.enabled参数
  3. 对RestTemplate的支持,使用@SentinelRestTemplate注解

本文主要说明使用webmvc方式的源码简析,看本文前需要对Sentinel组件有一定了解。项目依赖的spring cloud的版本信息如下:

Spring Cloud Alibaba Version Sentinel Version Nacos Version
2.2.3.RELEASE 1.8.0 1.3.3

涉及到的关键jar:
spring-cloud-starter-alibaba-sentinel-2.2.3.RELEASE.jarsentinel-spring-webmvc-adapter-1.8.0.jar

1.程序加载入口SentinelWebAutoConfiguration

按照套路,分析的入口肯定starter的某一个AutoConfiguration里。

@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 {

	private static final Logger log = LoggerFactory
			.getLogger(SentinelWebAutoConfiguration.class);
	@Autowired
	private SentinelProperties properties;
	@Autowired
	private Optional<UrlCleaner> urlCleanerOptional;
	@Autowired
	private Optional<BlockExceptionHandler> blockExceptionHandlerOptional;
	@Autowired
	private Optional<RequestOriginParser> requestOriginParserOptional;
	@Autowired
	private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;
    //将拦截器添加到spring mvc的配置中
	@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());
	}
    //构建拦截器对象,SentinelWebMvcConfig对象的创建在后面的代码里分析
	@Bean
	@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",
			matchIfMissing = true)
	public SentinelWebInterceptor sentinelWebInterceptor(
			SentinelWebMvcConfig sentinelWebMvcConfig) {
		return new SentinelWebInterceptor(sentinelWebMvcConfig);
	}
    //创建SentinelWebMvcConfig对象,为创建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;
	}

}

同过sentinelWebInterceptor()sentinelWebMvcConfig()方法,我们可以得到以下有用的信息:

  1. 通过sentinelWebInterceptor()方法上的注解参数spring.cloud.sentinel.filter.enabled可以知道引入sentinel-spring-webmvc-adapter-1.8.0.jar后默认就开启了使用SentinelWebInterceptor来处理
  2. UrlCleaner 为自动注入,如果想自定义UrlCleaner,只需自己写一个UrlCleaner接口实现类,并添加到Spring IOC 容器中即可
  3. BlockExceptionHandler为自动注入,如果不想使用默认的DefaultBlockExceptionHandler,只需自己写一个BlockExceptionHandler接口实现类,并添加到Spring IOC 容器中即可
  4. 可以采用和3类型的方式自定义RequestOriginParser

2.重点配置类SentinelProperties的配置

同过addInterceptors()方法可以看到,只针对filter提供如下配置:

1、通过spring.cloud.sentinel.filter的配置
spring:
  cloud:
    sentinel:
      filter:
        #配置需要拦截的URL(默认"/**")
        url-patterns:
          - '/userconfig/**'
          - '/nacosconfig/**'
        #拦截器的order(默认最高优先级-2147483648)
        order:-123   

2、SentinelProperties还提供很多常用的配置,跟SentinelWebMvcConfig相关的还有:
 spring:
  cloud:
    sentinel:
       #如果针对同一URL资源需要区分是Post还是Get请求可以将参数设置为true
       http-method-specify:true
       webContextUnify:true
       

3.拦截器SentinelWebInterceptor源码简析

public class SentinelWebInterceptor extends AbstractSentinelInterceptor {
    private final SentinelWebMvcConfig config;
    public SentinelWebInterceptor() {
        this(new SentinelWebMvcConfig());
    }
    public SentinelWebInterceptor(SentinelWebMvcConfig config) {
        super(config);
        if (config == null) {
            // Use the default config by default.
            this.config = new SentinelWebMvcConfig();
        } else {
            this.config = config;
        }
    }
    @Override
    protected String getResourceName(HttpServletRequest request) {
        // Resolve the Spring Web URL pattern from the request attribute.
        Object resourceNameObject = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        if (resourceNameObject == null || !(resourceNameObject instanceof String)) {
            return null;
        }
        String resourceName = (String) resourceNameObject;
        UrlCleaner urlCleaner = config.getUrlCleaner();
        if (urlCleaner != null) {
            resourceName = urlCleaner.clean(resourceName);
        }
        // Add method specification if necessary
        if (StringUtil.isNotEmpty(resourceName) && config.isHttpMethodSpecify()) {
            resourceName = request.getMethod().toUpperCase() + ":" + resourceName;
        }
        return resourceName;
    }
    @Override
    protected String getContextName(HttpServletRequest request) {
        if (config.isWebContextUnify()) {
            return super.getContextName(request);
        }
        return getResourceName(request);
    }
}
//AbstractSentinelInterceptor类的中的方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
    try {
        String resourceName = getResourceName(request);

        if (StringUtil.isEmpty(resourceName)) {
            return true;
        }
        
        if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) {
            return true;
        }
        
        // Parse the request origin using registered origin parser.
        String origin = parseOrigin(request);
        String contextName = getContextName(request);
        ContextUtil.enter(contextName, origin);
        Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
        request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry);
        return true;
    } catch (BlockException e) {
        try {
            handleBlockException(request, response, e);
        } finally {
            ContextUtil.exit();
        }
        return false;
    }
}

该类业务逻辑很简单。主要业务如下:

  1. 解析请求资源路径,支持通过我们自定义的UrlCleaner来对URL资源进行清洗,一般用于变量在url路径的restful形式的url,如:/getuser/123
  2. 在拦截器的preHandle方法中对URL使用 SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN)进行保护

本文地址:https://blog.csdn.net/mapleleafforest/article/details/111033992