diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/error.sh b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/error.sh new file mode 100644 index 00000000..5602a98d --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/error.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +n=1 +while [ $n -le 10 ] +do + echo `curl -s http://localhost:18083/test` + let n++ +done diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/feign-error.sh b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/feign-error.sh new file mode 100644 index 00000000..09db2477 --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/feign-error.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +n=1 +while [ $n -le 10 ] +do + echo `curl -s http://localhost:18083/divide-feign?a=1\&b=0` + let n++ +done diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/index.sh b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/index.sh new file mode 100644 index 00000000..7db176b8 --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/index.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +n=1 +while [ $n -le 10 ] +do + echo `curl -s http://localhost:18083/index` + let n++ +done diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/sleep.sh b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/sleep.sh new file mode 100644 index 00000000..423c3d15 --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/sleep.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +n=1 +while [ $n -le 10 ] +do + echo `curl -s http://localhost:18083/sleep` + let n++ +done diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ConsumerApplication.java b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ConsumerApplication.java index 5dfd4eba..97712312 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ConsumerApplication.java +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ConsumerApplication.java @@ -3,6 +3,7 @@ package org.springframework.cloud.alibaba.cloud.examples; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.alibaba.cloud.examples.ConsumerApplication.EchoService; +import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.EnableFeignClients; @@ -28,6 +29,13 @@ public class ConsumerApplication { return new RestTemplate(); } + @LoadBalanced + @Bean + @SentinelRestTemplate + public RestTemplate restTemplate1() { + return new RestTemplate(); + } + public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TestController.java b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TestController.java index 55c751c7..1b1970b8 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TestController.java +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TestController.java @@ -18,18 +18,52 @@ public class TestController { @Autowired private RestTemplate restTemplate; + + @Autowired + private RestTemplate restTemplate1; + @Autowired private EchoService echoService; @Autowired private DiscoveryClient discoveryClient; + // @PostConstruct + // public void init() { + // restTemplate1.setErrorHandler(new ResponseErrorHandler() { + // @Override + // public boolean hasError(ClientHttpResponse response) throws IOException { + // return false; + // } + // + // @Override + // public void handleError(ClientHttpResponse response) throws IOException { + // System.err.println("handle error"); + // } + // }); + // } + @RequestMapping(value = "/echo-rest/{str}", method = RequestMethod.GET) public String rest(@PathVariable String str) { return restTemplate.getForObject("http://service-provider/echo/" + str, String.class); } + @RequestMapping(value = "/index", method = RequestMethod.GET) + public String index() { + return restTemplate1.getForObject("http://service-provider", String.class); + } + + @RequestMapping(value = "/test", method = RequestMethod.GET) + public String test() { + return restTemplate1.getForObject("http://service-provider/test", String.class); + } + + @RequestMapping(value = "/sleep", method = RequestMethod.GET) + public String sleep() { + return restTemplate1.getForObject("http://service-provider/sleep", String.class); + } + @RequestMapping(value = "/notFound-feign", method = RequestMethod.GET) public String notFound() { return echoService.notFound(); diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties index 02b7ce32..2d260cb2 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties @@ -10,4 +10,8 @@ spring.cloud.sentinel.eager=true spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json spring.cloud.sentinel.datasource.ds1.file.data-type=json -spring.cloud.sentinel.datasource.ds1.file.rule-type=flow \ No newline at end of file +spring.cloud.sentinel.datasource.ds1.file.rule-type=flow + +spring.cloud.sentinel.datasource.ds2.file.file=classpath: degraderule.json +spring.cloud.sentinel.datasource.ds2.file.data-type=json +spring.cloud.sentinel.datasource.ds2.file.rule-type=degrade \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/degraderule.json b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/degraderule.json new file mode 100644 index 00000000..75e9d3b3 --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/degraderule.json @@ -0,0 +1,26 @@ +[ + { + "resource": "GET:http://service-provider/test", + "count": 0.5, + "grade": 1, + "timeWindow": 30 + }, + { + "resource": "GET:http://service-provider", + "count": 0.5, + "grade": 1, + "timeWindow": 10 + }, + { + "resource": "GET:http://service-provider/sleep", + "count": 20.0, + "grade": 0, + "timeWindow": 30 + }, + { + "resource": "GET:http://service-provider/divide", + "count": 0.5, + "grade": 1, + "timeWindow": 30 + } +] \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ProviderApplication.java b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ProviderApplication.java index ae090f6e..e9d6a743 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ProviderApplication.java +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ProviderApplication.java @@ -3,6 +3,8 @@ package org.springframework.cloud.alibaba.cloud.examples; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -22,6 +24,28 @@ public class ProviderApplication { @RestController class EchoController { + + @RequestMapping(value = "/", method = RequestMethod.GET) + public ResponseEntity index() { + return new ResponseEntity("index error", HttpStatus.INTERNAL_SERVER_ERROR); + } + + @RequestMapping(value = "/test", method = RequestMethod.GET) + public ResponseEntity test() { + return new ResponseEntity("error", HttpStatus.INTERNAL_SERVER_ERROR); + } + + @RequestMapping(value = "/sleep", method = RequestMethod.GET) + public String sleep() { + try { + Thread.sleep(1000L); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + return "ok"; + } + @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET) public String echo(@PathVariable String string) { return "hello Nacos Discovery " + string; 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 7ec733b5..26641002 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 @@ -168,9 +168,9 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces throws BeansException { if (cache.containsKey(beanName)) { // add interceptor for each RestTemplate with @SentinelRestTemplate annotation - StringBuilder interceptorBeanName = new StringBuilder(); + StringBuilder interceptorBeanNamePrefix = new StringBuilder(); SentinelRestTemplate sentinelRestTemplate = cache.get(beanName); - interceptorBeanName + interceptorBeanNamePrefix .append(StringUtils.uncapitalize( SentinelProtectInterceptor.class.getSimpleName())) .append("_") @@ -179,26 +179,25 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces .append(sentinelRestTemplate.fallbackClass().getSimpleName()) .append(sentinelRestTemplate.fallback()); RestTemplate restTemplate = (RestTemplate) bean; - registerBean(interceptorBeanName.toString(), sentinelRestTemplate); + String interceptorBeanName = interceptorBeanNamePrefix + "@" + + bean.toString(); + registerBean(interceptorBeanName, sentinelRestTemplate, (RestTemplate) bean); SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext - .getBean(interceptorBeanName.toString(), - SentinelProtectInterceptor.class); + .getBean(interceptorBeanName, SentinelProtectInterceptor.class); restTemplate.getInterceptors().add(0, sentinelProtectInterceptor); } return bean; } private void registerBean(String interceptorBeanName, - SentinelRestTemplate sentinelRestTemplate) { + SentinelRestTemplate sentinelRestTemplate, RestTemplate restTemplate) { // register SentinelProtectInterceptor bean DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext .getAutowireCapableBeanFactory(); - if (beanFactory.containsBean(interceptorBeanName)) { - return; - } BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder .genericBeanDefinition(SentinelProtectInterceptor.class); beanDefinitionBuilder.addConstructorArgValue(sentinelRestTemplate); + beanDefinitionBuilder.addConstructorArgValue(restTemplate); BeanDefinition interceptorBeanDefinition = beanDefinitionBuilder .getRawBeanDefinition(); beanFactory.registerBeanDefinition(interceptorBeanName, 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 48f63ef3..dc69efbb 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 @@ -27,11 +27,12 @@ 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.web.client.RestTemplate; import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.SphU; 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; @@ -44,8 +45,12 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor private final SentinelRestTemplate sentinelRestTemplate; - public SentinelProtectInterceptor(SentinelRestTemplate sentinelRestTemplate) { + private final RestTemplate restTemplate; + + public SentinelProtectInterceptor(SentinelRestTemplate sentinelRestTemplate, + RestTemplate restTemplate) { this.sentinelRestTemplate = sentinelRestTemplate; + this.restTemplate = restTemplate; } @Override @@ -61,32 +66,33 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor entryWithPath = false; } Entry hostEntry = null, hostWithPathEntry = null; - ClientHttpResponse response; + ClientHttpResponse response = null; try { - ContextUtil.enter(hostWithPathResource); + hostEntry = SphU.entry(hostResource, EntryType.OUT); if (entryWithPath) { - hostWithPathEntry = SphU.entry(hostWithPathResource); + hostWithPathEntry = SphU.entry(hostWithPathResource, EntryType.OUT); } - hostEntry = SphU.entry(hostResource); response = execution.execute(request, body); + if (this.restTemplate.getErrorHandler().hasError(response)) { + Tracer.trace( + new IllegalStateException("RestTemplate ErrorHandler has error")); + } } catch (Throwable e) { if (!BlockException.isBlockException(e)) { Tracer.trace(e); - throw new IllegalStateException(e); } else { return handleBlockException(request, body, execution, (BlockException) e); } } finally { - if (hostEntry != null) { - hostEntry.exit(); - } if (hostWithPathEntry != null) { hostWithPathEntry.exit(); } - ContextUtil.exit(); + if (hostEntry != null) { + hostEntry.exit(); + } } return response; } diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/feign/SentinelInvocationHandler.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/feign/SentinelInvocationHandler.java index 809879cc..f1f34fd5 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/feign/SentinelInvocationHandler.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/feign/SentinelInvocationHandler.java @@ -95,7 +95,7 @@ public class SentinelInvocationHandler implements InvocationHandler { + Feign.configKey(method.getDeclaringClass(), method)); // resource default is HttpMethod:protocol://url String resourceName = methodMetadata.template().method().toUpperCase() + ":" - + hardCodedTarget.url() + methodMetadata.template().url(); + + hardCodedTarget.url() + methodMetadata.template().path(); Entry entry = null; try { ContextUtil.enter(resourceName); @@ -128,7 +128,7 @@ public class SentinelInvocationHandler implements InvocationHandler { } finally { if (entry != null) { - entry.exit(); + entry.exit(1, args); } ContextUtil.exit(); } 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 434d0237..555097a6 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 @@ -106,14 +106,16 @@ public class SentinelAutoConfigurationTests { @LocalServerPort private int port; - private String url = "http://localhost:" + port; + private String flowUrl = "http://localhost:" + port + "/flow"; + + private String degradeUrl = "http://localhost:" + port + "/degrade"; @Before public void setUp() { FlowRule rule = new FlowRule(); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(0); - rule.setResource("GET:" + url); + rule.setResource("GET:" + flowUrl); rule.setLimitApp("default"); rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); rule.setStrategy(RuleConstant.STRATEGY_DIRECT); @@ -121,7 +123,7 @@ public class SentinelAutoConfigurationTests { DegradeRule degradeRule = new DegradeRule(); degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT); - degradeRule.setResource("GET:" + url + "/test"); + degradeRule.setResource("GET:" + degradeUrl); degradeRule.setCount(0); degradeRule.setTimeWindow(60); DegradeRuleManager.loadRules(Arrays.asList(degradeRule)); @@ -247,14 +249,15 @@ public class SentinelAutoConfigurationTests { restTemplate.getInterceptors().size()); assertEquals("RestTemplateWithBlockClass interceptors size was wrong", 1, restTemplateWithBlockClass.getInterceptors().size()); - ResponseEntity responseEntityBlock = restTemplateWithBlockClass.getForEntity(url, - String.class); + ResponseEntity responseEntityBlock = restTemplateWithBlockClass + .getForEntity(flowUrl, String.class); assertEquals("RestTemplateWithBlockClass Sentinel Block Message was wrong", "Oops", responseEntityBlock.getBody()); assertEquals( "RestTemplateWithBlockClass Sentinel Block Http Status Code was wrong", HttpStatus.OK, responseEntityBlock.getStatusCode()); - ResponseEntity responseEntityRaw = restTemplate.getForEntity(url, String.class); + ResponseEntity responseEntityRaw = restTemplate.getForEntity(flowUrl, + String.class); assertEquals("RestTemplate Sentinel Block Message was wrong", "RestTemplate request block by sentinel", responseEntityRaw.getBody()); assertEquals("RestTemplate Sentinel Block Http Status Code was wrong", @@ -266,14 +269,14 @@ public class SentinelAutoConfigurationTests { assertEquals("RestTemplateWithoutBlockClass interceptors size was wrong", 0, restTemplateWithoutBlockClass.getInterceptors().size()); assertThatExceptionOfType(RestClientException.class).isThrownBy(() -> { - restTemplateWithoutBlockClass.getForEntity(url, String.class); + restTemplateWithoutBlockClass.getForEntity(flowUrl, String.class); }); } @Test public void testFallbackRestTemplate() { ResponseEntity responseEntity = restTemplateWithFallbackClass - .getForEntity(url + "/test", String.class); + .getForEntity(degradeUrl, String.class); assertEquals("RestTemplateWithFallbackClass Sentinel Message was wrong", "Oops fallback", responseEntity.getBody()); assertEquals("RestTemplateWithFallbackClass Sentinel Http Status Code was wrong",