博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Cloud Alibaba Sentinel对RestTemplate的支持
阅读量:7117 次
发布时间:2019-06-28

本文共 5467 字,大约阅读时间需要 18 分钟。

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

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

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

@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
cache = new ConcurrentHashMap<>();@Overridepublic 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注解并且是RestTemplateprivate boolean checkSentinelProtect(RootBeanDefinition beanDefinition, Class
beanType) { return beanType == RestTemplate.class && beanDefinition.getSource() instanceof StandardMethodMetadata && ((StandardMethodMetadata) beanDefinition.getSource()) .isAnnotated(SentinelRestTemplate.class.getName());}复制代码

postProcessAfterInitialization

@Overridepublic 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);复制代码

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

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

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

转载地址:http://kkdel.baihongyu.com/

你可能感兴趣的文章
探讨.net Socket支持在线连接数量
查看>>
InstallShield在MySQL和Oracle中执行SQL脚本的方法InstallShield在MySQL和Oracle中执行SQL脚本的方法...
查看>>
Toad 所有 菜单说明(太多)
查看>>
12306登录界面的代码流程
查看>>
POJ 1860 - Currency Exchange
查看>>
基于springmvc的简单增删改查实现---中间使用到了bean validation
查看>>
LeetCode总结 -- 树的性质篇
查看>>
聊聊分布式事务
查看>>
第一次使用crontab linux选择编辑器问题
查看>>
模仿mybatis,用jdk proxy实现接口
查看>>
HTML学习笔记之二(回到顶部 与 回究竟部)
查看>>
Node.js之HTTP请求与响应
查看>>
数据库查询语句研究
查看>>
为什么要原型设计
查看>>
java.lang.IllegalThreadStateException异常原因解析
查看>>
读CopyOnWriteArrayList有感
查看>>
程序员的成长阶梯和级别定义(转)
查看>>
HTTP Authorization
查看>>
Js判断出生年月填写的 是否正确
查看>>
hiho1093_spfa
查看>>