Spring Cloud Alibaba Sentinel对RestTemplate的支持

Spring Cloud Alibaba Sentinel 支持对 RestTemplate 的服务调用使用 Sentinel 进行保护,在构造 RestTemplate bean的时候需要加上 @SentinelRestTemplate 注解。

需要注意的是目前的版本spring-cloud-starter-alibaba-sentinel.0.2.1.RELEASE在配置RestTemplate的时候有个Bug,需要将配置放在Spring Boot的启动类中,也就是@SpringBootApplication注解所在的类。

如果单独放在@Configuration标记的类中目前是有问题的,当然后续版本中会进行修复,对应的问题描述:github.com/spring-clou…

@Bean
@SentinelRestTemplate(fallback = "fallback", fallbackClass = ExceptionUtil.class, blockHandler="handleException",blockHandlerClass=ExceptionUtil.class)
public RestTemplate restTemplate() {
	return new RestTemplate();
}
复制代码
  • blockHandler 限流后处理的方法
  • blockHandlerClass 限流后处理的类
  • fallback 熔断后处理的方法
  • fallbackClass 熔断后处理的类

异常处理类定义需要注意的是该方法的参数跟返回值跟 org.springframework.http.client.ClientHttpRequestInterceptor#interceptor 方法一致,其中参数多出了一个 BlockException 参数用于获取 Sentinel 捕获的异常。

public class ExceptionUtil {
	public static SentinelClientHttpResponse handleException(HttpRequest request,
			byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
		System.err.println("Oops: " + ex.getClass().getCanonicalName());
		return new SentinelClientHttpResponse("custom block info");
	}
	
	public static SentinelClientHttpResponse fallback(HttpRequest request,
			byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
		System.err.println("fallback: " + ex.getClass().getCanonicalName());
		return new SentinelClientHttpResponse("custom fallback info");
	}
}

复制代码

原理剖析

核心代码在org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor中,实现了MergedBeanDefinitionPostProcessor接口,MergedBeanDefinitionPostProcessor接口实现了BeanPostProcessor接口。

核心方法就是重写的postProcessMergedBeanDefinition和postProcessAfterInitialization。

postProcessMergedBeanDefinition

private ConcurrentHashMap<String, SentinelRestTemplate> cache = new ConcurrentHashMap<>();

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
			Class<?> beanType, String beanName) {
	if (checkSentinelProtect(beanDefinition, beanType)) {
	    SentinelRestTemplate sentinelRestTemplate = ((StandardMethodMetadata) beanDefinition
					.getSource()).getIntrospectedMethod()
							.getAnnotation(SentinelRestTemplate.class);
        // 获取SentinelRestTemplate注解对象存储起来
		cache.put(beanName, sentinelRestTemplate);
	}
}
// 判断bean是否加了SentinelRestTemplate注解并且是RestTemplate
private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
			Class<?> beanType) {
	return beanType == RestTemplate.class
				&& beanDefinition.getSource() instanceof StandardMethodMetadata
				&& ((StandardMethodMetadata) beanDefinition.getSource())
						.isAnnotated(SentinelRestTemplate.class.getName());
}
复制代码

postProcessAfterInitialization

@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
	if (cache.containsKey(beanName)) {
		// add interceptor for each RestTemplate with @SentinelRestTemplate annotation
		StringBuilder interceptorBeanName = new StringBuilder();
        // 缓存中得到注解对象
		SentinelRestTemplate sentinelRestTemplate = cache.get(beanName);
        // 生成interceptorBeanName SentinelProtectInterceptor名称
		interceptorBeanName
					.append(StringUtils.uncapitalize(
							SentinelProtectInterceptor.class.getSimpleName()))
					.append("_")
					.append(sentinelRestTemplate.blockHandlerClass().getSimpleName())
					.append(sentinelRestTemplate.blockHandler()).append("_")
					.append(sentinelRestTemplate.fallbackClass().getSimpleName())
					.append(sentinelRestTemplate.fallback());
		RestTemplate restTemplate = (RestTemplate) bean;
        // 注册SentinelProtectInterceptor
		registerBean(interceptorBeanName.toString(), sentinelRestTemplate);
        // 获取SentinelProtectInterceptor
		SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
					.getBean(interceptorBeanName.toString(),
							SentinelProtectInterceptor.class);
        // 给restTemplate添加拦截器
		restTemplate.getInterceptors().add(sentinelProtectInterceptor);
	}
	return bean;
}
// 注册SentinelProtectInterceptor类
private void registerBean(String interceptorBeanName,
			SentinelRestTemplate sentinelRestTemplate) {
	// register SentinelProtectInterceptor bean
	DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
				.getAutowireCapableBeanFactory();
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
				.genericBeanDefinition(SentinelProtectInterceptor.class);
	beanDefinitionBuilder.addConstructorArgValue(sentinelRestTemplate);
	BeanDefinition interceptorBeanDefinition = beanDefinitionBuilder
				.getRawBeanDefinition();
	beanFactory.registerBeanDefinition(interceptorBeanName,
				interceptorBeanDefinition);
}
复制代码

看到这边大家就明白了,其实就是给restTemplate添加拦截器来处理。跟Ribbon中的@LoadBalanced原理是一样的。

SentinelProtectInterceptor

Sentinel RestTemplate 限流的资源规则提供两种粒度:

  • schema://host:port/path:协议、主机、端口和路径
  • schema://host:port:协议、主机和端口

这两种粒度从org.springframework.cloud.alibaba.sentinel.custom.SentinelProtectInterceptor.intercept(HttpRequest, byte[], ClientHttpRequestExecution)方法中可以看的出来

URI uri = request.getURI();
String hostResource = uri.getScheme() + "://" + uri.getHost()
			+ (uri.getPort() == -1 ? "" : ":" + uri.getPort());
String hostWithPathResource = hostResource + uri.getPath();
复制代码

下面就是根据hostResource和hostWithPathResource进行限流

ContextUtil.enter(hostWithPathResource);
if (entryWithPath) {
	hostWithPathEntry = SphU.entry(hostWithPathResource);
}
hostEntry = SphU.entry(hostResource);
// 执行Http调用
response = execution.execute(request, body);
复制代码

在后面就是释放资源,异常处理等代码,大家自己去了解下。

欢迎加入我的知识星球,一起交流技术,免费学习猿天地的课程(cxytiandi.com/course)

PS:目前星球中正在星主的带领下组队学习Sentinel,等你哦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值