From 4cbd4428fd856d2a23e85e7f1096c302566a5b5b Mon Sep 17 00:00:00 2001 From: fangjian0423 Date: Mon, 14 Jan 2019 16:09:34 +0800 Subject: [PATCH] SentinelRestTemplate block or fallback check in BeanPostProcessor, not runtime. --- .../alibaba/sentinel/SentinelConstants.java | 3 + .../custom/SentinelBeanPostProcessor.java | 73 ++++++++++++++ .../custom/SentinelProtectInterceptor.java | 94 +++++-------------- 3 files changed, 99 insertions(+), 71 deletions(-) diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java index 714cc08b..0c2a9d09 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java @@ -23,6 +23,9 @@ public interface SentinelConstants { String PROPERTY_PREFIX = "spring.cloud.sentinel"; + String BLOCK_TYPE = "block"; + String FALLBACK_TYPE = "fallback"; + // commercialization String FLOW_DATASOURCE_NAME = "edas-flow"; diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java index 41aaa563..ba2be853 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java @@ -16,8 +16,12 @@ package org.springframework.cloud.alibaba.sentinel.custom; +import java.lang.reflect.Method; +import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; @@ -25,13 +29,20 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.cloud.alibaba.sentinel.SentinelConstants; import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate; import org.springframework.context.ApplicationContext; import org.springframework.core.type.StandardMethodMetadata; import org.springframework.core.type.classreading.MethodMetadataReadingVisitor; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; +import com.alibaba.csp.sentinel.slots.block.BlockException; + /** * PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate * @@ -41,6 +52,9 @@ import org.springframework.web.client.RestTemplate; */ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor { + private static final Logger logger = LoggerFactory + .getLogger(SentinelBeanPostProcessor.class); + @Autowired private ApplicationContext applicationContext; @@ -60,10 +74,69 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces sentinelRestTemplate = beanDefinition.getResolvedFactoryMethod() .getAnnotation(SentinelRestTemplate.class); } + // check class and method validation + checkSentinelRestTemplate(sentinelRestTemplate, beanName); cache.put(beanName, sentinelRestTemplate); } } + private void checkSentinelRestTemplate(SentinelRestTemplate sentinelRestTemplate, + String beanName) { + checkBlock4RestTemplate(sentinelRestTemplate.blockHandlerClass(), + sentinelRestTemplate.blockHandler(), beanName, + SentinelConstants.BLOCK_TYPE); + checkBlock4RestTemplate(sentinelRestTemplate.fallbackClass(), + sentinelRestTemplate.fallback(), beanName, + SentinelConstants.FALLBACK_TYPE); + } + + private void checkBlock4RestTemplate(Class blockClass, String blockMethod, + String beanName, String type) { + if (blockClass == void.class && StringUtils.isEmpty(blockMethod)) { + return; + } + if (blockClass != void.class && StringUtils.isEmpty(blockMethod)) { + logger.error( + "{} class property exists but {}" + + " method property is not exists in bean[{}]", + type, type, beanName); + System.exit(-1); + } + else if (blockClass == void.class && !StringUtils.isEmpty(blockMethod)) { + logger.error( + "{} method property exists but {} class property is not exists in bean[{}]", + type, type, beanName); + System.exit(-1); + } + Class[] args = new Class[] { HttpRequest.class, byte[].class, + ClientHttpRequestExecution.class, BlockException.class }; + Method foundMethod = ClassUtils.getStaticMethod(blockClass, blockMethod, args); + if (foundMethod == null) { + logger.error( + "{} method can not be found in bean[{}]. The right method signature is {}#{}{}, please check your class name, method name and arguments", + type, beanName, blockClass.getName(), blockMethod, + Arrays.toString(Arrays.stream(args) + .map(clazz -> clazz.getSimpleName()).toArray())); + System.exit(-1); + } + + if (!ClientHttpResponse.class.isAssignableFrom(foundMethod.getReturnType())) { + logger.error( + "{} method return value in bean[{}] is not ClientHttpResponse: {}#{}{}", + type, beanName, blockClass.getName(), blockMethod, + Arrays.toString(Arrays.stream(args) + .map(clazz -> clazz.getSimpleName()).toArray())); + System.exit(-1); + } + if (type.equals(SentinelConstants.BLOCK_TYPE)) { + BlockClassRegistry.updateBlockHandlerFor(blockClass, blockMethod, + foundMethod); + } + else { + BlockClassRegistry.updateFallbackFor(blockClass, blockMethod, foundMethod); + } + } + private boolean checkSentinelProtect(RootBeanDefinition beanDefinition, Class beanType) { return beanType == RestTemplate.class diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java index a1ab9e22..7c5cd123 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java @@ -17,9 +17,9 @@ package org.springframework.cloud.alibaba.sentinel.custom; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; -import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +29,6 @@ import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; -import org.springframework.util.ClassUtils; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; @@ -37,12 +36,11 @@ import com.alibaba.csp.sentinel.Tracer; import com.alibaba.csp.sentinel.context.ContextUtil; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; -import com.alibaba.csp.sentinel.util.StringUtil; /** * Interceptor using by SentinelRestTemplate * - * @author fangjian + * @author Jim */ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor { @@ -82,18 +80,7 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor throw new IllegalStateException(e); } else { - try { - return handleBlockException(request, body, execution, - (BlockException) e); - } - catch (Exception ex) { - if (ex instanceof IllegalStateException) { - throw (IllegalStateException) ex; - } - throw new IllegalStateException( - "sentinel handle BlockException error: " + ex.getMessage(), - ex); - } + return handleBlockException(request, body, execution, (BlockException) e); } } finally { @@ -109,84 +96,49 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor } private ClientHttpResponse handleBlockException(HttpRequest request, byte[] body, - ClientHttpRequestExecution execution, BlockException ex) throws Exception { + ClientHttpRequestExecution execution, BlockException ex) { Object[] args = new Object[] { request, body, execution, ex }; // handle degrade if (isDegradeFailure(ex)) { - Method method = extractFallbackMethod(sentinelRestTemplate.fallback(), + Method fallbackMethod = extractFallbackMethod(sentinelRestTemplate.fallback(), sentinelRestTemplate.fallbackClass()); - if (method != null) { - return (ClientHttpResponse) method.invoke(null, args); + if (fallbackMethod != null) { + return methodInvoke(fallbackMethod, args); } else { return new SentinelClientHttpResponse(); } } - // handle block + // handle flow Method blockHandler = extractBlockHandlerMethod( sentinelRestTemplate.blockHandler(), sentinelRestTemplate.blockHandlerClass()); if (blockHandler != null) { - return (ClientHttpResponse) blockHandler.invoke(null, args); + return methodInvoke(blockHandler, args); } else { return new SentinelClientHttpResponse(); } } + private ClientHttpResponse methodInvoke(Method method, Object... args) { + try { + return (ClientHttpResponse) method.invoke(null, args); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + private Method extractFallbackMethod(String fallback, Class fallbackClass) { - if (StringUtil.isBlank(fallback) || fallbackClass == void.class) { - return null; - } - Method cachedMethod = BlockClassRegistry.lookupFallback(fallbackClass, fallback); - Class[] args = new Class[] { HttpRequest.class, byte[].class, - ClientHttpRequestExecution.class, BlockException.class }; - if (cachedMethod == null) { - cachedMethod = ClassUtils.getStaticMethod(fallbackClass, fallback, args); - if (cachedMethod != null) { - if (!ClientHttpResponse.class - .isAssignableFrom(cachedMethod.getReturnType())) { - throw new IllegalStateException(String.format( - "the return type of method [%s] in class [%s] is not ClientHttpResponse in degrade", - cachedMethod.getName(), fallbackClass.getCanonicalName())); - } - BlockClassRegistry.updateFallbackFor(fallbackClass, fallback, - cachedMethod); - } - else { - throw new IllegalStateException(String.format( - "Cannot find method [%s] in class [%s] with parameters %s in degrade", - fallback, fallbackClass.getCanonicalName(), Arrays.asList(args))); - } - } - return cachedMethod; + return BlockClassRegistry.lookupFallback(fallbackClass, fallback); } private Method extractBlockHandlerMethod(String block, Class blockClass) { - if (StringUtil.isBlank(block) || blockClass == void.class) { - return null; - } - Method cachedMethod = BlockClassRegistry.lookupBlockHandler(blockClass, block); - Class[] args = new Class[] { HttpRequest.class, byte[].class, - ClientHttpRequestExecution.class, BlockException.class }; - if (cachedMethod == null) { - cachedMethod = ClassUtils.getStaticMethod(blockClass, block, args); - if (cachedMethod != null) { - if (!ClientHttpResponse.class - .isAssignableFrom(cachedMethod.getReturnType())) { - throw new IllegalStateException(String.format( - "the return type of method [%s] in class [%s] is not ClientHttpResponse in flow control", - cachedMethod.getName(), blockClass.getCanonicalName())); - } - BlockClassRegistry.updateBlockHandlerFor(blockClass, block, cachedMethod); - } - else { - throw new IllegalStateException(String.format( - "Cannot find method [%s] in class [%s] with parameters %s in flow control", - block, blockClass.getCanonicalName(), Arrays.asList(args))); - } - } - return cachedMethod; + return BlockClassRegistry.lookupBlockHandler(blockClass, block); } private boolean isDegradeFailure(BlockException ex) {