1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00

Throw the origin RuntimeException when invoke a restTemplate fallback method, it's helpful for a global handler with @ExceptionHandler.

Refined codes.
This commit is contained in:
SoulSong 2020-03-27 00:09:42 +08:00
parent 3f534c8c33
commit 0a9f3eb6f9
2 changed files with 359 additions and 273 deletions

View File

@ -139,6 +139,9 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
throw new RuntimeException(e); throw new RuntimeException(e);
} }
catch (InvocationTargetException e) { catch (InvocationTargetException e) {
if (e.getTargetException() instanceof RuntimeException) {
throw (RuntimeException) e.getTargetException();
}
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }

View File

@ -20,6 +20,8 @@ import com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate;
import com.alibaba.cloud.sentinel.custom.SentinelBeanPostProcessor; import com.alibaba.cloud.sentinel.custom.SentinelBeanPostProcessor;
import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse; import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse;
import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.junit.Test; import org.junit.Test;
@ -29,7 +31,11 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpRequest; import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -39,326 +45,403 @@ import static org.junit.Assert.assertEquals;
*/ */
public class SentinelRestTemplateTests { public class SentinelRestTemplateTests {
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testFbkMethod() { public void testFbkMethod() {
new AnnotationConfigApplicationContext(TestConfig1.class); new AnnotationConfigApplicationContext(TestConfig1.class);
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testFbkClass() { public void testFbkClass() {
new AnnotationConfigApplicationContext(TestConfig2.class); new AnnotationConfigApplicationContext(TestConfig2.class);
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testblkMethod() { public void testblkMethod() {
new AnnotationConfigApplicationContext(TestConfig3.class); new AnnotationConfigApplicationContext(TestConfig3.class);
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testblkClass() { public void testblkClass() {
new AnnotationConfigApplicationContext(TestConfig4.class); new AnnotationConfigApplicationContext(TestConfig4.class);
} }
@Test @Test
public void testNormal() { public void testNormal() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TestConfig5.class); TestConfig5.class);
assertEquals("RestTemplate size was wrong", 1, assertEquals("RestTemplate size was wrong", 1,
context.getBeansOfType(RestTemplate.class).size()); context.getBeansOfType(RestTemplate.class).size());
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testBlkMethodExists() { public void testBlkMethodExists() {
new AnnotationConfigApplicationContext(TestConfig6.class); new AnnotationConfigApplicationContext(TestConfig6.class);
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testFbkMethodExists() { public void testFbkMethodExists() {
new AnnotationConfigApplicationContext(TestConfig7.class); new AnnotationConfigApplicationContext(TestConfig7.class);
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testBlkReturnValue() { public void testBlkReturnValue() {
new AnnotationConfigApplicationContext(TestConfig8.class); new AnnotationConfigApplicationContext(TestConfig8.class);
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testFbkReturnValue() { public void testFbkReturnValue() {
new AnnotationConfigApplicationContext(TestConfig9.class); new AnnotationConfigApplicationContext(TestConfig9.class);
} }
@Test @Test
public void testNormalWithoutParam() { public void testNormalWithoutParam() {
new AnnotationConfigApplicationContext(TestConfig10.class); new AnnotationConfigApplicationContext(TestConfig10.class);
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testUrlClnMethod() { public void testUrlClnMethod() {
new AnnotationConfigApplicationContext(TestConfig11.class); new AnnotationConfigApplicationContext(TestConfig11.class);
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testUrlClnClass() { public void testUrlClnClass() {
new AnnotationConfigApplicationContext(TestConfig12.class); new AnnotationConfigApplicationContext(TestConfig12.class);
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testUrlClnMethodExists() { public void testUrlClnMethodExists() {
new AnnotationConfigApplicationContext(TestConfig13.class); new AnnotationConfigApplicationContext(TestConfig13.class);
} }
@Test(expected = BeanCreationException.class) @Test(expected = BeanCreationException.class)
public void testUrlClnReturnValue() { public void testUrlClnReturnValue() {
new AnnotationConfigApplicationContext(TestConfig14.class); new AnnotationConfigApplicationContext(TestConfig14.class);
} }
@Configuration @Test
public static class TestConfig1 { public void testExceptionHandler4Fallback() {
@Bean new AnnotationConfigApplicationContext(TestConfig15.class);
SentinelBeanPostProcessor sentinelBeanPostProcessor( }
ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext);
}
@Bean
@SentinelRestTemplate(fallback = "fbk")
RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Configuration @Configuration
public static class TestConfig2 { public static class TestConfig1 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(fallbackClass = ExceptionUtil.class) @SentinelRestTemplate(fallback = "fbk")
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig3 { public static class TestConfig2 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(blockHandler = "blk") @SentinelRestTemplate(fallbackClass = ExceptionUtil.class)
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig4 { public static class TestConfig3 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(blockHandlerClass = ExceptionUtil.class) @SentinelRestTemplate(blockHandler = "blk")
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig5 { public static class TestConfig4 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException", fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException", urlCleanerClass = SentinelRestTemplateTests.UrlCleanUtil.class, urlCleaner = "clean") @SentinelRestTemplate(blockHandlerClass = ExceptionUtil.class)
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig6 { public static class TestConfig5 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException1") @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException", fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException", urlCleanerClass = SentinelRestTemplateTests.UrlCleanUtil.class, urlCleaner = "clean")
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig7 { public static class TestConfig6 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException1") @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException1")
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig8 { public static class TestConfig7 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException2") @SentinelRestTemplate(fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException1")
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig9 { public static class TestConfig8 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException2") @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException2")
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig10 { public static class TestConfig9 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate @SentinelRestTemplate(fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException2")
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
}
@Bean @Configuration
@SentinelRestTemplate public static class TestConfig10 {
RestTemplate restTemplate2() { @Bean
return new RestTemplate(); SentinelBeanPostProcessor sentinelBeanPostProcessor(
} ApplicationContext applicationContext) {
} return new SentinelBeanPostProcessor(applicationContext);
}
@Configuration @Bean
public static class TestConfig11 { @SentinelRestTemplate
@Bean RestTemplate restTemplate() {
SentinelBeanPostProcessor sentinelBeanPostProcessor( return new RestTemplate();
ApplicationContext applicationContext) { }
return new SentinelBeanPostProcessor(applicationContext);
}
@Bean @Bean
@SentinelRestTemplate(urlCleaner = "cln") @SentinelRestTemplate
RestTemplate restTemplate() { RestTemplate restTemplate2() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig12 { public static class TestConfig11 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(urlCleanerClass = UrlCleanUtil.class) @SentinelRestTemplate(urlCleaner = "cln")
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig13 { public static class TestConfig12 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(urlCleanerClass = SentinelRestTemplateTests.UrlCleanUtil.class, urlCleaner = "clean1") @SentinelRestTemplate(urlCleanerClass = UrlCleanUtil.class)
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
@Configuration @Configuration
public static class TestConfig14 { public static class TestConfig13 {
@Bean @Bean
SentinelBeanPostProcessor sentinelBeanPostProcessor( SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) { ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext); return new SentinelBeanPostProcessor(applicationContext);
} }
@Bean @Bean
@SentinelRestTemplate(urlCleanerClass = SentinelRestTemplateTests.UrlCleanUtil.class, urlCleaner = "clean2") @SentinelRestTemplate(urlCleanerClass = SentinelRestTemplateTests.UrlCleanUtil.class, urlCleaner = "clean1")
RestTemplate restTemplate() { RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }
public static class ExceptionUtil { @Configuration
public static SentinelClientHttpResponse handleException(HttpRequest request, public static class TestConfig14 {
byte[] body, ClientHttpRequestExecution execution, BlockException ex) { @Bean
System.out.println("Oops: " + ex.getClass().getCanonicalName()); SentinelBeanPostProcessor sentinelBeanPostProcessor(
return new SentinelClientHttpResponse("Oops"); ApplicationContext applicationContext) {
} return new SentinelBeanPostProcessor(applicationContext);
}
public static void handleException2(HttpRequest request, byte[] body, @Bean
ClientHttpRequestExecution execution, BlockException ex) { @SentinelRestTemplate(urlCleanerClass = SentinelRestTemplateTests.UrlCleanUtil.class, urlCleaner = "clean2")
System.out.println("Oops: " + ex.getClass().getCanonicalName()); RestTemplate restTemplate() {
} return new RestTemplate();
}
}
public static SentinelClientHttpResponse fallbackException(HttpRequest request, @Configuration
byte[] body, ClientHttpRequestExecution execution, BlockException ex) { public static class TestConfig15 {
System.out.println("Oops: " + ex.getClass().getCanonicalName()); @Bean
return new SentinelClientHttpResponse("Oops fallback"); SentinelBeanPostProcessor sentinelBeanPostProcessor(
} ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext);
}
public static void fallbackException2(HttpRequest request, byte[] body, @Bean
ClientHttpRequestExecution execution, BlockException ex) { @SentinelRestTemplate(fallbackClass = TestConfig15.class,
System.out.println("Oops: " + ex.getClass().getCanonicalName()); fallback = "fallbackHandler",
} blockHandlerClass = TestConfig15.class,
} blockHandler = "flowHandler")
RestTemplate restTemplate() {
return new RestTemplate();
}
public static class UrlCleanUtil { @Bean
public static String clean(String url) { public SentinelRestTemplateExceptionHandler globalExceptionHandler() {
return url; return new SentinelRestTemplateExceptionHandler();
} }
public static SentinelClientHttpResponse flowHandler(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution,
BlockException exception) {
if (exception instanceof FlowException) {
throw new SentinelRestTemplateFlowException(exception.getMessage(), exception);
}
return new SentinelClientHttpResponse("Oops flowHandler");
}
public static SentinelClientHttpResponse fallbackHandler(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution,
BlockException exception) {
if (exception instanceof DegradeException) {
throw new SentinelRestTemplateDegradeException(exception.getMessage(), exception);
}
return new SentinelClientHttpResponse("Oops fallback");
}
public static class SentinelRestTemplateFlowException extends RuntimeException {
SentinelRestTemplateFlowException(String message, Throwable cause) {
super(message, cause);
}
}
public static class SentinelRestTemplateDegradeException extends RuntimeException {
SentinelRestTemplateDegradeException(String message, Throwable cause) {
super(message, cause);
}
}
@ControllerAdvice
public class SentinelRestTemplateExceptionHandler {
@ExceptionHandler(SentinelRestTemplateFlowException.class)
public ResponseEntity<String> restTemplateFlowExceptionHandler(SentinelRestTemplateFlowException exception) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("PS: have a rest.");
}
@ExceptionHandler(SentinelRestTemplateDegradeException.class)
public ResponseEntity<String> restTemplateDegradeExceptionHandler(SentinelRestTemplateDegradeException exception) {
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("PS: " + HttpStatus.SERVICE_UNAVAILABLE.getReasonPhrase());
}
}
}
public static class ExceptionUtil {
public static SentinelClientHttpResponse handleException(HttpRequest request,
byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
System.out.println("Oops: " + ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse("Oops");
}
public static void handleException2(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution, BlockException ex) {
System.out.println("Oops: " + ex.getClass().getCanonicalName());
}
public static SentinelClientHttpResponse fallbackException(HttpRequest request,
byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
System.out.println("Oops: " + ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse("Oops fallback");
}
public static void fallbackException2(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution, BlockException ex) {
System.out.println("Oops: " + ex.getClass().getCanonicalName());
}
}
public static class UrlCleanUtil {
public static String clean(String url) {
return url;
}
public static void clean2(String url) {
}
}
public static void clean2(String url) {
}
}
} }