Spring Cloud Ablibaba Sentinel与Neflix Ribbon结合使用,@SentinelRestTemplate不生效问题

文章目录

    • 问题:@SentinelRestTemplate不生效
    • 解决
    • 问题原因
      • 源码分析
        • @LoadBalanced作用时机
          • smartSingleton.afterSingletonsInstantiated()
        • @SentinelTemplate作用时机
      • Spring Boot版本过高

问题:@SentinelRestTemplate不生效

今天使用和@LoadBalanced和@SentinelRestTemplate进行负载均衡和链路流量控制。

@Configuration
public class MainConfig {

    @Bean
    @LoadBalanced
    @SentinelRestTemplate(
            blockHandler = "handleException",blockHandlerClass = GlobalExceptionHandler.class,
            fallback = "fallback",fallbackClass = GlobalExceptionHandler.class
    )
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

但是发现restTemplate中只有一个用于负载均衡的拦截器LoadBalancerInterceptor,就是说@SentinelRestTemplate未生效,@LoadBalanced生效了。
Spring Cloud Ablibaba Sentinel与Neflix Ribbon结合使用,@SentinelRestTemplate不生效问题_第1张图片

解决

开始以为@SentinelRestTemplate开关没打开,于是添加以下配置

#是否开启@SentinelRestTemplate注解
resttemplate:
  sentinel:
    enabled: true

配置后仍未生效。

最后发现将RestTemplate 配置在配置在主类才生效

@SpringBootApplication
public class SentinelOrderApplication {
    @Bean
    @LoadBalanced
    @SentinelRestTemplate(
            blockHandler = "handleException",blockHandlerClass = GlobalExceptionHandler.class,
            fallback = "fallback",fallbackClass = GlobalExceptionHandler.class
    )
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(SentinelOrderApplication.class);
    }
}

Spring Cloud Ablibaba Sentinel与Neflix Ribbon结合使用,@SentinelRestTemplate不生效问题_第2张图片

问题原因

源码分析

@LoadBalanced作用时机

smartSingleton.afterSingletonsInstantiated()

首先,经过查看@LoadBalanced源码发现,起作用的时机
finishBeanFactoryInitialization(beanFactory);
->beanFactory.preInstantiateSingletons();
->smartSingleton.afterSingletonsInstantiated();
   (loadBalancedRestTemplateInitializerDeprecated)
->customizer.customize(restTemplate);
   (restTemplateCustomizer)
最终起作用的是LoadBalancerAutoConfiguration配置类中的匿名内部类RestTemplateCustomizer ,通过此类添加了loadBalancerInterceptor拦截器

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

@SentinelTemplate作用时机

通过 SentinelBeanPostProcessor后置处理器的postProcessAfterInitialization方法实现sentinelProtectInterceptor拦截器的添加

@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		if (cache.containsKey(beanName)) {
			// add interceptor for each RestTemplate with @SentinelRestTemplate annotation
			StringBuilder interceptorBeanNamePrefix = new StringBuilder();
			SentinelRestTemplate sentinelRestTemplate = cache.get(beanName);
			interceptorBeanNamePrefix
					.append(StringUtils.uncapitalize(
							SentinelProtectInterceptor.class.getSimpleName()))
					.append("_")
					.append(sentinelRestTemplate.blockHandlerClass().getSimpleName())
					.append(sentinelRestTemplate.blockHandler()).append("_")
					.append(sentinelRestTemplate.fallbackClass().getSimpleName())
					.append(sentinelRestTemplate.fallback()).append("_")
					.append(sentinelRestTemplate.urlCleanerClass().getSimpleName())
					.append(sentinelRestTemplate.urlCleaner());
			RestTemplate restTemplate = (RestTemplate) bean;
			String interceptorBeanName = interceptorBeanNamePrefix + "@"
					+ bean.toString();
			registerBean(interceptorBeanName, sentinelRestTemplate, (RestTemplate) bean);
			SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
					.getBean(interceptorBeanName, SentinelProtectInterceptor.class);
			restTemplate.getInterceptors().add(0, sentinelProtectInterceptor);
		}
		return bean;
	}

通过postProcessMergedBeanDefinition方法判断是否有@SentinelRestemplate注解

	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
			Class<?> beanType, String beanName) {
		if (checkSentinelProtect(beanDefinition, beanType)) {
			SentinelRestTemplate sentinelRestTemplate;
			if (beanDefinition.getSource() instanceof StandardMethodMetadata) {
				sentinelRestTemplate = ((StandardMethodMetadata) beanDefinition
						.getSource()).getIntrospectedMethod()
								.getAnnotation(SentinelRestTemplate.class);
			}
			else {
				sentinelRestTemplate = beanDefinition.getResolvedFactoryMethod()
						.getAnnotation(SentinelRestTemplate.class);
			}
			// check class and method validation
			checkSentinelRestTemplate(sentinelRestTemplate, beanName);
			cache.put(beanName, sentinelRestTemplate);
		}
	}

判断方法

		private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
			Class<?> beanType) {
		return beanType == RestTemplate.class
				&& (checkStandardMethodMetadata(beanDefinition)
						|| checkMethodMetadataReadingVisitor(beanDefinition));
	}

在这个判断断时返回false导致

(checkStandardMethodMetadata(beanDefinition)|| checkMethodMetadataReadingVisitor(beanDefinition))

以上语句主要判断
beanDefinition.getSource() 是 StandardMethodMetadata类型,还是MethodMetadataReadingVisitor

但是在第一种情况将RestTemplate配置在MainConfig里面时beanDefinition.getSource() 却属于SimpleMethodMetadata,所以导致无法成功添加拦截器sentinelProtectInterceptor;

而在第二种情况,将RestTemplate配置在入口启动类时,beanDefinition.getSource() 为StandardMethodMetadata类型,所以能够添加成功。

Spring Boot版本过高

继续深入研究,发现是因为Spring Boot版本与Spring Cloud Ablibaba Sentinel版本不一致的问题导致,sprint-boot使用了2.2.4版本过高,将版本降为2.1.3即可。

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.2.4.RELEASEversion>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>Hoxton.SR1version>

                <type>pomtype>
                <scope>importscope>
            dependency>

            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-alibaba-dependenciesartifactId>
                <version>2.1.1.RELEASEversion>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

在2.1.3版本中使用的是

5.1.5.RELEASE

在2.2.4版本中使用的是

5.2.3.RELEASE

并且MethodMetadataReadingVisitor 被标注为Deprecated

@Deprecated
public class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata 

你可能感兴趣的:(spring,SpringBoot,SpringCloud)