support resttemplate default fallback

pull/917/head
seanyu 3 years ago
parent 9ef157f7c1
commit f299c29315

@ -76,12 +76,4 @@ public class PolarisCircuitBreakerAutoConfiguration {
return new CircuitBreakerConfigModifier(properties); return new CircuitBreakerConfigModifier(properties);
} }
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
public static PolarisCircuitBreakerRestTemplateBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext, CircuitBreakerFactory circuitBreakerFactory) {
return new PolarisCircuitBreakerRestTemplateBeanPostProcessor(applicationContext, circuitBreakerFactory);
}
} }

@ -28,7 +28,13 @@ import org.springframework.context.annotation.Import;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnProperty("spring.cloud.polaris.enabled") @ConditionalOnProperty("spring.cloud.polaris.enabled")
@Import({PolarisCircuitBreakerAutoConfiguration.class, ReactivePolarisCircuitBreakerAutoConfiguration.class, PolarisCircuitBreakerFeignClientAutoConfiguration.class}) @Import({
PolarisCircuitBreakerAutoConfiguration.class,
ReactivePolarisCircuitBreakerAutoConfiguration.class,
PolarisCircuitBreakerFeignClientAutoConfiguration.class,
GatewayPolarisCircuitBreakerAutoConfiguration.class,
PolarisCircuitBreakerRestTemplateAutoConfiguration.class
})
public class PolarisCircuitBreakerBootstrapConfiguration { public class PolarisCircuitBreakerBootstrapConfiguration {
} }

@ -0,0 +1,24 @@
package com.tencent.cloud.polaris.circuitbreaker.config;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerRestTemplateBeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisCircuitBreakerEnabled
@AutoConfigureAfter(PolarisCircuitBreakerAutoConfiguration.class)
public class PolarisCircuitBreakerRestTemplateAutoConfiguration {
@Bean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
public static PolarisCircuitBreakerRestTemplateBeanPostProcessor polarisCircuitBreakerRestTemplateBeanPostProcessor(
ApplicationContext applicationContext) {
return new PolarisCircuitBreakerRestTemplateBeanPostProcessor(applicationContext);
}
}

@ -60,7 +60,7 @@ public class ReactivePolarisCircuitBreakerAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(ReactiveCircuitBreakerFactory.class) @ConditionalOnMissingBean(ReactiveCircuitBreakerFactory.class)
public ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI) { public ReactiveCircuitBreakerFactory polarisReactiveCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI) {
ReactivePolarisCircuitBreakerFactory factory = new ReactivePolarisCircuitBreakerFactory(circuitBreakAPI); ReactivePolarisCircuitBreakerFactory factory = new ReactivePolarisCircuitBreakerFactory(circuitBreakAPI);
customizers.forEach(customizer -> customizer.customize(factory)); customizers.forEach(customizer -> customizer.customize(factory));
return factory; return factory;

@ -11,6 +11,8 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.client.AbstractClientHttpResponse; import org.springframework.http.client.AbstractClientHttpResponse;
import static com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter.POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER;
public class PolarisCircuitBreakerHttpResponse extends AbstractClientHttpResponse { public class PolarisCircuitBreakerHttpResponse extends AbstractClientHttpResponse {
private final CircuitBreakerStatus.FallbackInfo fallbackInfo; private final CircuitBreakerStatus.FallbackInfo fallbackInfo;
@ -57,12 +59,12 @@ public class PolarisCircuitBreakerHttpResponse extends AbstractClientHttpRespons
@Override @Override
public HttpHeaders getHeaders() { public HttpHeaders getHeaders() {
if (fallbackInfo.getHeaders() != null) {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
if (fallbackInfo.getHeaders() != null) {
fallbackInfo.getHeaders().forEach(headers::add); fallbackInfo.getHeaders().forEach(headers::add);
return headers;
} }
return null; headers.add(POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER, "true");
return headers;
} }
} }

@ -13,6 +13,6 @@ public @interface PolarisCircuitBreakerRestTemplate {
String fallback() default ""; String fallback() default "";
Class<PolarisCircuitBreakerFallback> fallbackClass() default PolarisCircuitBreakerFallback.class; Class<? extends PolarisCircuitBreakerFallback> fallbackClass() default PolarisCircuitBreakerFallback.class;
} }

@ -2,6 +2,9 @@ package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@ -17,20 +20,22 @@ import org.springframework.web.client.RestTemplate;
public class PolarisCircuitBreakerRestTemplateBeanPostProcessor implements MergedBeanDefinitionPostProcessor { public class PolarisCircuitBreakerRestTemplateBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
private static final Logger log = LoggerFactory.getLogger(PolarisCircuitBreakerRestTemplateBeanPostProcessor.class);
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
private final CircuitBreakerFactory circuitBreakerFactory; public PolarisCircuitBreakerRestTemplateBeanPostProcessor(ApplicationContext applicationContext) {
public PolarisCircuitBreakerRestTemplateBeanPostProcessor(ApplicationContext applicationContext, CircuitBreakerFactory circuitBreakerFactory) {
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
this.circuitBreakerFactory = circuitBreakerFactory;
} }
private ConcurrentHashMap<String, PolarisCircuitBreakerRestTemplate> cache = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, PolarisCircuitBreakerRestTemplate> cache = new ConcurrentHashMap<>();
private void checkPolarisCircuitBreakerRestTemplate(PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate,
String beanName) {
private void checkPolarisCircuitBreakerRestTemplate(PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate) {
if (
StringUtils.hasText(polarisCircuitBreakerRestTemplate.fallback()) &&
!PolarisCircuitBreakerFallback.class.toGenericString().equals(polarisCircuitBreakerRestTemplate.fallbackClass().toGenericString())
) {
throw new IllegalArgumentException("PolarisCircuitBreakerRestTemplate's fallback and fallbackClass could not set at sametime !");
}
} }
@Override @Override
@ -45,7 +50,7 @@ public class PolarisCircuitBreakerRestTemplateBeanPostProcessor implements Merge
polarisCircuitBreakerRestTemplate = beanDefinition.getResolvedFactoryMethod() polarisCircuitBreakerRestTemplate = beanDefinition.getResolvedFactoryMethod()
.getAnnotation(PolarisCircuitBreakerRestTemplate.class); .getAnnotation(PolarisCircuitBreakerRestTemplate.class);
} }
checkPolarisCircuitBreakerRestTemplate(polarisCircuitBreakerRestTemplate, beanName); checkPolarisCircuitBreakerRestTemplate(polarisCircuitBreakerRestTemplate);
cache.put(beanName, polarisCircuitBreakerRestTemplate); cache.put(beanName, polarisCircuitBreakerRestTemplate);
} }
} }
@ -63,6 +68,7 @@ public class PolarisCircuitBreakerRestTemplateBeanPostProcessor implements Merge
.append(polarisCircuitBreakerRestTemplate.fallbackClass().getSimpleName()); .append(polarisCircuitBreakerRestTemplate.fallbackClass().getSimpleName());
RestTemplate restTemplate = (RestTemplate) bean; RestTemplate restTemplate = (RestTemplate) bean;
String interceptorBeanName = interceptorBeanNamePrefix + "@" + bean; String interceptorBeanName = interceptorBeanNamePrefix + "@" + bean;
CircuitBreakerFactory circuitBreakerFactory = this.applicationContext.getBean(CircuitBreakerFactory.class);
registerBean(interceptorBeanName, polarisCircuitBreakerRestTemplate, applicationContext, circuitBreakerFactory); registerBean(interceptorBeanName, polarisCircuitBreakerRestTemplate, applicationContext, circuitBreakerFactory);
PolarisCircuitBreakerRestTemplateInterceptor polarisCircuitBreakerRestTemplateInterceptor = applicationContext PolarisCircuitBreakerRestTemplateInterceptor polarisCircuitBreakerRestTemplateInterceptor = applicationContext
.getBean(interceptorBeanName, PolarisCircuitBreakerRestTemplateInterceptor.class); .getBean(interceptorBeanName, PolarisCircuitBreakerRestTemplateInterceptor.class);

@ -5,7 +5,6 @@ import java.lang.reflect.Method;
import com.tencent.polaris.api.pojo.CircuitBreakerStatus; import com.tencent.polaris.api.pojo.CircuitBreakerStatus;
import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -14,6 +13,7 @@ import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpRequestInterceptor { public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpRequestInterceptor {
@ -45,11 +45,11 @@ public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpR
} }
}, },
t -> { t -> {
if (!StringUtils.isEmpty(polarisCircuitBreakerRestTemplate.fallback())) { if (StringUtils.hasText(polarisCircuitBreakerRestTemplate.fallback())) {
CircuitBreakerStatus.FallbackInfo fallbackInfo = new CircuitBreakerStatus.FallbackInfo(200, null, polarisCircuitBreakerRestTemplate.fallback()); CircuitBreakerStatus.FallbackInfo fallbackInfo = new CircuitBreakerStatus.FallbackInfo(200, null, polarisCircuitBreakerRestTemplate.fallback());
return new PolarisCircuitBreakerHttpResponse(fallbackInfo); return new PolarisCircuitBreakerHttpResponse(fallbackInfo);
} }
if (polarisCircuitBreakerRestTemplate.fallbackClass() != null && polarisCircuitBreakerRestTemplate.fallbackClass().getSuperclass() != null) { if (!PolarisCircuitBreakerFallback.class.toGenericString().equals(polarisCircuitBreakerRestTemplate.fallbackClass().toGenericString())) {
Method method = ReflectionUtils.findMethod(PolarisCircuitBreakerFallback.class, "fallback"); Method method = ReflectionUtils.findMethod(PolarisCircuitBreakerFallback.class, "fallback");
PolarisCircuitBreakerFallback polarisCircuitBreakerFallback = applicationContext.getBean(polarisCircuitBreakerRestTemplate.fallbackClass()); PolarisCircuitBreakerFallback polarisCircuitBreakerFallback = applicationContext.getBean(polarisCircuitBreakerRestTemplate.fallbackClass());
return (PolarisCircuitBreakerHttpResponse) ReflectionUtils.invokeMethod(method, polarisCircuitBreakerFallback); return (PolarisCircuitBreakerHttpResponse) ReflectionUtils.invokeMethod(method, polarisCircuitBreakerFallback);

@ -40,4 +40,16 @@ public class ServiceBController {
public String info() { public String info() {
return "hello world ! I'm a service B1"; return "hello world ! I'm a service B1";
} }
@GetMapping("/health")
public String health() {
System.out.println("health check: 200 instance");
return "hello world ! I'm a service B1";
}
@GetMapping("/health-svc")
public String healthsvc() {
System.out.println("health-svc check: 200 instance");
return "hello world ! I'm a service B1";
}
} }

@ -18,6 +18,8 @@
package com.tencent.cloud.polaris.ciruitbreaker.example; package com.tencent.cloud.polaris.ciruitbreaker.example;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -43,4 +45,18 @@ public class ServiceBController {
public String info() { public String info() {
return "failed for call service B2"; return "failed for call service B2";
} }
@GetMapping("/health")
@ResponseStatus(value = HttpStatus.BAD_GATEWAY, reason = "failed for call my service")
public String health() {
System.out.println("health check: 502 instance");
return "hello world ! I'm a service B1";
}
@GetMapping("/health-svc")
@ResponseStatus(value = HttpStatus.BAD_GATEWAY, reason = "failed for call my service")
public String healthsvc() {
System.out.println("health-svc check: 502 instance");
return "hello world ! I'm a service B1";
}
} }

@ -0,0 +1,17 @@
package com.tencent.cloud.polaris.circuitbreaker.resttemplate.example;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerFallback;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerHttpResponse;
import org.springframework.stereotype.Component;
@Component
public class CustomPolarisCircuitBreakerFallback implements PolarisCircuitBreakerFallback {
@Override
public PolarisCircuitBreakerHttpResponse fallback() {
return new PolarisCircuitBreakerHttpResponse(
200,
"{\"msg\": \"this is a fallback class\"}");
}
}

@ -19,7 +19,9 @@ package com.tencent.cloud.polaris.circuitbreaker.resttemplate.example;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@ -35,24 +37,38 @@ import org.springframework.web.client.RestTemplate;
public class ServiceAController { public class ServiceAController {
@Autowired @Autowired
private RestTemplate restTemplate; @Qualifier("defaultRestTemplate")
private RestTemplate defaultRestTemplate;
@Autowired @Autowired
private CircuitBreakerFactory circuitBreakerFactory; @Qualifier("fallbackRestTemplate")
private RestTemplate fallbackRestTemplate;
@Autowired
@Qualifier("fallbackClassRestTemplate")
private RestTemplate fallbackClassRestTemplate;
// @GetMapping("/getBServiceInfo") @Autowired
// public String getBServiceInfo() { private CircuitBreakerFactory circuitBreakerFactory;
// return circuitBreakerFactory
// .create("polaris-circuitbreaker-callee-service#/example/service/b/info")
// .run(() ->
// restTemplate.getForObject("/example/service/b/info", String.class),
// throwable -> "trigger the refuse for service b"
// );
// }
@GetMapping("/getBServiceInfo") @GetMapping("/getBServiceInfo")
public String getBServiceInfo() { public String getBServiceInfo() {
return restTemplate.getForObject("/example/service/b/info", String.class); return circuitBreakerFactory
.create("polaris-circuitbreaker-callee-service#/example/service/b/info")
.run(() ->
defaultRestTemplate.getForObject("/example/service/b/info", String.class),
throwable -> "trigger the refuse for service b"
);
}
@GetMapping("/getBServiceInfo/fallback")
public String getBServiceInfoFallback() {
return fallbackRestTemplate.getForObject("/example/service/b/info", String.class);
}
@GetMapping("/getBServiceInfo/fallbackClass")
public String getBServiceInfoFallbackClass() {
return fallbackClassRestTemplate.getForObject("/example/service/b/info", String.class);
} }
} }

@ -20,10 +20,14 @@ package com.tencent.cloud.polaris.circuitbreaker.resttemplate.example;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerFallback; import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerFallback;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerRestTemplate; import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerRestTemplate;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerRestTemplateBeanPostProcessor;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.DefaultUriBuilderFactory;
@ -40,10 +44,29 @@ public class ServiceAResTemplate {
SpringApplication.run(ServiceAResTemplate.class, args); SpringApplication.run(ServiceAResTemplate.class, args);
} }
@Bean
@LoadBalanced
public RestTemplate defaultRestTemplate() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://polaris-circuitbreaker-callee-service");
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(uriBuilderFactory);
return restTemplate;
}
@Bean @Bean
@LoadBalanced @LoadBalanced
@PolarisCircuitBreakerRestTemplate @PolarisCircuitBreakerRestTemplate
public RestTemplate restTemplate() { public RestTemplate fallbackRestTemplate() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://polaris-circuitbreaker-callee-service");
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(uriBuilderFactory);
return restTemplate;
}
@Bean
@LoadBalanced
@PolarisCircuitBreakerRestTemplate(fallbackClass = CustomPolarisCircuitBreakerFallback.class)
public RestTemplate fallbackClassRestTemplate() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://polaris-circuitbreaker-callee-service"); DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://polaris-circuitbreaker-callee-service");
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(uriBuilderFactory); restTemplate.setUriTemplateHandler(uriBuilderFactory);

@ -58,6 +58,7 @@ import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
*/ */
public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter implements ResponseErrorHandler, ApplicationContextAware { public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter implements ResponseErrorHandler, ApplicationContextAware {
public static final String POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER = "PolarisCircuitBreakerFallback";
static final String HEADER_HAS_ERROR = "X-SCT-Has-Error"; static final String HEADER_HAS_ERROR = "X-SCT-Has-Error";
private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedRestTemplateReporter.class); private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedRestTemplateReporter.class);
private final ConsumerAPI consumerAPI; private final ConsumerAPI consumerAPI;
@ -119,6 +120,9 @@ public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter
} }
private void reportResult(URI url, ClientHttpResponse response) { private void reportResult(URI url, ClientHttpResponse response) {
if (Boolean.parseBoolean(response.getHeaders().getFirst(POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER))){
return;
}
ServiceCallResult resultRequest = createServiceCallResult(url); ServiceCallResult resultRequest = createServiceCallResult(url);
try { try {
Map<String, String> loadBalancerContext = MetadataContextHolder.get().getLoadbalancerMetadata(); Map<String, String> loadBalancerContext = MetadataContextHolder.get().getLoadbalancerMetadata();

Loading…
Cancel
Save