From be7dace3d1b01e5e35dbf9cf3e54d4cc501989f5 Mon Sep 17 00:00:00 2001 From: Rivers-Shall Date: Mon, 19 Aug 2019 13:53:54 +0800 Subject: [PATCH 1/7] Add `urlCleaner` and `urlCleanerClass` configurations to annotation `SentinelRestTemplate` --- .../cloud/sentinel/annotation/SentinelRestTemplate.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/annotation/SentinelRestTemplate.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/annotation/SentinelRestTemplate.java index 2638ee33..039c9b16 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/annotation/SentinelRestTemplate.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/annotation/SentinelRestTemplate.java @@ -38,4 +38,7 @@ public @interface SentinelRestTemplate { Class fallbackClass() default void.class; + String urlCleaner() default ""; + + Class urlCleanerClass() default void.class; } From 370512cafd4c7800107a89f30dd5ce54c24b6c71 Mon Sep 17 00:00:00 2001 From: Rivers-Shall Date: Mon, 19 Aug 2019 15:58:31 +0800 Subject: [PATCH 2/7] Check and store `UrlCleaner` and `UrlCleanerClass` configurations of every `SentinelResttemplate` annotation --- .../cloud/sentinel/SentinelConstants.java | 1 + .../sentinel/custom/BlockClassRegistry.java | 13 +++++++ .../custom/SentinelBeanPostProcessor.java | 38 +++++++++++++++---- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelConstants.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelConstants.java index 921458e3..7501ee49 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelConstants.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelConstants.java @@ -25,6 +25,7 @@ public interface SentinelConstants { String BLOCK_TYPE = "block"; String FALLBACK_TYPE = "fallback"; + String URLCLEANER_TYPE = "urlCleaner"; // commercialization diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/BlockClassRegistry.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/BlockClassRegistry.java index 1e5d6c8b..f1092018 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/BlockClassRegistry.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/BlockClassRegistry.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.alibaba.csp.sentinel.util.StringUtil; +import org.springframework.util.StringUtils; /** * @author fangjian @@ -29,6 +30,7 @@ final class BlockClassRegistry { private static final Map FALLBACK_MAP = new ConcurrentHashMap<>(); private static final Map BLOCK_HANDLER_MAP = new ConcurrentHashMap<>(); + private static final Map URL_CLEANER_MAP = new ConcurrentHashMap<>(); static Method lookupFallback(Class clazz, String name) { return FALLBACK_MAP.get(getKey(clazz, name)); @@ -38,6 +40,10 @@ final class BlockClassRegistry { return BLOCK_HANDLER_MAP.get(getKey(clazz, name)); } + static Method lookupUrlCleaner(Class clazz, String name) { + return URL_CLEANER_MAP.get(getKey(clazz, name)); + } + static void updateFallbackFor(Class clazz, String name, Method method) { if (clazz == null || StringUtil.isBlank(name)) { throw new IllegalArgumentException("Bad argument"); @@ -52,6 +58,13 @@ final class BlockClassRegistry { BLOCK_HANDLER_MAP.put(getKey(clazz, name), method); } + static void updateUrlCleanerFor(Class clazz, String name, Method method) { + if (clazz == null || StringUtil.isBlank(name)) { + throw new IllegalArgumentException("Bad argument"); + } + URL_CLEANER_MAP.put(getKey(clazz, name), method); + } + private static String getKey(Class clazz, String name) { return String.format("%s:%s", clazz.getCanonicalName(), name); } diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelBeanPostProcessor.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelBeanPostProcessor.java index 178060bf..d9c150c0 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelBeanPostProcessor.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelBeanPostProcessor.java @@ -90,6 +90,9 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces checkBlock4RestTemplate(sentinelRestTemplate.fallbackClass(), sentinelRestTemplate.fallback(), beanName, SentinelConstants.FALLBACK_TYPE); + checkBlock4RestTemplate(sentinelRestTemplate.urlCleanerClass(), + sentinelRestTemplate.urlCleaner(), beanName, + SentinelConstants.URLCLEANER_TYPE); } private void checkBlock4RestTemplate(Class blockClass, String blockMethod, @@ -111,8 +114,14 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces throw new IllegalArgumentException(type + " method attribute exists but " + type + " class attribute is not exists in bean[" + beanName + "]"); } - Class[] args = new Class[] { HttpRequest.class, byte[].class, - ClientHttpRequestExecution.class, BlockException.class }; + Class[] args; + if (type.equals(SentinelConstants.URLCLEANER_TYPE)) { + args = new Class[] { String.class }; + } + else { + args = new Class[] { HttpRequest.class, byte[].class, + ClientHttpRequestExecution.class, BlockException.class }; + } String argsStr = Arrays.toString( Arrays.stream(args).map(clazz -> clazz.getSimpleName()).toArray()); Method foundMethod = ClassUtils.getStaticMethod(blockClass, blockMethod, args); @@ -127,10 +136,18 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces + ", please check your class name, method name and arguments"); } - if (!ClientHttpResponse.class.isAssignableFrom(foundMethod.getReturnType())) { - log.error( - "{} method return value in bean[{}] is not ClientHttpResponse: {}#{}{}", - type, beanName, blockClass.getName(), blockMethod, argsStr); + Class standardReturnType; + if (type.equals(SentinelConstants.URLCLEANER_TYPE)) { + standardReturnType = String.class; + } + else { + standardReturnType = ClientHttpResponse.class; + } + + if (!standardReturnType.isAssignableFrom(foundMethod.getReturnType())) { + log.error("{} method return value in bean[{}] is not {}: {}#{}{}", type, + beanName, standardReturnType.getName(), blockClass.getName(), + blockMethod, argsStr); throw new IllegalArgumentException(type + " method return value in bean[" + beanName + "] is not ClientHttpResponse: " + blockClass.getName() + "#" + blockMethod + argsStr); @@ -139,9 +156,12 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces BlockClassRegistry.updateBlockHandlerFor(blockClass, blockMethod, foundMethod); } - else { + else if (type.equals(SentinelConstants.FALLBACK_TYPE)) { BlockClassRegistry.updateFallbackFor(blockClass, blockMethod, foundMethod); } + else { + BlockClassRegistry.updateUrlCleanerFor(blockClass, blockMethod, foundMethod); + } } private boolean checkSentinelProtect(RootBeanDefinition beanDefinition, @@ -177,7 +197,9 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces .append(sentinelRestTemplate.blockHandlerClass().getSimpleName()) .append(sentinelRestTemplate.blockHandler()).append("_") .append(sentinelRestTemplate.fallbackClass().getSimpleName()) - .append(sentinelRestTemplate.fallback()); + .append(sentinelRestTemplate.fallback()).append("_") + .append(sentinelRestTemplate.urlCleanerClass().getSimpleName()) + .append(sentinelRestTemplate.urlCleaner()); RestTemplate restTemplate = (RestTemplate) bean; String interceptorBeanName = interceptorBeanNamePrefix + "@" + bean.toString(); From 44f073cdb4f498a62165f8cf843a761c4b20b8d0 Mon Sep 17 00:00:00 2001 From: Rivers-Shall Date: Mon, 19 Aug 2019 15:59:45 +0800 Subject: [PATCH 3/7] Use public static method `UrlCleaner` in `UrlCleanerClass` to clean `hostWithPathResource` --- .../custom/SentinelProtectInterceptor.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelProtectInterceptor.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelProtectInterceptor.java index 4594caef..fd91016e 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelProtectInterceptor.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelProtectInterceptor.java @@ -65,6 +65,24 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor if (hostResource.equals(hostWithPathResource)) { entryWithPath = false; } + Method urlCleanerMethod = BlockClassRegistry.lookupUrlCleaner( + sentinelRestTemplate.urlCleanerClass(), + sentinelRestTemplate.urlCleaner()); + if (urlCleanerMethod != null) { + try { + hostWithPathResource = (String) urlCleanerMethod.invoke(null, + hostWithPathResource); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + System.out.println("hostWithPathResource: " + hostWithPathResource); + System.out.println("entryWithPath: " + entryWithPath); + Entry hostEntry = null, hostWithPathEntry = null; ClientHttpResponse response = null; try { From 57ec776c7b531209a9703742ea63f6eca4794828 Mon Sep 17 00:00:00 2001 From: Rivers-Shall Date: Mon, 19 Aug 2019 16:02:01 +0800 Subject: [PATCH 4/7] Modify `nacos-discovery-example` to test UrlCleaner of `SentinelResttemplate` --- .../alibaba/cloud/examples/ConsumerApplication.java | 1 + .../java/com/alibaba/cloud/examples/UrlCleaner.java | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/UrlCleaner.java diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java index ab4ffd7e..984d1161 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java @@ -26,6 +26,7 @@ public class ConsumerApplication { @LoadBalanced @Bean + @SentinelRestTemplate(urlCleanerClass = UrlCleaner.class, urlCleaner = "clean") public RestTemplate restTemplate() { return new RestTemplate(); } diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/UrlCleaner.java b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/UrlCleaner.java new file mode 100644 index 00000000..4eab398c --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/UrlCleaner.java @@ -0,0 +1,12 @@ +package com.alibaba.cloud.examples; + +public class UrlCleaner { + public static String clean(String url) { + System.out.println("enter urlCleaner"); + if (url.matches(".*/echo/.*")) { + System.out.println("change url"); + url = url.replaceAll("/echo/.*", "/echo/{str}"); + } + return url; + } +} From 578d51c7da7a706b5e3fb237a288522669acd1e1 Mon Sep 17 00:00:00 2001 From: Rivers-Shall Date: Tue, 20 Aug 2019 20:51:31 +0800 Subject: [PATCH 5/7] Add tests for `urlCleanerClass` and `urlCleaner` in `SentinelRestTemplate` --- .../sentinel/SentinelRestTemplateTests.java | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/spring-cloud-alibaba-sentinel/src/test/java/com/alibaba/cloud/sentinel/SentinelRestTemplateTests.java b/spring-cloud-alibaba-sentinel/src/test/java/com/alibaba/cloud/sentinel/SentinelRestTemplateTests.java index d05624fb..29967378 100644 --- a/spring-cloud-alibaba-sentinel/src/test/java/com/alibaba/cloud/sentinel/SentinelRestTemplateTests.java +++ b/spring-cloud-alibaba-sentinel/src/test/java/com/alibaba/cloud/sentinel/SentinelRestTemplateTests.java @@ -91,6 +91,26 @@ public class SentinelRestTemplateTests { new AnnotationConfigApplicationContext(TestConfig10.class); } + @Test(expected = BeanCreationException.class) + public void testUrlClnMethod() { + new AnnotationConfigApplicationContext(TestConfig11.class); + } + + @Test(expected = BeanCreationException.class) + public void testUrlClnClass() { + new AnnotationConfigApplicationContext(TestConfig12.class); + } + + @Test(expected = BeanCreationException.class) + public void testUrlClnMethodExists() { + new AnnotationConfigApplicationContext(TestConfig13.class); + } + + @Test(expected = BeanCreationException.class) + public void testUrlClnReturnValue() { + new AnnotationConfigApplicationContext(TestConfig14.class); + } + @Configuration public static class TestConfig1 { @Bean @@ -160,7 +180,7 @@ public class SentinelRestTemplateTests { } @Bean - @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException", fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException") + @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException", fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException", urlCleanerClass = SentinelRestTemplateTests.UrlCleanUtil.class, urlCleaner = "clean") RestTemplate restTemplate() { return new RestTemplate(); } @@ -247,6 +267,66 @@ public class SentinelRestTemplateTests { } } + @Configuration + public static class TestConfig11 { + @Bean + SentinelBeanPostProcessor sentinelBeanPostProcessor( + ApplicationContext applicationContext) { + return new SentinelBeanPostProcessor(applicationContext); + } + + @Bean + @SentinelRestTemplate(urlCleaner = "cln") + RestTemplate restTemplate() { + return new RestTemplate(); + } + } + + @Configuration + public static class TestConfig12 { + @Bean + SentinelBeanPostProcessor sentinelBeanPostProcessor( + ApplicationContext applicationContext) { + return new SentinelBeanPostProcessor(applicationContext); + } + + @Bean + @SentinelRestTemplate(urlCleanerClass = UrlCleanUtil.class) + RestTemplate restTemplate() { + return new RestTemplate(); + } + } + + @Configuration + public static class TestConfig13 { + @Bean + SentinelBeanPostProcessor sentinelBeanPostProcessor( + ApplicationContext applicationContext) { + return new SentinelBeanPostProcessor(applicationContext); + } + + @Bean + @SentinelRestTemplate(urlCleanerClass = SentinelRestTemplateTests.UrlCleanUtil.class, urlCleaner = "clean1") + RestTemplate restTemplate() { + return new RestTemplate(); + } + } + + @Configuration + public static class TestConfig14 { + @Bean + SentinelBeanPostProcessor sentinelBeanPostProcessor( + ApplicationContext applicationContext) { + return new SentinelBeanPostProcessor(applicationContext); + } + + @Bean + @SentinelRestTemplate(urlCleanerClass = SentinelRestTemplateTests.UrlCleanUtil.class, urlCleaner = "clean2") + RestTemplate restTemplate() { + return new RestTemplate(); + } + } + public static class ExceptionUtil { public static SentinelClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) { @@ -271,4 +351,13 @@ public class SentinelRestTemplateTests { } } + public static class UrlCleanUtil { + public static String clean(String url) { + return url; + } + + public static void clean2(String url) { + } + } + } From d9982c61434b2b5adc0467675c67572d64932fe6 Mon Sep 17 00:00:00 2001 From: Rivers-Shall Date: Tue, 20 Aug 2019 20:53:11 +0800 Subject: [PATCH 6/7] Adjust error message in the thrown exception --- .../cloud/sentinel/custom/SentinelBeanPostProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelBeanPostProcessor.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelBeanPostProcessor.java index d9c150c0..ce94da20 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelBeanPostProcessor.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelBeanPostProcessor.java @@ -149,8 +149,8 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces beanName, standardReturnType.getName(), blockClass.getName(), blockMethod, argsStr); throw new IllegalArgumentException(type + " method return value in bean[" - + beanName + "] is not ClientHttpResponse: " + blockClass.getName() - + "#" + blockMethod + argsStr); + + beanName + "] is not " + standardReturnType.getName() + ": " + + blockClass.getName() + "#" + blockMethod + argsStr); } if (type.equals(SentinelConstants.BLOCK_TYPE)) { BlockClassRegistry.updateBlockHandlerFor(blockClass, blockMethod, From fe55285e9c9fef5e80c2d6e827e92ef5545e7546 Mon Sep 17 00:00:00 2001 From: Rivers-Shall Date: Tue, 20 Aug 2019 20:53:57 +0800 Subject: [PATCH 7/7] Replace native method invoke with `methodInvoke` --- .../custom/SentinelProtectInterceptor.java | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelProtectInterceptor.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelProtectInterceptor.java index fd91016e..7b370264 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelProtectInterceptor.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelProtectInterceptor.java @@ -69,19 +69,9 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor sentinelRestTemplate.urlCleanerClass(), sentinelRestTemplate.urlCleaner()); if (urlCleanerMethod != null) { - try { - hostWithPathResource = (String) urlCleanerMethod.invoke(null, - hostWithPathResource); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - catch (InvocationTargetException e) { - throw new RuntimeException(e); - } + hostWithPathResource = (String) methodInvoke(urlCleanerMethod, + hostWithPathResource); } - System.out.println("hostWithPathResource: " + hostWithPathResource); - System.out.println("entryWithPath: " + entryWithPath); Entry hostEntry = null, hostWithPathEntry = null; ClientHttpResponse response = null; @@ -123,7 +113,7 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor Method fallbackMethod = extractFallbackMethod(sentinelRestTemplate.fallback(), sentinelRestTemplate.fallbackClass()); if (fallbackMethod != null) { - return methodInvoke(fallbackMethod, args); + return (ClientHttpResponse) methodInvoke(fallbackMethod, args); } else { return new SentinelClientHttpResponse(); @@ -134,16 +124,16 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor sentinelRestTemplate.blockHandler(), sentinelRestTemplate.blockHandlerClass()); if (blockHandler != null) { - return methodInvoke(blockHandler, args); + return (ClientHttpResponse) methodInvoke(blockHandler, args); } else { return new SentinelClientHttpResponse(); } } - private ClientHttpResponse methodInvoke(Method method, Object... args) { + private Object methodInvoke(Method method, Object... args) { try { - return (ClientHttpResponse) method.invoke(null, args); + return method.invoke(null, args); } catch (IllegalAccessException e) { throw new RuntimeException(e);