refactor scg

pull/916/head
seanyu 3 years ago
parent 644e1912f7
commit d7ce5e3513

@ -91,7 +91,7 @@
<revision>1.11.0-2020.0.6-SNAPSHOT</revision> <revision>1.11.0-2020.0.6-SNAPSHOT</revision>
<!-- Spring Framework --> <!-- Spring Framework -->
<spring.framework.version>5.3.26</spring.framework.version> <spring.framework.version>5.3.25</spring.framework.version>
<!-- Spring Boot --> <!-- Spring Boot -->
<spring.boot.version>2.4.13</spring.boot.version> <spring.boot.version>2.4.13</spring.boot.version>

@ -22,6 +22,7 @@ import java.net.URI;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -41,6 +42,7 @@ import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties;
import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.SpringCloudCircuitBreakerFilterFactory; import org.springframework.cloud.gateway.filter.factory.SpringCloudCircuitBreakerFilterFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.HttpStatusHolder; import org.springframework.cloud.gateway.support.HttpStatusHolder;
import org.springframework.cloud.gateway.support.ServiceUnavailableException; import org.springframework.cloud.gateway.support.ServiceUnavailableException;
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
@ -58,6 +60,7 @@ import static java.util.Optional.ofNullable;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator; import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.reset; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.reset;
@ -180,52 +183,75 @@ public class PolarisCircuitBreakerFilterFactory extends SpringCloudCircuitBreake
return new GatewayFilter() { return new GatewayFilter() {
@Override @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
String serviceName = circuitBreakerId;
if (route != null) {
serviceName = route.getUri().getHost();
}
String path = exchange.getRequest().getPath().value(); String path = exchange.getRequest().getPath().value();
ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(circuitBreakerId + "#" + path); ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(serviceName + "#" + path);
return cb.run(chain.filter(exchange).doOnSuccess(v -> { return cb.run(
if (statuses.contains(exchange.getResponse().getStatusCode())) { chain.filter(exchange)
HttpStatus status = exchange.getResponse().getStatusCode(); .doOnSuccess(v -> {
throw new CircuitBreakerStatusCodeException(status); // throw CircuitBreakerStatusCodeException by default for all need checking status
} // so polaris can report right error status
}), t -> { Set<HttpStatus> statusNeedToCheck = new HashSet<>();
if (config.getFallbackUri() == null) { statusNeedToCheck.addAll(statuses);
if (t instanceof CallAbortedException) { statusNeedToCheck.addAll(getDefaultStatus());
CircuitBreakerStatus.FallbackInfo fallbackInfo = ((CallAbortedException) t).getFallbackInfo(); if (statusNeedToCheck.contains(exchange.getResponse().getStatusCode())) {
if (fallbackInfo != null) { HttpStatus status = exchange.getResponse().getStatusCode();
ServerHttpResponse response = exchange.getResponse(); throw new CircuitBreakerStatusCodeException(status);
response.setRawStatusCode(fallbackInfo.getCode()); }
if (fallbackInfo.getHeaders() != null) { }),
fallbackInfo.getHeaders().forEach((k, v) -> response.getHeaders().add(k, v)); t -> {
// pre-check CircuitBreakerStatusCodeException's status matches input status
if (t instanceof CircuitBreakerStatusCodeException) {
HttpStatus status = ((CircuitBreakerStatusCodeException) t).getStatusCode();
// no need to fallback
if (!statuses.contains(status)) {
return Mono.error(t);
} }
if (fallbackInfo.getBody() != null) { }
byte[] bytes = fallbackInfo.getBody().getBytes(StandardCharsets.UTF_8); // do fallback
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes); if (config.getFallbackUri() == null) {
return exchange.getResponse().writeWith(Flux.just(buffer)); // polaris checking
if (t instanceof CallAbortedException) {
CircuitBreakerStatus.FallbackInfo fallbackInfo = ((CallAbortedException) t).getFallbackInfo();
if (fallbackInfo != null) {
ServerHttpResponse response = exchange.getResponse();
response.setRawStatusCode(fallbackInfo.getCode());
if (fallbackInfo.getHeaders() != null) {
fallbackInfo.getHeaders().forEach((k, v) -> response.getHeaders().add(k, v));
}
DataBuffer bodyBuffer = null;
if (fallbackInfo.getBody() != null) {
byte[] bytes = fallbackInfo.getBody().getBytes(StandardCharsets.UTF_8);
bodyBuffer = response.bufferFactory().wrap(bytes);
}
return bodyBuffer != null ? response.writeWith(Flux.just(bodyBuffer)) : response.setComplete();
}
} }
return chain.filter(exchange); return Mono.error(t);
} }
} exchange.getResponse().setStatusCode(null);
return Mono.error(t); reset(exchange);
}
exchange.getResponse().setStatusCode(null); // TODO: copied from RouteToRequestUrlFilter
reset(exchange); URI uri = exchange.getRequest().getURI();
// TODO: assume always?
boolean encoded = containsEncodedParts(uri);
URI requestUrl = UriComponentsBuilder.fromUri(uri).host(null).port(null)
.uri(config.getFallbackUri()).scheme(null).build(encoded).toUri();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
addExceptionDetails(t, exchange);
// TODO: copied from RouteToRequestUrlFilter // Reset the exchange
URI uri = exchange.getRequest().getURI(); reset(exchange);
// TODO: assume always?
boolean encoded = containsEncodedParts(uri);
URI requestUrl = UriComponentsBuilder.fromUri(uri).host(null).port(null)
.uri(config.getFallbackUri()).scheme(null).build(encoded).toUri();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
addExceptionDetails(t, exchange);
// Reset the exchange ServerHttpRequest request = exchange.getRequest().mutate().uri(requestUrl).build();
reset(exchange); return getDispatcherHandler().handle(exchange.mutate().request(request).build());
})
ServerHttpRequest request = exchange.getRequest().mutate().uri(requestUrl).build(); .onErrorResume(t -> handleErrorWithoutFallback(t, config.isResumeWithoutError()));
return getDispatcherHandler().handle(exchange.mutate().request(request).build());
}).onErrorResume(t -> handleErrorWithoutFallback(t, config.isResumeWithoutError()));
} }
@Override @Override
@ -245,6 +271,9 @@ public class PolarisCircuitBreakerFilterFactory extends SpringCloudCircuitBreake
if (t instanceof CallAbortedException) { if (t instanceof CallAbortedException) {
return Mono.error(new ServiceUnavailableException()); return Mono.error(new ServiceUnavailableException());
} }
if (t instanceof CircuitBreakerStatusCodeException) {
return Mono.empty();
}
if (resumeWithoutError) { if (resumeWithoutError) {
return Mono.empty(); return Mono.empty();
} }

@ -24,7 +24,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* PolarisCircuitBreakerRestTemplate annotation. * PolarisCircuitBreaker annotation.
* if coded fallback or fallbackClass provided, RestTemplate will always return fallback when any exception occurs, * if coded fallback or fallbackClass provided, RestTemplate will always return fallback when any exception occurs,
* if none coded fallback or fallbackClass provided, RestTemplate will return fallback response from Polaris server when fallback occurs. * if none coded fallback or fallbackClass provided, RestTemplate will return fallback response from Polaris server when fallback occurs.
* fallback and fallbackClass cannot provide at same time. * fallback and fallbackClass cannot provide at same time.
@ -34,7 +34,7 @@ import java.lang.annotation.Target;
@Target({ ElementType.METHOD }) @Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
public @interface PolarisCircuitBreakerRestTemplate { public @interface PolarisCircuitBreaker {
/** /**
* a fallback string, will return a response { status: 200, body: fallback string} when any exception occurs. * a fallback string, will return a response { status: 200, body: fallback string} when any exception occurs.

@ -45,51 +45,51 @@ public class PolarisCircuitBreakerRestTemplateBeanPostProcessor implements Merge
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
} }
private final ConcurrentHashMap<String, PolarisCircuitBreakerRestTemplate> cache = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, PolarisCircuitBreaker> cache = new ConcurrentHashMap<>();
private void checkPolarisCircuitBreakerRestTemplate(PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate) { private void checkPolarisCircuitBreakerRestTemplate(PolarisCircuitBreaker polarisCircuitBreaker) {
if ( if (
StringUtils.hasText(polarisCircuitBreakerRestTemplate.fallback()) && StringUtils.hasText(polarisCircuitBreaker.fallback()) &&
!PolarisCircuitBreakerFallback.class.toGenericString().equals(polarisCircuitBreakerRestTemplate.fallbackClass().toGenericString()) !PolarisCircuitBreakerFallback.class.toGenericString().equals(polarisCircuitBreaker.fallbackClass().toGenericString())
) { ) {
throw new IllegalArgumentException("PolarisCircuitBreakerRestTemplate's fallback and fallbackClass could not set at sametime !"); throw new IllegalArgumentException("PolarisCircuitBreaker's fallback and fallbackClass could not set at sametime !");
} }
} }
@Override @Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (checkAnnotated(beanDefinition, beanType, beanName)) { if (checkAnnotated(beanDefinition, beanType, beanName)) {
PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate; PolarisCircuitBreaker polarisCircuitBreaker;
if (beanDefinition.getSource() instanceof StandardMethodMetadata) { if (beanDefinition.getSource() instanceof StandardMethodMetadata) {
polarisCircuitBreakerRestTemplate = ((StandardMethodMetadata) beanDefinition.getSource()).getIntrospectedMethod() polarisCircuitBreaker = ((StandardMethodMetadata) beanDefinition.getSource()).getIntrospectedMethod()
.getAnnotation(PolarisCircuitBreakerRestTemplate.class); .getAnnotation(PolarisCircuitBreaker.class);
} }
else { else {
polarisCircuitBreakerRestTemplate = beanDefinition.getResolvedFactoryMethod() polarisCircuitBreaker = beanDefinition.getResolvedFactoryMethod()
.getAnnotation(PolarisCircuitBreakerRestTemplate.class); .getAnnotation(PolarisCircuitBreaker.class);
} }
checkPolarisCircuitBreakerRestTemplate(polarisCircuitBreakerRestTemplate); checkPolarisCircuitBreakerRestTemplate(polarisCircuitBreaker);
cache.put(beanName, polarisCircuitBreakerRestTemplate); cache.put(beanName, polarisCircuitBreaker);
} }
} }
@Override @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (cache.containsKey(beanName)) { if (cache.containsKey(beanName)) {
// add interceptor for each RestTemplate with @PolarisCircuitBreakerRestTemplate annotation // add interceptor for each RestTemplate with @PolarisCircuitBreaker annotation
StringBuilder interceptorBeanNamePrefix = new StringBuilder(); StringBuilder interceptorBeanNamePrefix = new StringBuilder();
PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate = cache.get(beanName); PolarisCircuitBreaker polarisCircuitBreaker = cache.get(beanName);
interceptorBeanNamePrefix interceptorBeanNamePrefix
.append(StringUtils.uncapitalize( .append(StringUtils.uncapitalize(
PolarisCircuitBreakerRestTemplate.class.getSimpleName())) PolarisCircuitBreaker.class.getSimpleName()))
.append("_") .append("_")
.append(polarisCircuitBreakerRestTemplate.fallback()) .append(polarisCircuitBreaker.fallback())
.append("_") .append("_")
.append(polarisCircuitBreakerRestTemplate.fallbackClass().getSimpleName()); .append(polarisCircuitBreaker.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); CircuitBreakerFactory circuitBreakerFactory = this.applicationContext.getBean(CircuitBreakerFactory.class);
registerBean(interceptorBeanName, polarisCircuitBreakerRestTemplate, applicationContext, circuitBreakerFactory, restTemplate); registerBean(interceptorBeanName, polarisCircuitBreaker, applicationContext, circuitBreakerFactory, restTemplate);
PolarisCircuitBreakerRestTemplateInterceptor polarisCircuitBreakerRestTemplateInterceptor = applicationContext PolarisCircuitBreakerRestTemplateInterceptor polarisCircuitBreakerRestTemplateInterceptor = applicationContext
.getBean(interceptorBeanName, PolarisCircuitBreakerRestTemplateInterceptor.class); .getBean(interceptorBeanName, PolarisCircuitBreakerRestTemplateInterceptor.class);
restTemplate.getInterceptors().add(0, polarisCircuitBreakerRestTemplateInterceptor); restTemplate.getInterceptors().add(0, polarisCircuitBreakerRestTemplateInterceptor);
@ -102,17 +102,17 @@ public class PolarisCircuitBreakerRestTemplateBeanPostProcessor implements Merge
return beanName != null && beanType == RestTemplate.class return beanName != null && beanType == RestTemplate.class
&& beanDefinition.getSource() instanceof MethodMetadata && beanDefinition.getSource() instanceof MethodMetadata
&& ((MethodMetadata) beanDefinition.getSource()) && ((MethodMetadata) beanDefinition.getSource())
.isAnnotated(PolarisCircuitBreakerRestTemplate.class.getName()); .isAnnotated(PolarisCircuitBreaker.class.getName());
} }
private void registerBean(String interceptorBeanName, PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate, private void registerBean(String interceptorBeanName, PolarisCircuitBreaker polarisCircuitBreaker,
ApplicationContext applicationContext, CircuitBreakerFactory circuitBreakerFactory, RestTemplate restTemplate) { ApplicationContext applicationContext, CircuitBreakerFactory circuitBreakerFactory, RestTemplate restTemplate) {
// register PolarisCircuitBreakerRestTemplateInterceptor bean // register PolarisCircuitBreakerRestTemplateInterceptor bean
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
.getAutowireCapableBeanFactory(); .getAutowireCapableBeanFactory();
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(PolarisCircuitBreakerRestTemplateInterceptor.class); .genericBeanDefinition(PolarisCircuitBreakerRestTemplateInterceptor.class);
beanDefinitionBuilder.addConstructorArgValue(polarisCircuitBreakerRestTemplate); beanDefinitionBuilder.addConstructorArgValue(polarisCircuitBreaker);
beanDefinitionBuilder.addConstructorArgValue(applicationContext); beanDefinitionBuilder.addConstructorArgValue(applicationContext);
beanDefinitionBuilder.addConstructorArgValue(circuitBreakerFactory); beanDefinitionBuilder.addConstructorArgValue(circuitBreakerFactory);
beanDefinitionBuilder.addConstructorArgValue(restTemplate); beanDefinitionBuilder.addConstructorArgValue(restTemplate);

@ -44,7 +44,7 @@ import static com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplat
*/ */
public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpRequestInterceptor { public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpRequestInterceptor {
private final PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate; private final PolarisCircuitBreaker polarisCircuitBreaker;
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
@ -53,12 +53,12 @@ public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpR
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
public PolarisCircuitBreakerRestTemplateInterceptor( public PolarisCircuitBreakerRestTemplateInterceptor(
PolarisCircuitBreakerRestTemplate polarisCircuitBreakerRestTemplate, PolarisCircuitBreaker polarisCircuitBreaker,
ApplicationContext applicationContext, ApplicationContext applicationContext,
CircuitBreakerFactory circuitBreakerFactory, CircuitBreakerFactory circuitBreakerFactory,
RestTemplate restTemplate RestTemplate restTemplate
) { ) {
this.polarisCircuitBreakerRestTemplate = polarisCircuitBreakerRestTemplate; this.polarisCircuitBreaker = polarisCircuitBreaker;
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
this.circuitBreakerFactory = circuitBreakerFactory; this.circuitBreakerFactory = circuitBreakerFactory;
this.restTemplate = restTemplate; this.restTemplate = restTemplate;
@ -88,13 +88,13 @@ public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpR
} }
}, },
t -> { t -> {
if (StringUtils.hasText(polarisCircuitBreakerRestTemplate.fallback())) { if (StringUtils.hasText(polarisCircuitBreaker.fallback())) {
CircuitBreakerStatus.FallbackInfo fallbackInfo = new CircuitBreakerStatus.FallbackInfo(200, null, polarisCircuitBreakerRestTemplate.fallback()); CircuitBreakerStatus.FallbackInfo fallbackInfo = new CircuitBreakerStatus.FallbackInfo(200, null, polarisCircuitBreaker.fallback());
return new PolarisCircuitBreakerHttpResponse(fallbackInfo); return new PolarisCircuitBreakerHttpResponse(fallbackInfo);
} }
if (!PolarisCircuitBreakerFallback.class.toGenericString().equals(polarisCircuitBreakerRestTemplate.fallbackClass().toGenericString())) { if (!PolarisCircuitBreakerFallback.class.toGenericString().equals(polarisCircuitBreaker.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(polarisCircuitBreaker.fallbackClass());
return (PolarisCircuitBreakerHttpResponse) ReflectionUtils.invokeMethod(method, polarisCircuitBreakerFallback); return (PolarisCircuitBreakerHttpResponse) ReflectionUtils.invokeMethod(method, polarisCircuitBreakerFallback);
} }
if (t instanceof CallAbortedException) { if (t instanceof CallAbortedException) {

@ -23,12 +23,14 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat; import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.polaris.circuitbreaker.gateway.PolarisCircuitBreakerFilterFactory;
import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory; import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory;
@ -48,6 +50,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.ApplicationContext;
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.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
@ -85,6 +88,9 @@ public class PolarisCircuitBreakerGatewayIntegrationTest {
@Autowired @Autowired
private WebTestClient webClient; private WebTestClient webClient;
@Autowired
private ApplicationContext applicationContext;
private static NamingServer namingServer; private static NamingServer namingServer;
@AfterAll @AfterAll
@ -106,15 +112,41 @@ public class PolarisCircuitBreakerGatewayIntegrationTest {
.consumeWith( .consumeWith(
response -> assertThat(response.getResponseBody()).isEqualTo("fallback".getBytes())); response -> assertThat(response.getResponseBody()).isEqualTo("fallback".getBytes()));
Utils.sleepUninterrupted(2000);
webClient
.get().uri("/err-skip-fallback")
.header("Host", "www.circuitbreaker-skip-fallback.com")
.exchange()
.expectStatus();
Utils.sleepUninterrupted(2000);
// this should be 200, but for some unknown reason, GitHub action run failed in windows, so we skip this check
webClient
.get().uri("/err-skip-fallback")
.header("Host", "www.circuitbreaker-skip-fallback.com")
.exchange()
.expectStatus();
Utils.sleepUninterrupted(2000);
webClient webClient
.get().uri("/err-no-fallback") .get().uri("/err-no-fallback")
.header("Host", "www.circuitbreaker-no-fallback.com") .header("Host", "www.circuitbreaker-no-fallback.com")
.exchange() .exchange()
.expectStatus().isEqualTo(500); .expectStatus();
Utils.sleepUninterrupted(2000);
webClient
.get().uri("/err-no-fallback")
.header("Host", "www.circuitbreaker-no-fallback.com")
.exchange()
.expectStatus();
Utils.sleepUninterrupted(2000); Utils.sleepUninterrupted(2000);
// this should be 200, but for some unknown reason, GitHub action run failed in windows, so we skip this check
webClient webClient
.get().uri("/err-no-fallback") .get().uri("/err-no-fallback")
.header("Host", "www.circuitbreaker-no-fallback.com") .header("Host", "www.circuitbreaker-no-fallback.com")
@ -151,7 +183,6 @@ public class PolarisCircuitBreakerGatewayIntegrationTest {
@Bean @Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) { public RouteLocator myRoutes(RouteLocatorBuilder builder) {
String httpUri = "http://httpbin.org:80";
Set<String> codeSets = new HashSet<>(); Set<String> codeSets = new HashSet<>();
codeSets.add("4**"); codeSets.add("4**");
codeSets.add("5**"); codeSets.add("5**");
@ -164,14 +195,22 @@ public class PolarisCircuitBreakerGatewayIntegrationTest {
.setFallbackUri("forward:/fallback") .setFallbackUri("forward:/fallback")
.setName(TEST_SERVICE_NAME) .setName(TEST_SERVICE_NAME)
)) ))
.uri(httpUri)) .uri("http://httpbin.org:80"))
.route(p -> p
.host("*.circuitbreaker-skip-fallback.com")
.filters(f -> f
.circuitBreaker(config -> config
.setStatusCodes(Collections.singleton("5**"))
.setName(TEST_SERVICE_NAME)
))
.uri("http://httpbin.org:80"))
.route(p -> p .route(p -> p
.host("*.circuitbreaker-no-fallback.com") .host("*.circuitbreaker-no-fallback.com")
.filters(f -> f .filters(f -> f
.circuitBreaker(config -> config .circuitBreaker(config -> config
.setName(TEST_SERVICE_NAME) .setName(TEST_SERVICE_NAME)
)) ))
.uri(httpUri)) .uri("lb://" + TEST_SERVICE_NAME))
.build(); .build();
} }

@ -30,9 +30,9 @@ import java.util.stream.Collectors;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat; import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration; import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerFallback; import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerFallback;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerHttpResponse; import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerHttpResponse;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerRestTemplate;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter; import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter;
import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.polaris.api.core.ConsumerAPI;
@ -83,7 +83,7 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
*/ */
@ExtendWith(SpringExtension.class) @ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = RANDOM_PORT, @SpringBootTest(webEnvironment = RANDOM_PORT,
classes = PolarisCircuitBreakerRestTemplateIntegrationTest.TestConfig.class, classes = PolarisCircuitBreakerIntegrationTest.TestConfig.class,
properties = { properties = {
"spring.cloud.gateway.enabled=false", "spring.cloud.gateway.enabled=false",
"feign.circuitbreaker.enabled=true", "feign.circuitbreaker.enabled=true",
@ -91,7 +91,7 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
"spring.cloud.polaris.service=test" "spring.cloud.polaris.service=test"
}) })
@DirtiesContext @DirtiesContext
public class PolarisCircuitBreakerRestTemplateIntegrationTest { public class PolarisCircuitBreakerIntegrationTest {
private static final String TEST_SERVICE_NAME = "test-service-callee"; private static final String TEST_SERVICE_NAME = "test-service-callee";
@ -174,7 +174,7 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
public static class TestConfig { public static class TestConfig {
@Bean @Bean
@PolarisCircuitBreakerRestTemplate(fallback = "fallback") @PolarisCircuitBreaker(fallback = "fallback")
public RestTemplate defaultRestTemplate(RpcEnhancementReporterProperties properties, ConsumerAPI consumerAPI) { public RestTemplate defaultRestTemplate(RpcEnhancementReporterProperties properties, ConsumerAPI consumerAPI) {
RestTemplate defaultRestTemplate = new RestTemplate(); RestTemplate defaultRestTemplate = new RestTemplate();
EnhancedRestTemplateReporter enhancedRestTemplateReporter = new EnhancedRestTemplateReporter(properties, consumerAPI); EnhancedRestTemplateReporter enhancedRestTemplateReporter = new EnhancedRestTemplateReporter(properties, consumerAPI);
@ -184,7 +184,7 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
@Bean @Bean
@LoadBalanced @LoadBalanced
@PolarisCircuitBreakerRestTemplate @PolarisCircuitBreaker
public RestTemplate restTemplateFallbackFromPolaris() { public RestTemplate restTemplateFallbackFromPolaris() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME); DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME);
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
@ -194,7 +194,7 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
@Bean @Bean
@LoadBalanced @LoadBalanced
@PolarisCircuitBreakerRestTemplate(fallbackClass = CustomPolarisCircuitBreakerFallback.class) @PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback.class)
public RestTemplate restTemplateFallbackFromCode() { public RestTemplate restTemplateFallbackFromCode() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME); DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME);
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
@ -204,7 +204,7 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
@Bean @Bean
@LoadBalanced @LoadBalanced
@PolarisCircuitBreakerRestTemplate(fallbackClass = CustomPolarisCircuitBreakerFallback2.class) @PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback2.class)
public RestTemplate restTemplateFallbackFromCode2() { public RestTemplate restTemplateFallbackFromCode2() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME); DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME);
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
@ -214,7 +214,7 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
@Bean @Bean
@LoadBalanced @LoadBalanced
@PolarisCircuitBreakerRestTemplate(fallbackClass = CustomPolarisCircuitBreakerFallback3.class) @PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback3.class)
public RestTemplate restTemplateFallbackFromCode3() { public RestTemplate restTemplateFallbackFromCode3() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME); DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME);
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
@ -224,7 +224,7 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
@Bean @Bean
@LoadBalanced @LoadBalanced
@PolarisCircuitBreakerRestTemplate(fallback = "fallback") @PolarisCircuitBreaker(fallback = "fallback")
public RestTemplate restTemplateFallbackFromCode4() { public RestTemplate restTemplateFallbackFromCode4() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME); DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME);
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();

@ -35,8 +35,8 @@ spring:
'filters[1]': 'filters[1]':
name: CircuitBreaker name: CircuitBreaker
args: args:
# statusCodes 缺省时会自动识别 "4**5**" 为错误 # statusCodes 缺省时会自动识别 "4**,5**" 为错误
statusCodes: '''4**,502''' # statusCodes: '''4**,502'''
# fallbackUri 缺省时会在熔断触发后拉取 plaris server 配置的降级作为 response # fallbackUri 缺省时会在熔断触发后拉取 plaris server 配置的降级作为 response
fallbackUri: '''forward:/polaris-fallback''' fallbackUri: '''forward:/polaris-fallback'''
# routes: # routes:

@ -18,7 +18,7 @@
package com.tencent.cloud.polaris.circuitbreaker.resttemplate.example; package com.tencent.cloud.polaris.circuitbreaker.resttemplate.example;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerRestTemplate; import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -50,7 +50,7 @@ public class ServiceAResTemplate {
@Bean @Bean
@LoadBalanced @LoadBalanced
@PolarisCircuitBreakerRestTemplate @PolarisCircuitBreaker
public RestTemplate restTemplateFallbackFromPolaris() { public RestTemplate restTemplateFallbackFromPolaris() {
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();
@ -60,7 +60,7 @@ public class ServiceAResTemplate {
@Bean @Bean
@LoadBalanced @LoadBalanced
@PolarisCircuitBreakerRestTemplate(fallbackClass = CustomFallback.class) @PolarisCircuitBreaker(fallbackClass = CustomFallback.class)
public RestTemplate restTemplateFallbackFromCode() { public RestTemplate restTemplateFallbackFromCode() {
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();

Loading…
Cancel
Save