diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java index 93183382c..f1fe28e6d 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java @@ -2,7 +2,7 @@ package org.springframework.cloud.alibaba.cloud.examples; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect; +import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @@ -15,7 +15,7 @@ import com.alibaba.csp.sentinel.datasource.Converter; public class ServiceApplication { @Bean - @SentinelProtect(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class) + @SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class) public RestTemplate restTemplate() { return new RestTemplate(); } diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelProtect.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelRestTemplate.java similarity index 79% rename from spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelProtect.java rename to spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelRestTemplate.java index 6586c8d31..da1a3c26a 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelProtect.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelRestTemplate.java @@ -16,7 +16,11 @@ package org.springframework.cloud.alibaba.sentinel.annotation; -import java.lang.annotation.*; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * @author fangjian @@ -24,7 +28,7 @@ import java.lang.annotation.*; @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented -public @interface SentinelProtect { +public @interface SentinelRestTemplate { String blockHandler() default ""; 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 fdad96641..4fb9a6cf5 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 @@ -25,87 +25,88 @@ 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.annotation.SentinelProtect; +import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate; import org.springframework.context.ApplicationContext; import org.springframework.core.type.StandardMethodMetadata; import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; /** - * PostProcessor handle @SentinelProtect Annotation, add interceptor for RestTemplate + * PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate * * @author Jim - * @see SentinelProtect + * @see SentinelRestTemplate * @see SentinelProtectInterceptor */ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor { - @Autowired - private ApplicationContext applicationContext; + @Autowired + private ApplicationContext applicationContext; - private ConcurrentHashMap cache = new ConcurrentHashMap<>(); + private ConcurrentHashMap cache = new ConcurrentHashMap<>(); - @Override - public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, - Class beanType, String beanName) { - if (checkSentinelProtect(beanDefinition, beanType)) { - SentinelProtect sentinelProtect = ((StandardMethodMetadata) beanDefinition - .getSource()).getIntrospectedMethod() - .getAnnotation(SentinelProtect.class); - cache.put(beanName, sentinelProtect); - } - } + @Override + public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, + Class beanType, String beanName) { + if (checkSentinelProtect(beanDefinition, beanType)) { + SentinelRestTemplate sentinelRestTemplate = ((StandardMethodMetadata) beanDefinition + .getSource()).getIntrospectedMethod() + .getAnnotation(SentinelRestTemplate.class); + cache.put(beanName, sentinelRestTemplate); + } + } - private boolean checkSentinelProtect(RootBeanDefinition beanDefinition, - Class beanType) { - return beanType == RestTemplate.class - && beanDefinition.getSource() instanceof StandardMethodMetadata - && ((StandardMethodMetadata) beanDefinition.getSource()) - .isAnnotated(SentinelProtect.class.getName()); - } + 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 @SentinelProtect annotation - StringBuilder interceptorBeanName = new StringBuilder(); - SentinelProtect sentinelProtect = cache.get(beanName); - interceptorBeanName - .append(StringUtils.uncapitalize( - SentinelProtectInterceptor.class.getSimpleName())) - .append("_") - .append(sentinelProtect.blockHandlerClass().getSimpleName()) - .append(sentinelProtect.blockHandler()).append("_") - .append(sentinelProtect.fallbackClass().getSimpleName()) - .append(sentinelProtect.fallback()); - RestTemplate restTemplate = (RestTemplate) bean; - registerBean(interceptorBeanName.toString(), sentinelProtect); - SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext - .getBean(interceptorBeanName.toString(), - SentinelProtectInterceptor.class); - restTemplate.getInterceptors().add(sentinelProtectInterceptor); - } - return bean; - } + @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 + .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; + registerBean(interceptorBeanName.toString(), sentinelRestTemplate); + SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext + .getBean(interceptorBeanName.toString(), + SentinelProtectInterceptor.class); + restTemplate.getInterceptors().add(sentinelProtectInterceptor); + } + return bean; + } - private void registerBean(String interceptorBeanName, - SentinelProtect sentinelProtect) { - // register SentinelProtectInterceptor bean - DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext - .getAutowireCapableBeanFactory(); - BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder - .genericBeanDefinition(SentinelProtectInterceptor.class); - beanDefinitionBuilder.addConstructorArgValue(sentinelProtect); - BeanDefinition interceptorBeanDefinition = beanDefinitionBuilder - .getRawBeanDefinition(); - beanFactory.registerBeanDefinition(interceptorBeanName, - interceptorBeanDefinition); - } + 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); + } - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - return bean; - } + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + return bean; + } } \ No newline at end of file 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 dc22262f4..f8fa08209 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 @@ -22,7 +22,7 @@ import java.net.URI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect; +import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; @@ -37,103 +37,104 @@ import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; import com.alibaba.csp.sentinel.util.StringUtil; /** - * Interceptor using by SentinelProtect and SentinelProtectInterceptor + * Interceptor using by SentinelRestTemplate and SentinelProtectInterceptor * * @author fangjian */ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor { - private static final Logger logger = LoggerFactory - .getLogger(SentinelProtectInterceptor.class); + private static final Logger logger = LoggerFactory + .getLogger(SentinelProtectInterceptor.class); - private SentinelProtect sentinelProtect; + private SentinelRestTemplate sentinelRestTemplate; - public SentinelProtectInterceptor(SentinelProtect sentinelProtect) { - this.sentinelProtect = sentinelProtect; - } + public SentinelProtectInterceptor(SentinelRestTemplate sentinelRestTemplate) { + this.sentinelRestTemplate = sentinelRestTemplate; + } - @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, - ClientHttpRequestExecution execution) throws IOException { - URI uri = request.getURI(); - String hostResource = uri.getScheme() + "://" + uri.getHost() + ":" - + (uri.getPort() == -1 ? 80 : uri.getPort()); - String hostWithPathResource = hostResource + uri.getPath(); - Entry hostEntry = null, hostWithPathEntry = null; - ClientHttpResponse response = null; - try { - ContextUtil.enter(hostWithPathResource); - hostWithPathEntry = SphU.entry(hostWithPathResource); - hostEntry = SphU.entry(hostResource); - response = execution.execute(request, body); - } - catch (BlockException e) { - logger.error("RestTemplate block", e); - try { - handleBlockException(e); - } - catch (Exception ex) { - logger.error("sentinel handle BlockException error.", e); - } - } - finally { - if (hostEntry != null) { - hostEntry.exit(); - } - if (hostWithPathEntry != null) { - hostWithPathEntry.exit(); - } - ContextUtil.exit(); - } - return response; - } + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, + ClientHttpRequestExecution execution) throws IOException { + URI uri = request.getURI(); + String hostResource = uri.getScheme() + "://" + uri.getHost() + ":" + + (uri.getPort() == -1 ? 80 : uri.getPort()); + String hostWithPathResource = hostResource + uri.getPath(); + Entry hostEntry = null, hostWithPathEntry = null; + ClientHttpResponse response = null; + try { + ContextUtil.enter(hostWithPathResource); + hostWithPathEntry = SphU.entry(hostWithPathResource); + hostEntry = SphU.entry(hostResource); + response = execution.execute(request, body); + } + catch (BlockException e) { + logger.error("RestTemplate block", e); + try { + handleBlockException(e); + } + catch (Exception ex) { + logger.error("sentinel handle BlockException error.", e); + } + } + finally { + if (hostEntry != null) { + hostEntry.exit(); + } + if (hostWithPathEntry != null) { + hostWithPathEntry.exit(); + } + ContextUtil.exit(); + } + return response; + } - private void handleBlockException(BlockException ex) throws Exception { - Object[] args = new Object[] { ex }; - // handle degrade - if (isDegradeFailure(ex)) { - Method method = extractFallbackMethod(sentinelProtect.fallback(), - sentinelProtect.fallbackClass()); - if (method != null) { - method.invoke(null, args); - } - } - // handle block - Method blockHandler = extractBlockHandlerMethod(sentinelProtect.blockHandler(), - sentinelProtect.blockHandlerClass()); - if (blockHandler != null) { - blockHandler.invoke(null, args); - } - } + private void handleBlockException(BlockException ex) throws Exception { + Object[] args = new Object[] { ex }; + // handle degrade + if (isDegradeFailure(ex)) { + Method method = extractFallbackMethod(sentinelRestTemplate.fallback(), + sentinelRestTemplate.fallbackClass()); + if (method != null) { + method.invoke(null, args); + } + } + // handle block + Method blockHandler = extractBlockHandlerMethod( + sentinelRestTemplate.blockHandler(), + sentinelRestTemplate.blockHandlerClass()); + if (blockHandler != null) { + blockHandler.invoke(null, args); + } + } - private Method extractFallbackMethod(String fallback, Class fallbackClass) { - if (StringUtil.isBlank(fallback) || fallbackClass == void.class) { - return null; - } - Method cachedMethod = BlockClassRegistry.lookupFallback(fallbackClass, fallback); - if (cachedMethod == null) { - cachedMethod = ClassUtils.getStaticMethod(fallbackClass, fallback, - BlockException.class); - BlockClassRegistry.updateFallbackFor(fallbackClass, fallback, cachedMethod); - } - return cachedMethod; - } + private Method extractFallbackMethod(String fallback, Class fallbackClass) { + if (StringUtil.isBlank(fallback) || fallbackClass == void.class) { + return null; + } + Method cachedMethod = BlockClassRegistry.lookupFallback(fallbackClass, fallback); + if (cachedMethod == null) { + cachedMethod = ClassUtils.getStaticMethod(fallbackClass, fallback, + BlockException.class); + BlockClassRegistry.updateFallbackFor(fallbackClass, fallback, cachedMethod); + } + return cachedMethod; + } - private Method extractBlockHandlerMethod(String block, Class blockClass) { - if (StringUtil.isBlank(block) || blockClass == void.class) { - return null; - } - Method cachedMethod = BlockClassRegistry.lookupBlockHandler(blockClass, block); - if (cachedMethod == null) { - cachedMethod = ClassUtils.getStaticMethod(blockClass, block, - BlockException.class); - BlockClassRegistry.updateBlockHandlerFor(blockClass, block, cachedMethod); - } - return cachedMethod; - } + private Method extractBlockHandlerMethod(String block, Class blockClass) { + if (StringUtil.isBlank(block) || blockClass == void.class) { + return null; + } + Method cachedMethod = BlockClassRegistry.lookupBlockHandler(blockClass, block); + if (cachedMethod == null) { + cachedMethod = ClassUtils.getStaticMethod(blockClass, block, + BlockException.class); + BlockClassRegistry.updateBlockHandlerFor(blockClass, block, cachedMethod); + } + return cachedMethod; + } - private boolean isDegradeFailure(BlockException ex) { - return ex instanceof DegradeException; - } + private boolean isDegradeFailure(BlockException ex) { + return ex instanceof DegradeException; + } } diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java index 957e2a6a3..eaa682ca3 100644 --- a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java +++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java @@ -16,14 +16,14 @@ package org.springframework.cloud.alibaba.sentinel; -import com.alibaba.csp.sentinel.slots.block.BlockException; +import static org.assertj.core.api.Assertions.assertThat; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect; +import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate; import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration; import org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor; import org.springframework.cloud.alibaba.sentinel.custom.SentinelProtectInterceptor; @@ -32,84 +32,86 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; -import static org.assertj.core.api.Assertions.assertThat; +import com.alibaba.csp.sentinel.slots.block.BlockException; /** * @author fangjian */ public class SentinelAutoConfigurationTests { - private final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); - - @Before - public void init() { - context.register(SentinelAutoConfiguration.class, SentinelWebAutoConfiguration.class, - SentinelTestConfiguration.class); - EnvironmentTestUtils.addEnvironment(this.context, - "spring.cloud.sentinel.transport.port=8888", - "spring.cloud.sentinel.filter.order=123", - "spring.cloud.sentinel.filter.urlPatterns=/*,/test"); - this.context.refresh(); - } - - @After - public void closeContext() { - this.context.close(); - } - - @Test - public void testFilter() { - assertThat(context.getBean( - "servletRequestListener").getClass() == FilterRegistrationBean.class).isTrue(); - } - - @Test - public void testBeanPostProcessor() { - assertThat(context.getBean("sentinelBeanPostProcessor") - .getClass() == SentinelBeanPostProcessor.class).isTrue(); - } - - @Test - public void testProperties() { - SentinelProperties sentinelProperties = context.getBean(SentinelProperties.class); - assertThat(sentinelProperties).isNotNull(); - assertThat(sentinelProperties.getTransport().getPort()).isEqualTo("8888"); - assertThat(sentinelProperties.getFilter().getUrlPatterns().size()).isEqualTo(2); - assertThat(sentinelProperties.getFilter().getUrlPatterns().get(0)).isEqualTo("/*"); - assertThat(sentinelProperties.getFilter().getUrlPatterns().get(1)).isEqualTo("/test"); - } - - @Test - public void testRestTemplate() { - assertThat(context.getBeansOfType(RestTemplate.class).size()).isEqualTo(2); - RestTemplate restTemplate = context.getBean("restTemplateWithBlockClass", - RestTemplate.class); - assertThat(restTemplate.getInterceptors().size()).isEqualTo(1); - assertThat(restTemplate.getInterceptors().get(0).getClass()) - .isEqualTo(SentinelProtectInterceptor.class); - } - - @Configuration - static class SentinelTestConfiguration { - - @Bean - @SentinelProtect - RestTemplate restTemplate() { - return new RestTemplate(); - } - - @Bean - @SentinelProtect(blockHandlerClass = ExceptionUtil.class, blockHandler = "handleException") - RestTemplate restTemplateWithBlockClass() { - return new RestTemplate(); - } - - } - - static class ExceptionUtil { - public static void handleException(BlockException ex) { - System.out.println("Oops: " + ex.getClass().getCanonicalName()); - } - } + private final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + + @Before + public void init() { + context.register(SentinelAutoConfiguration.class, + SentinelWebAutoConfiguration.class, SentinelTestConfiguration.class); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.cloud.sentinel.transport.port=8888", + "spring.cloud.sentinel.filter.order=123", + "spring.cloud.sentinel.filter.urlPatterns=/*,/test"); + this.context.refresh(); + } + + @After + public void closeContext() { + this.context.close(); + } + + @Test + public void testFilter() { + assertThat(context.getBean("servletRequestListener") + .getClass() == FilterRegistrationBean.class).isTrue(); + } + + @Test + public void testBeanPostProcessor() { + assertThat(context.getBean("sentinelBeanPostProcessor") + .getClass() == SentinelBeanPostProcessor.class).isTrue(); + } + + @Test + public void testProperties() { + SentinelProperties sentinelProperties = context.getBean(SentinelProperties.class); + assertThat(sentinelProperties).isNotNull(); + assertThat(sentinelProperties.getTransport().getPort()).isEqualTo("8888"); + assertThat(sentinelProperties.getFilter().getUrlPatterns().size()).isEqualTo(2); + assertThat(sentinelProperties.getFilter().getUrlPatterns().get(0)) + .isEqualTo("/*"); + assertThat(sentinelProperties.getFilter().getUrlPatterns().get(1)) + .isEqualTo("/test"); + } + + @Test + public void testRestTemplate() { + assertThat(context.getBeansOfType(RestTemplate.class).size()).isEqualTo(2); + RestTemplate restTemplate = context.getBean("restTemplateWithBlockClass", + RestTemplate.class); + assertThat(restTemplate.getInterceptors().size()).isEqualTo(1); + assertThat(restTemplate.getInterceptors().get(0).getClass()) + .isEqualTo(SentinelProtectInterceptor.class); + } + + @Configuration + static class SentinelTestConfiguration { + + @Bean + @SentinelRestTemplate + RestTemplate restTemplate() { + return new RestTemplate(); + } + + @Bean + @SentinelRestTemplate(blockHandlerClass = ExceptionUtil.class, blockHandler = "handleException") + RestTemplate restTemplateWithBlockClass() { + return new RestTemplate(); + } + + } + + static class ExceptionUtil { + public static void handleException(BlockException ex) { + System.out.println("Oops: " + ex.getClass().getCanonicalName()); + } + } }