Spring Cloud Alibaba Sentinel 支持對 RestTemplate 的服務調用使用 Sentinel 進行保護,在構造 RestTemplate bean的時候須要加上 @SentinelRestTemplate 註解。git
須要注意的是目前的版本spring-cloud-starter-alibaba-sentinel.0.2.1.RELEASE在配置RestTemplate的時候有個Bug,須要將配置放在Spring Boot的啓動類中,也就是@SpringBootApplication註解所在的類。github
若是單獨放在@Configuration標記的類中目前是有問題的,固然後續版本中會進行修復,對應的問題描述:https://github.com/spring-cloud-incubator/spring-cloud-alibaba/issues/227spring
@Bean @SentinelRestTemplate(fallback = "fallback", fallbackClass = ExceptionUtil.class, blockHandler="handleException",blockHandlerClass=ExceptionUtil.class) public RestTemplate restTemplate() { return new RestTemplate(); }
限流後處理的方法api
限流後處理的類緩存
熔斷後處理的方法微信
熔斷後處理的類app
異常處理類定義須要注意的是該方法的參數跟返回值跟 org.springframework.http.client.ClientHttpRequestInterceptor#interceptor 方法一致,其中參數多出了一個 BlockException 參數用於獲取 Sentinel 捕獲的異常。ide
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接口。post
核心方法就是重寫的postProcessMergedBeanDefinition和postProcessAfterInitialization。學習
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()); }
@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原理是同樣的。
Sentinel RestTemplate 限流的資源規則提供兩種粒度:
這兩種粒度從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);
在後面就是釋放資源,異常處理等代碼,你們本身去了解下。