今天使用和@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生效了。
开始以为@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);
}
}
首先,经过查看@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);
};
}
通过 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 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