diff --git a/CHANGELOG.md b/CHANGELOG.md index c1f85bb12..3ba2c8459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ - [fix:fix the error capture of rate limit exception.](https://github.com/Tencent/spring-cloud-tencent/pull/854) - [feature:add User-Agent:polaris for healthyCheck api.](https://github.com/Tencent/spring-cloud-tencent/pull/859) - [feat:enable stat reporting as default.](https://github.com/Tencent/spring-cloud-tencent/pull/862) -- [optimize:optimize ServiceRuleManager.](https://github.com/Tencent/spring-cloud-tencent/pull/869) - [refactor:update to junit 5.](https://github.com/Tencent/spring-cloud-tencent/pull/865) +- [optimize:optimize ServiceRuleManager.](https://github.com/Tencent/spring-cloud-tencent/pull/869) - [docs:support auto snapshot release in GitHub Action.](https://github.com/Tencent/spring-cloud-tencent/pull/868) +- [feature:add polaris circuit breaker support.](https://github.com/Tencent/spring-cloud-tencent/pull/875) - [refactor:refactor stat module.](https://github.com/Tencent/spring-cloud-tencent/pull/876) diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml b/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml index dce497bd1..6ce29236c 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml @@ -14,6 +14,30 @@ + + org.springframework.boot + spring-boot-starter-web + true + + + + org.springframework.boot + spring-boot-starter-webflux + true + + + + org.springframework.cloud + spring-cloud-starter-openfeign + true + + + + org.springframework.cloud + spring-cloud-starter-gateway + true + + com.tencent.cloud spring-cloud-tencent-rpc-enhancement @@ -55,6 +79,7 @@ + @@ -62,5 +87,35 @@ spring-boot-starter-test test + + + org.springframework.cloud + spring-cloud-starter-contract-stub-runner + test + + + + com.tencent.polaris + polaris-test-common + test + + + + com.tencent.polaris + polaris-test-mock-discovery + test + + + + org.mockito + mockito-inline + test + + + + io.projectreactor + reactor-test + test + diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreaker.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreaker.java new file mode 100644 index 000000000..d1d70b9e3 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreaker.java @@ -0,0 +1,68 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker; + +import java.util.function.Function; +import java.util.function.Supplier; + +import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder; +import com.tencent.cloud.polaris.circuitbreaker.common.PolarisResultToErrorCode; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.circuitbreak.api.FunctionalDecorator; +import com.tencent.polaris.circuitbreak.api.pojo.FunctionalDecoratorRequest; +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; + +/** + * PolarisCircuitBreaker. + * + * @author seanyu 2023-02-27 + */ +public class PolarisCircuitBreaker implements CircuitBreaker { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisCircuitBreaker.class); + + private final FunctionalDecorator decorator; + + public PolarisCircuitBreaker(PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf, CircuitBreakAPI circuitBreakAPI) { + FunctionalDecoratorRequest makeDecoratorRequest = new FunctionalDecoratorRequest(new ServiceKey(conf.getNamespace(), conf.getService()), conf.getMethod()); + makeDecoratorRequest.setSourceService(new ServiceKey(conf.getSourceNamespace(), conf.getSourceService())); + makeDecoratorRequest.setResultToErrorCode(new PolarisResultToErrorCode()); + this.decorator = circuitBreakAPI.makeFunctionalDecorator(makeDecoratorRequest); + } + + @Override + public T run(Supplier toRun, Function fallback) { + Supplier toRunDecorator = decorator.decorateSupplier(toRun); + try { + return toRunDecorator.get(); + } + catch (CallAbortedException e) { + LOGGER.debug("PolarisCircuitBreaker CallAbortedException: {}", e.getMessage()); + return fallback.apply(e); + } + catch (Exception e) { + return fallback.apply(e); + } + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerFactory.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerFactory.java new file mode 100644 index 000000000..d80ae90e5 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerFactory.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker; + +import java.util.function.Function; + +import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder; +import com.tencent.cloud.polaris.circuitbreaker.util.PolarisCircuitBreakerUtils; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; + +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; + +/** + * PolarisCircuitBreakerFactory. + * + * @author seanyu 2023-02-27 + */ +public class PolarisCircuitBreakerFactory + extends CircuitBreakerFactory { + + private Function defaultConfiguration = + id -> { + String[] metadata = PolarisCircuitBreakerUtils.resolveCircuitBreakerId(id); + return new PolarisCircuitBreakerConfigBuilder() + .namespace(metadata[0]) + .service(metadata[1]) + .method(metadata[2]) + .build(); + }; + + + private final CircuitBreakAPI circuitBreakAPI; + + public PolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI) { + this.circuitBreakAPI = circuitBreakAPI; + } + + @Override + public CircuitBreaker create(String id) { + PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf = getConfigurations() + .computeIfAbsent(id, defaultConfiguration); + return new PolarisCircuitBreaker(conf, circuitBreakAPI); + } + + @Override + protected PolarisCircuitBreakerConfigBuilder configBuilder(String id) { + String[] metadata = PolarisCircuitBreakerUtils.resolveCircuitBreakerId(id); + return new PolarisCircuitBreakerConfigBuilder(metadata[0], metadata[1], metadata[2]); + } + + @Override + public void configureDefault(Function defaultConfiguration) { + this.defaultConfiguration = defaultConfiguration; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreaker.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreaker.java new file mode 100644 index 000000000..cfd7e481a --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreaker.java @@ -0,0 +1,70 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker; + +import java.util.function.Function; + +import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder; +import com.tencent.cloud.polaris.circuitbreaker.common.PolarisResultToErrorCode; +import com.tencent.cloud.polaris.circuitbreaker.reactor.PolarisCircuitBreakerReactorTransformer; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.circuitbreak.api.InvokeHandler; +import com.tencent.polaris.circuitbreak.api.pojo.FunctionalDecoratorRequest; +import com.tencent.polaris.circuitbreak.api.pojo.InvokeContext; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker; + +/** + * ReactivePolarisCircuitBreaker. + * + * @author seanyu 2023-02-27 + */ +public class ReactivePolarisCircuitBreaker implements ReactiveCircuitBreaker { + + private final InvokeHandler invokeHandler; + + public ReactivePolarisCircuitBreaker(PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf, CircuitBreakAPI circuitBreakAPI) { + InvokeContext.RequestContext requestContext = new FunctionalDecoratorRequest(new ServiceKey(conf.getNamespace(), conf.getService()), conf.getMethod()); + requestContext.setSourceService(new ServiceKey(conf.getSourceNamespace(), conf.getSourceService())); + requestContext.setResultToErrorCode(new PolarisResultToErrorCode()); + this.invokeHandler = circuitBreakAPI.makeInvokeHandler(requestContext); + } + + + @Override + public Mono run(Mono toRun, Function> fallback) { + Mono toReturn = toRun.transform(new PolarisCircuitBreakerReactorTransformer<>(invokeHandler)); + if (fallback != null) { + toReturn = toReturn.onErrorResume(fallback); + } + return toReturn; + } + + @Override + public Flux run(Flux toRun, Function> fallback) { + Flux toReturn = toRun.transform(new PolarisCircuitBreakerReactorTransformer<>(invokeHandler)); + if (fallback != null) { + toReturn = toReturn.onErrorResume(fallback); + } + return toReturn; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerFactory.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerFactory.java new file mode 100644 index 000000000..4a481e866 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerFactory.java @@ -0,0 +1,73 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker; + +import java.util.function.Function; + +import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder; +import com.tencent.cloud.polaris.circuitbreaker.util.PolarisCircuitBreakerUtils; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; + +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory; + +/** + * ReactivePolarisCircuitBreakerFactory. + * + * @author seanyu 2023-02-27 + */ +public class ReactivePolarisCircuitBreakerFactory extends + ReactiveCircuitBreakerFactory { + + private Function defaultConfiguration = + id -> { + String[] metadata = PolarisCircuitBreakerUtils.resolveCircuitBreakerId(id); + return new PolarisCircuitBreakerConfigBuilder() + .namespace(metadata[0]) + .service(metadata[1]) + .method(metadata[2]) + .build(); + }; + + private final CircuitBreakAPI circuitBreakAPI; + + public ReactivePolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI) { + this.circuitBreakAPI = circuitBreakAPI; + } + + + @Override + public ReactiveCircuitBreaker create(String id) { + PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf = getConfigurations() + .computeIfAbsent(id, defaultConfiguration); + return new ReactivePolarisCircuitBreaker(conf, circuitBreakAPI); + } + + @Override + protected PolarisCircuitBreakerConfigBuilder configBuilder(String id) { + String[] metadata = PolarisCircuitBreakerUtils.resolveCircuitBreakerId(id); + return new PolarisCircuitBreakerConfigBuilder(metadata[0], metadata[1], metadata[2]); + } + + @Override + public void configureDefault( + Function defaultConfiguration) { + this.defaultConfiguration = defaultConfiguration; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/common/CircuitBreakerConfigModifier.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/common/CircuitBreakerConfigModifier.java new file mode 100644 index 000000000..220d98490 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/common/CircuitBreakerConfigModifier.java @@ -0,0 +1,62 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.common; + +import com.tencent.cloud.common.constant.ContextConstant; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.plugins.router.healthy.RecoverRouterConfig; + +/** + * CircuitBreakerConfigModifier. + * + * @author seanyu 2023-02-27 + */ +public class CircuitBreakerConfigModifier implements PolarisConfigModifier { + + private final RpcEnhancementReporterProperties properties; + + public CircuitBreakerConfigModifier(RpcEnhancementReporterProperties properties) { + this.properties = properties; + } + + @Override + public void modify(ConfigurationImpl configuration) { + properties.setEnabled(true); + + // Turn on circuitbreaker configuration + configuration.getConsumer().getCircuitBreaker().setEnable(true); + + // Set excludeCircuitBreakInstances to true + RecoverRouterConfig recoverRouterConfig = configuration.getConsumer().getServiceRouter() + .getPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, RecoverRouterConfig.class); + + recoverRouterConfig.setExcludeCircuitBreakInstances(true); + + // Update modified config to source properties + configuration.getConsumer().getServiceRouter() + .setPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, recoverRouterConfig); + } + + @Override + public int getOrder() { + return ContextConstant.ModifierOrder.CIRCUIT_BREAKER_ORDER; + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/common/PolarisCircuitBreakerConfigBuilder.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/common/PolarisCircuitBreakerConfigBuilder.java new file mode 100644 index 000000000..733180491 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/common/PolarisCircuitBreakerConfigBuilder.java @@ -0,0 +1,116 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.common; + +import com.tencent.cloud.common.metadata.MetadataContext; + +import org.springframework.cloud.client.circuitbreaker.ConfigBuilder; + +/** + * PolarisCircuitBreakerConfigBuilder. + * + * @author seanyu 2023-02-27 + */ +public class PolarisCircuitBreakerConfigBuilder implements ConfigBuilder { + + private String namespace = MetadataContext.LOCAL_NAMESPACE; + + private String service; + + private String method; + + public PolarisCircuitBreakerConfigBuilder() { + + } + + public PolarisCircuitBreakerConfigBuilder(String namespace, String service, String method) { + this.namespace = namespace; + this.service = service; + this.method = method; + } + + public PolarisCircuitBreakerConfigBuilder namespace(String namespace) { + this.namespace = namespace; + return this; + } + + public PolarisCircuitBreakerConfigBuilder service(String service) { + this.service = service; + return this; + } + + public PolarisCircuitBreakerConfigBuilder method(String method) { + this.method = method; + return this; + } + + @Override + public PolarisCircuitBreakerConfiguration build() { + PolarisCircuitBreakerConfiguration conf = new PolarisCircuitBreakerConfiguration(); + conf.setNamespace(namespace); + conf.setService(service); + conf.setMethod(method); + return conf; + } + + public static class PolarisCircuitBreakerConfiguration { + + private final String sourceNamespace = MetadataContext.LOCAL_NAMESPACE; + + private final String sourceService = MetadataContext.LOCAL_SERVICE; + + private String namespace; + + private String service; + + private String method; + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getSourceNamespace() { + return sourceNamespace; + } + + public String getSourceService() { + return sourceService; + } + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/common/PolarisResultToErrorCode.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/common/PolarisResultToErrorCode.java new file mode 100644 index 000000000..3d0badf3a --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/common/PolarisResultToErrorCode.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.common; + +import com.tencent.polaris.circuitbreak.api.pojo.ResultToErrorCode; +import feign.FeignException; + +import org.springframework.web.client.RestClientResponseException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** + * PolarisResultToErrorCode. + * + * @author seanyu 2023-02-27 + */ +public class PolarisResultToErrorCode implements ResultToErrorCode { + + @Override + public int onSuccess(Object value) { + return 200; + } + + @Override + public int onError(Throwable e) { + if (checkClassExist("org.springframework.web.client.RestClientResponseException") + && e instanceof RestClientResponseException) { + return ((RestClientResponseException) e).getRawStatusCode(); + } + else if (checkClassExist("feign.FeignException") + && e instanceof FeignException) { + return ((FeignException) e).status(); + } + else if (checkClassExist("org.springframework.web.reactive.function.client.WebClientResponseException") + && e instanceof WebClientResponseException) { + return ((WebClientResponseException) e).getRawStatusCode(); + } + return -1; + } + + private boolean checkClassExist(String clazzName) { + try { + Class.forName(clazzName, false, getClass().getClassLoader()); + } + catch (ClassNotFoundException e) { + return false; + } + return true; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/GatewayPolarisCircuitBreakerAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/GatewayPolarisCircuitBreakerAutoConfiguration.java new file mode 100644 index 000000000..e97358015 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/GatewayPolarisCircuitBreakerAutoConfiguration.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.config; + +import com.tencent.cloud.polaris.circuitbreaker.ReactivePolarisCircuitBreakerFactory; +import com.tencent.cloud.polaris.circuitbreaker.gateway.PolarisCircuitBreakerFilterFactory; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; +import org.springframework.cloud.gateway.config.GatewayAutoConfiguration; +import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledFilter; +import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; +import org.springframework.cloud.gateway.filter.factory.FallbackHeadersGatewayFilterFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.DispatcherHandler; + +/** + * GatewayPolarisCircuitBreakerAutoConfiguration. + * + * @author seanyu 2023-02-27 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true) +@AutoConfigureAfter({ReactivePolarisCircuitBreakerAutoConfiguration.class }) +@ConditionalOnClass({ DispatcherHandler.class, ReactivePolarisCircuitBreakerAutoConfiguration.class, + ReactiveCircuitBreakerFactory.class, ReactivePolarisCircuitBreakerFactory.class, GatewayAutoConfiguration.class}) +public class GatewayPolarisCircuitBreakerAutoConfiguration { + + @Bean + @ConditionalOnBean(ReactiveCircuitBreakerFactory.class) + @ConditionalOnEnabledFilter + public PolarisCircuitBreakerFilterFactory polarisCircuitBreakerFilterFactory( + ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory, + ObjectProvider dispatcherHandler, + @Autowired(required = false) ReactiveDiscoveryClient discoveryClient, + @Autowired(required = false) DiscoveryLocatorProperties properties + ) { + return new PolarisCircuitBreakerFilterFactory(reactiveCircuitBreakerFactory, dispatcherHandler, discoveryClient, properties); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnEnabledFilter + public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() { + return new FallbackHeadersGatewayFilterFactory(); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerAutoConfiguration.java index a245d695a..f838214f2 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerAutoConfiguration.java @@ -17,20 +17,29 @@ package com.tencent.cloud.polaris.circuitbreaker.config; -import com.tencent.cloud.common.constant.ContextConstant; -import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import java.util.ArrayList; +import java.util.List; + +import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreakerFactory; +import com.tencent.cloud.polaris.circuitbreaker.common.CircuitBreakerConfigModifier; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; -import com.tencent.polaris.factory.config.ConfigurationImpl; -import com.tencent.polaris.plugins.router.healthy.RecoverRouterConfig; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory; +import com.tencent.polaris.client.api.SDKContext; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.cloud.client.circuitbreaker.Customizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; + /** - * Autoconfiguration at bootstrap phase. + * Autoconfiguration for PolarisCircuitBreaker. * * @author lepdou 2022-03-29 */ @@ -39,40 +48,28 @@ import org.springframework.context.annotation.Configuration; @AutoConfigureAfter(RpcEnhancementAutoConfiguration.class) public class PolarisCircuitBreakerAutoConfiguration { + @Autowired(required = false) + private List> customizers = new ArrayList<>(); + @Bean - public CircuitBreakerConfigModifier circuitBreakerConfigModifier(RpcEnhancementReporterProperties properties) { - return new CircuitBreakerConfigModifier(properties); + @ConditionalOnMissingBean(CircuitBreakAPI.class) + public CircuitBreakAPI circuitBreakAPI(SDKContext polarisContext) { + return CircuitBreakAPIFactory.createCircuitBreakAPIByContext(polarisContext); } - public static class CircuitBreakerConfigModifier implements PolarisConfigModifier { - - private final RpcEnhancementReporterProperties properties; - - public CircuitBreakerConfigModifier(RpcEnhancementReporterProperties properties) { - this.properties = properties; - } - - @Override - public void modify(ConfigurationImpl configuration) { - properties.setEnabled(true); - - // Turn on circuitbreaker configuration - configuration.getConsumer().getCircuitBreaker().setEnable(true); - - // Set excludeCircuitBreakInstances to true - RecoverRouterConfig recoverRouterConfig = configuration.getConsumer().getServiceRouter() - .getPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, RecoverRouterConfig.class); - - recoverRouterConfig.setExcludeCircuitBreakInstances(true); - - // Update modified config to source properties - configuration.getConsumer().getServiceRouter() - .setPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, recoverRouterConfig); - } + @Bean + @ConditionalOnMissingBean(CircuitBreakerFactory.class) + public CircuitBreakerFactory polarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI) { + PolarisCircuitBreakerFactory factory = new PolarisCircuitBreakerFactory(circuitBreakAPI); + customizers.forEach(customizer -> customizer.customize(factory)); + return factory; + } - @Override - public int getOrder() { - return ContextConstant.ModifierOrder.CIRCUIT_BREAKER_ORDER; - } + @Bean + @ConditionalOnBean(RpcEnhancementReporterProperties.class) + @ConditionalOnMissingBean(CircuitBreakerConfigModifier.class) + public CircuitBreakerConfigModifier circuitBreakerConfigModifier(RpcEnhancementReporterProperties properties) { + return new CircuitBreakerConfigModifier(properties); } + } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfiguration.java index 9d21fbf0d..c1cafea0b 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfiguration.java @@ -28,7 +28,7 @@ import org.springframework.context.annotation.Import; */ @Configuration(proxyBeanMethods = false) @ConditionalOnProperty("spring.cloud.polaris.enabled") -@Import(PolarisCircuitBreakerAutoConfiguration.class) +@Import({PolarisCircuitBreakerAutoConfiguration.class, ReactivePolarisCircuitBreakerAutoConfiguration.class, PolarisCircuitBreakerFeignClientAutoConfiguration.class}) public class PolarisCircuitBreakerBootstrapConfiguration { } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerFeignClientAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerFeignClientAutoConfiguration.java new file mode 100644 index 000000000..91096e1e6 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerFeignClientAutoConfiguration.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.config; + +import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisCircuitBreakerNameResolver; +import feign.Feign; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.openfeign.CircuitBreakerNameResolver; +import org.springframework.cloud.openfeign.FeignClientFactoryBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * PolarisCircuitBreakerFeignClientAutoConfiguration. + * + * @author seansyyu 2023-02-28 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass({ Feign.class, FeignClientFactoryBean.class }) +@ConditionalOnPolarisCircuitBreakerEnabled +public class PolarisCircuitBreakerFeignClientAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(CircuitBreakerNameResolver.class) + public CircuitBreakerNameResolver polarisCircuitBreakerNameResolver() { + return new PolarisCircuitBreakerNameResolver(); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/ReactivePolarisCircuitBreakerAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/ReactivePolarisCircuitBreakerAutoConfiguration.java new file mode 100644 index 000000000..58ca348a5 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/ReactivePolarisCircuitBreakerAutoConfiguration.java @@ -0,0 +1,76 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.config; + +import java.util.ArrayList; +import java.util.List; + +import com.tencent.cloud.polaris.circuitbreaker.ReactivePolarisCircuitBreakerFactory; +import com.tencent.cloud.polaris.circuitbreaker.common.CircuitBreakerConfigModifier; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory; +import com.tencent.polaris.client.api.SDKContext; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.client.circuitbreaker.Customizer; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * AutoConfiguration for ReactivePolarisCircuitBreaker. + * + * @author seanyu 2023-02-27 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(name = { "reactor.core.publisher.Mono", "reactor.core.publisher.Flux" }) +@ConditionalOnPolarisCircuitBreakerEnabled +@AutoConfigureAfter(RpcEnhancementAutoConfiguration.class) +public class ReactivePolarisCircuitBreakerAutoConfiguration { + + @Autowired(required = false) + private List> customizers = new ArrayList<>(); + + @Bean + @ConditionalOnMissingBean(CircuitBreakAPI.class) + public CircuitBreakAPI circuitBreakAPI(SDKContext polarisContext) { + return CircuitBreakAPIFactory.createCircuitBreakAPIByContext(polarisContext); + } + + @Bean + @ConditionalOnMissingBean(ReactiveCircuitBreakerFactory.class) + public ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI) { + ReactivePolarisCircuitBreakerFactory factory = new ReactivePolarisCircuitBreakerFactory(circuitBreakAPI); + customizers.forEach(customizer -> customizer.customize(factory)); + return factory; + } + + @Bean + @ConditionalOnBean(RpcEnhancementReporterProperties.class) + @ConditionalOnMissingBean(CircuitBreakerConfigModifier.class) + public CircuitBreakerConfigModifier reactiveCircuitBreakerConfigModifier(RpcEnhancementReporterProperties properties) { + return new CircuitBreakerConfigModifier(properties); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerNameResolver.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerNameResolver.java new file mode 100644 index 000000000..2b1689c45 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerNameResolver.java @@ -0,0 +1,51 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.feign; + +import java.lang.reflect.Method; + +import com.tencent.cloud.common.metadata.MetadataContext; +import feign.Target; + +import org.springframework.cloud.openfeign.CircuitBreakerNameResolver; +import org.springframework.web.bind.annotation.RequestMapping; + +import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation; + +/** + * PolarisCircuitBreakerNameResolver. + * + * @author seanyu 2023-02-27 + */ +public class PolarisCircuitBreakerNameResolver implements CircuitBreakerNameResolver { + + @Override + public String resolveCircuitBreakerName(String feignClientName, Target target, Method method) { + RequestMapping requestMapping = findMergedAnnotation(method, RequestMapping.class); + String path = ""; + if (requestMapping != null) { + path = requestMapping.path().length == 0 ? + requestMapping.value().length == 0 ? "" : requestMapping.value()[0] : + requestMapping.path()[0]; + } + return "".equals(path) ? + MetadataContext.LOCAL_NAMESPACE + "#" + feignClientName : + MetadataContext.LOCAL_NAMESPACE + "#" + feignClientName + "#" + path; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/PolarisCircuitBreakerFilterFactory.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/PolarisCircuitBreakerFilterFactory.java new file mode 100644 index 000000000..90a9749fe --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/PolarisCircuitBreakerFilterFactory.java @@ -0,0 +1,228 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.gateway; + + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +import org.springframework.beans.InvalidPropertyException; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; +import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.factory.SpringCloudCircuitBreakerFilterFactory; +import org.springframework.cloud.gateway.support.HttpStatusHolder; +import org.springframework.cloud.gateway.support.ServiceUnavailableException; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.StringUtils; +import org.springframework.web.reactive.DispatcherHandler; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.util.UriComponentsBuilder; + +import static java.util.Optional.ofNullable; +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.GATEWAY_REQUEST_URL_ATTR; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.reset; + +/** + * PolarisCircuitBreakerFilterFactory. + * mostly copy from SpringCloudCircuitBreakerFilterFactory, but create ReactiveCircuitBreaker per request to build method level CircuitBreaker. + * + * @author seanyu 2023-02-27 + */ +public class PolarisCircuitBreakerFilterFactory extends SpringCloudCircuitBreakerFilterFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisCircuitBreakerFilterFactory.class); + + private String routeIdPrefix; + + private final ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory; + + private final ObjectProvider dispatcherHandlerProvider; + + // do not use this dispatcherHandler directly, use getDispatcherHandler() instead. + private volatile DispatcherHandler dispatcherHandler; + + public PolarisCircuitBreakerFilterFactory( + ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory, + ObjectProvider dispatcherHandlerProvider, + ReactiveDiscoveryClient discoveryClient, + DiscoveryLocatorProperties properties + ) { + super(reactiveCircuitBreakerFactory, dispatcherHandlerProvider); + this.reactiveCircuitBreakerFactory = reactiveCircuitBreakerFactory; + this.dispatcherHandlerProvider = dispatcherHandlerProvider; + if (discoveryClient != null && properties != null) { + if (StringUtils.hasText(properties.getRouteIdPrefix())) { + routeIdPrefix = properties.getRouteIdPrefix(); + } + else { + routeIdPrefix = discoveryClient.getClass().getSimpleName() + "_"; + } + } + } + + private void addExceptionDetails(Throwable t, ServerWebExchange exchange) { + ofNullable(t).ifPresent( + exception -> exchange.getAttributes().put(CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR, exception)); + } + + private DispatcherHandler getDispatcherHandler() { + if (dispatcherHandler == null) { + dispatcherHandler = dispatcherHandlerProvider.getIfAvailable(); + } + return dispatcherHandler; + } + + private String getCircuitBreakerId(Config config) { + if (!StringUtils.hasText(config.getName()) && StringUtils.hasText(config.getRouteId())) { + if (routeIdPrefix != null && config.getRouteId().startsWith(routeIdPrefix)) { + return config.getRouteId().replace(routeIdPrefix, ""); + } + return config.getRouteId(); + } + return config.getName(); + } + + private boolean isNumeric(String statusString) { + try { + Integer.parseInt(statusString); + return true; + } + catch (NumberFormatException e) { + return false; + } + } + + private List getSeriesStatus(String series) { + if (!Arrays.asList("1**", "2**", "3**", "4**", "5**").contains(series)) { + throw new InvalidPropertyException(Config.class, "statusCodes", "polaris circuit breaker status code can only be a numeric http status, or a http series pattern, e.g. [\"1**\",\"2**\",\"3**\",\"4**\",\"5**\"]"); + } + HttpStatus[] allHttpStatus = HttpStatus.values(); + if (series.startsWith("1")) { + return Arrays.stream(allHttpStatus).filter(HttpStatus::is1xxInformational).collect(Collectors.toList()); + } + else if (series.startsWith("2")) { + return Arrays.stream(allHttpStatus).filter(HttpStatus::is2xxSuccessful).collect(Collectors.toList()); + } + else if (series.startsWith("3")) { + return Arrays.stream(allHttpStatus).filter(HttpStatus::is3xxRedirection).collect(Collectors.toList()); + } + else if (series.startsWith("4")) { + return Arrays.stream(allHttpStatus).filter(HttpStatus::is4xxClientError).collect(Collectors.toList()); + } + else if (series.startsWith("5")) { + return Arrays.stream(allHttpStatus).filter(HttpStatus::is5xxServerError).collect(Collectors.toList()); + } + return Arrays.asList(allHttpStatus); + } + + @Override + public GatewayFilter apply(Config config) { + Set statuses = config.getStatusCodes().stream() + .flatMap(statusCode -> { + List httpStatuses = new ArrayList<>(); + if (isNumeric(statusCode)) { + httpStatuses.add(HttpStatusHolder.parse(statusCode).getHttpStatus()); + } + else { + httpStatuses.addAll(getSeriesStatus(statusCode)); + } + return httpStatuses.stream(); + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + String circuitBreakerId = getCircuitBreakerId(config); + return new GatewayFilter() { + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + String path = exchange.getRequest().getPath().value(); + ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(circuitBreakerId + "#" + path); + return cb.run(chain.filter(exchange).doOnSuccess(v -> { + if (statuses.contains(exchange.getResponse().getStatusCode())) { + HttpStatus status = exchange.getResponse().getStatusCode(); + throw new CircuitBreakerStatusCodeException(status); + } + }), t -> { + if (config.getFallbackUri() == null) { + return Mono.error(t); + } + + exchange.getResponse().setStatusCode(null); + reset(exchange); + + // TODO: copied from RouteToRequestUrlFilter + 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); + + // Reset the exchange + reset(exchange); + + ServerHttpRequest request = exchange.getRequest().mutate().uri(requestUrl).build(); + return getDispatcherHandler().handle(exchange.mutate().request(request).build()); + }).onErrorResume(t -> handleErrorWithoutFallback(t, config.isResumeWithoutError())); + } + + @Override + public String toString() { + return filterToStringCreator(PolarisCircuitBreakerFilterFactory.this) + .append("name", config.getName()).append("fallback", config.getFallbackUri()).toString(); + } + }; + + } + + @Override + protected Mono handleErrorWithoutFallback(Throwable t, boolean resumeWithoutError) { + if (t instanceof java.util.concurrent.TimeoutException) { + return Mono.error(new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, t.getMessage(), t)); + } + if (t instanceof CallAbortedException) { + LOGGER.debug("PolarisCircuitBreaker CallAbortedException: {}", t.getMessage()); + return Mono.error(new ServiceUnavailableException()); + } + if (resumeWithoutError) { + return Mono.empty(); + } + return Mono.error(t); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerFluxOperator.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerFluxOperator.java new file mode 100644 index 000000000..faad71135 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerFluxOperator.java @@ -0,0 +1,57 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.reactor; + +import com.tencent.polaris.circuitbreak.api.InvokeHandler; +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.CoreSubscriber; +import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxOperator; +import reactor.core.publisher.Operators; + +/** + * FluxOperator for PolarisCircuitBreaker. + * + * @author seanyu 2023-02-27 + */ +public class PolarisCircuitBreakerFluxOperator extends FluxOperator { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisCircuitBreakerFluxOperator.class); + + private final InvokeHandler invokeHandler; + + protected PolarisCircuitBreakerFluxOperator(Flux source, InvokeHandler invokeHandler) { + super(source); + this.invokeHandler = invokeHandler; + } + + @Override + public void subscribe(CoreSubscriber actual) { + try { + invokeHandler.acquirePermission(); + source.subscribe(new PolarisCircuitBreakerReactorSubscriber<>(invokeHandler, actual, false)); + } + catch (CallAbortedException e) { + LOGGER.debug("ReactivePolarisCircuitBreaker CallAbortedException: {}", e.getMessage()); + Operators.error(actual, e); + } + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerMonoOperator.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerMonoOperator.java new file mode 100644 index 000000000..6f0a44fef --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerMonoOperator.java @@ -0,0 +1,57 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.reactor; + +import com.tencent.polaris.circuitbreak.api.InvokeHandler; +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.CoreSubscriber; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoOperator; +import reactor.core.publisher.Operators; + +/** + * MonoOperator for PolarisCircuitBreaker. + * + * @author seanyu 2023-02-27 + */ +public class PolarisCircuitBreakerMonoOperator extends MonoOperator { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisCircuitBreakerMonoOperator.class); + + private final InvokeHandler invokeHandler; + + protected PolarisCircuitBreakerMonoOperator(Mono source, InvokeHandler invokeHandler) { + super(source); + this.invokeHandler = invokeHandler; + } + + @Override + public void subscribe(CoreSubscriber actual) { + try { + invokeHandler.acquirePermission(); + source.subscribe(new PolarisCircuitBreakerReactorSubscriber<>(invokeHandler, actual, true)); + } + catch (CallAbortedException e) { + LOGGER.debug("ReactivePolarisCircuitBreaker CallAbortedException: {}", e.getMessage()); + Operators.error(actual, e); + } + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerReactorSubscriber.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerReactorSubscriber.java new file mode 100644 index 000000000..202f63369 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerReactorSubscriber.java @@ -0,0 +1,119 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.reactor; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.tencent.polaris.circuitbreak.api.InvokeHandler; +import com.tencent.polaris.circuitbreak.api.pojo.InvokeContext; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; +import reactor.core.publisher.BaseSubscriber; +import reactor.util.context.Context; + +/** + * Reactor Subscriber for PolarisCircuitBreaker. + * + * @author seanyu 2023-02-27 + */ +public class PolarisCircuitBreakerReactorSubscriber extends BaseSubscriber { + + private final InvokeHandler invokeHandler; + + + private final CoreSubscriber downstreamSubscriber; + + private final long startTimeMilli; + private final boolean singleProducer; + + private final AtomicBoolean successSignaled = new AtomicBoolean(false); + private final AtomicBoolean eventWasEmitted = new AtomicBoolean(false); + + public PolarisCircuitBreakerReactorSubscriber(InvokeHandler invokeHandler, CoreSubscriber downstreamSubscriber, boolean singleProducer) { + this.invokeHandler = invokeHandler; + this.downstreamSubscriber = downstreamSubscriber; + this.singleProducer = singleProducer; + this.startTimeMilli = System.currentTimeMillis(); + } + + @Override + public Context currentContext() { + return downstreamSubscriber.currentContext(); + } + + @Override + protected void hookOnSubscribe(Subscription subscription) { + downstreamSubscriber.onSubscribe(this); + } + + @Override + protected void hookOnNext(T value) { + if (!isDisposed()) { + if (singleProducer && successSignaled.compareAndSet(false, true)) { + long delay = System.currentTimeMillis() - startTimeMilli; + InvokeContext.ResponseContext responseContext = new InvokeContext.ResponseContext(); + responseContext.setDuration(delay); + responseContext.setDurationUnit(TimeUnit.MILLISECONDS); + responseContext.setResult(value); + invokeHandler.onSuccess(responseContext); + } + eventWasEmitted.set(true); + + downstreamSubscriber.onNext(value); + } + } + + @Override + protected void hookOnComplete() { + if (successSignaled.compareAndSet(false, true)) { + long delay = System.currentTimeMillis() - startTimeMilli; + InvokeContext.ResponseContext responseContext = new InvokeContext.ResponseContext(); + responseContext.setDuration(delay); + responseContext.setDurationUnit(TimeUnit.MILLISECONDS); + invokeHandler.onSuccess(responseContext); + } + + downstreamSubscriber.onComplete(); + } + + @Override + public void hookOnCancel() { + if (!successSignaled.get()) { + if (eventWasEmitted.get()) { + long delay = System.currentTimeMillis() - startTimeMilli; + InvokeContext.ResponseContext responseContext = new InvokeContext.ResponseContext(); + responseContext.setDuration(delay); + responseContext.setDurationUnit(TimeUnit.MILLISECONDS); + invokeHandler.onSuccess(responseContext); + } + } + } + + @Override + protected void hookOnError(Throwable e) { + long delay = System.currentTimeMillis() - startTimeMilli; + InvokeContext.ResponseContext responseContext = new InvokeContext.ResponseContext(); + responseContext.setDuration(delay); + responseContext.setDurationUnit(TimeUnit.MILLISECONDS); + responseContext.setError(e); + invokeHandler.onError(responseContext); + downstreamSubscriber.onError(e); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerReactorTransformer.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerReactorTransformer.java new file mode 100644 index 000000000..b7fa450e1 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reactor/PolarisCircuitBreakerReactorTransformer.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.reactor; + +import java.util.function.Function; + +import com.tencent.polaris.circuitbreak.api.InvokeHandler; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Reactor Transformer for PolarisCircuitBreaker. + * + * @author seanyu 2023-02-27 + */ +public class PolarisCircuitBreakerReactorTransformer implements Function, Publisher> { + + private final InvokeHandler invokeHandler; + + public PolarisCircuitBreakerReactorTransformer(InvokeHandler invokeHandler) { + this.invokeHandler = invokeHandler; + } + + @Override + public Publisher apply(Publisher publisher) { + if (publisher instanceof Mono) { + return new PolarisCircuitBreakerMonoOperator<>((Mono) publisher, invokeHandler); + } + else if (publisher instanceof Flux) { + return new PolarisCircuitBreakerFluxOperator<>((Flux) publisher, invokeHandler); + } + else { + throw new IllegalStateException("Publisher type is not supported: " + publisher.getClass().getCanonicalName()); + } + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/util/PolarisCircuitBreakerUtils.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/util/PolarisCircuitBreakerUtils.java new file mode 100644 index 000000000..885e2917b --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/util/PolarisCircuitBreakerUtils.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.util; + +import com.tencent.cloud.common.metadata.MetadataContext; + +import org.springframework.util.Assert; + +/** + * PolarisCircuitBreakerUtils. + * + * @author seanyu 2023-02-27 + */ +public final class PolarisCircuitBreakerUtils { + + private PolarisCircuitBreakerUtils() { + + } + + /** + * + * @param id CircuitBreakerId + * Format: namespace#service#method or service#method or service , + * namespace set as default spring.cloud.polaris.namespace if absent + * @return String[]{namespace, service, method} + */ + public static String[] resolveCircuitBreakerId(String id) { + Assert.hasText(id, "A CircuitBreaker must have an id. Id could be : namespace#service#method or service#method or service"); + String[] polarisCircuitBreakerMetaData = id.split("#"); + if (polarisCircuitBreakerMetaData.length == 2) { + return new String[]{MetadataContext.LOCAL_NAMESPACE, polarisCircuitBreakerMetaData[0], polarisCircuitBreakerMetaData[1]}; + } + if (polarisCircuitBreakerMetaData.length == 3) { + return new String[]{polarisCircuitBreakerMetaData[0], polarisCircuitBreakerMetaData[1], polarisCircuitBreakerMetaData[2]}; + } + return new String[]{MetadataContext.LOCAL_NAMESPACE, id, ""}; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories index 418da24ab..a873ed563 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories @@ -1,4 +1,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration + com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration,\ + com.tencent.cloud.polaris.circuitbreaker.config.ReactivePolarisCircuitBreakerAutoConfiguration,\ + com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration,\ + com.tencent.cloud.polaris.circuitbreaker.config.GatewayPolarisCircuitBreakerAutoConfiguration org.springframework.cloud.bootstrap.BootstrapConfiguration=\ com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerAutoConfigurationTest.java new file mode 100644 index 000000000..24801dbea --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerAutoConfigurationTest.java @@ -0,0 +1,84 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker; + +import com.tencent.cloud.polaris.circuitbreaker.common.CircuitBreakerConfigModifier; +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration; +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration; +import com.tencent.cloud.polaris.circuitbreaker.config.ReactivePolarisCircuitBreakerAutoConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory; +import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; +import org.springframework.cloud.openfeign.CircuitBreakerNameResolver; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisCircuitBreakerAutoConfiguration}. + * + * @author Haotian Zhang + */ +public class PolarisCircuitBreakerAutoConfigurationTest { + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + PolarisContextAutoConfiguration.class, + RpcEnhancementAutoConfiguration.class, + LoadBalancerAutoConfiguration.class, + PolarisCircuitBreakerFeignClientAutoConfiguration.class, + PolarisCircuitBreakerAutoConfiguration.class)) + .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); + + private final ApplicationContextRunner reactiveContextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + PolarisContextAutoConfiguration.class, + RpcEnhancementAutoConfiguration.class, + LoadBalancerAutoConfiguration.class, + ReactivePolarisCircuitBreakerAutoConfiguration.class)) + .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); + + @Test + public void testDefaultInitialization() { + this.contextRunner.run(context -> { + assertThat(context).hasSingleBean(PolarisCircuitBreakerAutoConfiguration.class); + assertThat(context).hasSingleBean(CircuitBreakerFactory.class); + assertThat(context).hasSingleBean(CircuitBreakerConfigModifier.class); + assertThat(context).hasSingleBean(CircuitBreakAPI.class); + assertThat(context).hasSingleBean(CircuitBreakerNameResolver.class); + }); + } + + @Test + public void testReactiveInitialization() { + this.reactiveContextRunner.run(context -> { + assertThat(context).hasSingleBean(ReactivePolarisCircuitBreakerAutoConfiguration.class); + assertThat(context).hasSingleBean(ReactiveCircuitBreakerFactory.class); + assertThat(context).hasSingleBean(CircuitBreakerConfigModifier.class); + assertThat(context).hasSingleBean(CircuitBreakAPI.class); + }); + } + + + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfigurationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfigurationTest.java similarity index 69% rename from spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfigurationTest.java rename to spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfigurationTest.java index 9213272cd..c78784308 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfigurationTest.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfigurationTest.java @@ -15,8 +15,12 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.circuitbreaker.config; +package com.tencent.cloud.polaris.circuitbreaker; +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration; +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration; +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration; +import com.tencent.cloud.polaris.circuitbreaker.config.ReactivePolarisCircuitBreakerAutoConfiguration; import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; import org.junit.jupiter.api.Test; @@ -34,10 +38,10 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class PolarisCircuitBreakerBootstrapConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(PolarisContextAutoConfiguration.class, + .withConfiguration(AutoConfigurations.of( + PolarisContextAutoConfiguration.class, RpcEnhancementAutoConfiguration.class, LoadBalancerAutoConfiguration.class, - RpcEnhancementAutoConfiguration.class, PolarisCircuitBreakerBootstrapConfiguration.class)) .withPropertyValues("spring.cloud.polaris.enabled=true") .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); @@ -45,8 +49,9 @@ public class PolarisCircuitBreakerBootstrapConfigurationTest { @Test public void testDefaultInitialization() { this.contextRunner.run(context -> { - assertThat(context).hasSingleBean( - PolarisCircuitBreakerAutoConfiguration.CircuitBreakerConfigModifier.class); + assertThat(context).hasSingleBean(PolarisCircuitBreakerAutoConfiguration.class); + assertThat(context).hasSingleBean(PolarisCircuitBreakerFeignClientAutoConfiguration.class); + assertThat(context).hasSingleBean(ReactivePolarisCircuitBreakerAutoConfiguration.class); }); } } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerFeignIntegrationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerFeignIntegrationTest.java new file mode 100644 index 000000000..f2963b9b3 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerFeignIntegrationTest.java @@ -0,0 +1,178 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker; + + +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * @author sean yu + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = RANDOM_PORT, + classes = PolarisCircuitBreakerFeignIntegrationTest.TestConfig.class, + properties = { + "spring.cloud.gateway.enabled=false", + "feign.circuitbreaker.enabled=true", + "spring.cloud.polaris.namespace=default", + "spring.cloud.polaris.service=Test" +}) +@DirtiesContext +public class PolarisCircuitBreakerFeignIntegrationTest { + + @Autowired + private EchoService echoService; + + @Autowired + private FooService fooService; + + @Autowired + private BarService barService; + + @Autowired + private BazService bazService; + + @Test + public void contextLoads() throws Exception { + assertThat(echoService).isNotNull(); + assertThat(fooService).isNotNull(); + } + + @Test + public void testFeignClient() { + assertThat(echoService.echo("test")).isEqualTo("echo fallback"); + assertThat(fooService.echo("test")).isEqualTo("foo fallback"); + + assertThatThrownBy(() -> { + barService.bar(); + }).isInstanceOf(Exception.class); + + assertThatThrownBy(() -> { + bazService.baz(); + }).isInstanceOf(Exception.class); + + assertThat(fooService.toString()).isNotEqualTo(echoService.toString()); + assertThat(fooService.hashCode()).isNotEqualTo(echoService.hashCode()); + assertThat(echoService.equals(fooService)).isEqualTo(Boolean.FALSE); + } + + @Configuration + @EnableAutoConfiguration + @ImportAutoConfiguration({ PolarisCircuitBreakerFeignClientAutoConfiguration.class }) + @EnableFeignClients + public static class TestConfig { + + @Bean + public EchoServiceFallback echoServiceFallback() { + return new EchoServiceFallback(); + } + + @Bean + public CustomFallbackFactory customFallbackFactory() { + return new CustomFallbackFactory(); + } + + } + + @FeignClient(value = "test-service", fallback = EchoServiceFallback.class) + public interface EchoService { + + @RequestMapping(path = "echo/{str}") + String echo(@RequestParam("str") String param); + + } + + @FeignClient(value = "foo-service", fallbackFactory = CustomFallbackFactory.class) + public interface FooService { + + @RequestMapping("echo/{str}") + String echo(@RequestParam("str") String param); + + } + + @FeignClient("bar-service") + public interface BarService { + + @RequestMapping(path = "bar") + String bar(); + + } + + public interface BazService { + + @RequestMapping(path = "baz") + String baz(); + + } + + @FeignClient("baz-service") + public interface BazClient extends BazService { + + } + + public static class EchoServiceFallback implements EchoService { + + @Override + public String echo(@RequestParam("str") String param) { + return "echo fallback"; + } + + } + + public static class FooServiceFallback implements FooService { + + @Override + public String echo(@RequestParam("str") String param) { + return "foo fallback"; + } + + } + + public static class CustomFallbackFactory + implements FallbackFactory { + + private FooService fooService = new FooServiceFallback(); + + @Override + public FooService create(Throwable throwable) { + return fooService; + } + + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerGatewayIntegrationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerGatewayIntegrationTest.java new file mode 100644 index 000000000..cf0823417 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerGatewayIntegrationTest.java @@ -0,0 +1,148 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker; + + +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author sean yu + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.cloud.gateway.enabled=true", + "spring.cloud.polaris.namespace=default", + "spring.cloud.polaris.service=Test", + "spring.main.web-application-type=reactive", + "httpbin=http://localhost:${wiremock.server.port}" + }, + classes = PolarisCircuitBreakerGatewayIntegrationTest.TestApplication.class +) +@AutoConfigureWireMock(port = 0) +@ActiveProfiles("test-gateway") +@AutoConfigureWebTestClient(timeout = "10000") +public class PolarisCircuitBreakerGatewayIntegrationTest { + + @Autowired + private WebTestClient webClient; + + @Test + public void fallback() throws Exception { + + stubFor(get(urlEqualTo("/err")) + .willReturn(aResponse() + .withStatus(500) + .withBody("err") + .withFixedDelay(3000))); + + webClient + .get().uri("/err") + .header("Host", "www.circuitbreaker.com") + .exchange() + .expectStatus().isOk() + .expectBody() + .consumeWith( + response -> assertThat(response.getResponseBody()).isEqualTo("fallback".getBytes())); + } + + @Test + public void noFallback() throws Exception { + + stubFor(get(urlEqualTo("/err-no-fallback")) + .willReturn(aResponse() + .withStatus(500) + .withBody("err") + .withFixedDelay(3000))); + + webClient + .get().uri("/err-no-fallback") + .header("Host", "www.circuitbreaker-no-fallback.com") + .exchange() + .expectStatus().isEqualTo(500); + } + + + @Configuration + @EnableAutoConfiguration + public static class TestApplication { + + @Bean + public RouteLocator myRoutes(RouteLocatorBuilder builder) { + String httpUri = "http://httpbin.org:80"; + Set codeSets = new HashSet<>(); + codeSets.add("4**"); + codeSets.add("5**"); + return builder.routes() + .route(p -> p + .host("*.circuitbreaker.com") + .filters(f -> f + .circuitBreaker(config -> config + .setStatusCodes(codeSets) + .setFallbackUri("forward:/fallback") + )) + .uri(httpUri)) + .route(p -> p + .host("*.circuitbreaker-no-fallback.com") + .filters(f -> f + .circuitBreaker(config -> config + .setStatusCodes(codeSets) + )) + .uri(httpUri)) + .build(); + } + + @RestController + static class Controller { + @RequestMapping("/fallback") + public Mono fallback() { + return Mono.just("fallback"); + } + } + + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerMockServerTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerMockServerTest.java new file mode 100644 index 000000000..e8315c2e7 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerMockServerTest.java @@ -0,0 +1,149 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import com.google.protobuf.util.JsonFormat; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory; +import com.tencent.polaris.client.util.Utils; +import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto; +import com.tencent.polaris.test.common.TestUtils; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.SERVICE_CIRCUIT_BREAKER; +import static com.tencent.polaris.test.common.TestUtils.SERVER_ADDRESS_ENV; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisCircuitBreaker} and {@link ReactivePolarisCircuitBreaker} using real mock server. + * + * @author sean yu + */ +@ExtendWith(MockitoExtension.class) +public class PolarisCircuitBreakerMockServerTest { + + private static MockedStatic mockedApplicationContextAwareUtils; + private static NamingServer namingServer; + + @BeforeAll + public static void beforeAll() throws IOException { + mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.namespace")) + .thenReturn(NAMESPACE_TEST); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.service")) + .thenReturn(SERVICE_CIRCUIT_BREAKER); + + try { + namingServer = NamingServer.startNamingServer(-1); + System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort())); + } + catch (IOException e) { + + } + ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, SERVICE_CIRCUIT_BREAKER); + + CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder(); + InputStream inputStream = PolarisCircuitBreakerMockServerTest.class.getClassLoader().getResourceAsStream("circuitBreakerRule.json"); + String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("")); + JsonFormat.parser().ignoringUnknownFields().merge(json, circuitBreakerRuleBuilder); + CircuitBreakerProto.CircuitBreakerRule circuitBreakerRule = circuitBreakerRuleBuilder.build(); + CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder().addRules(circuitBreakerRule).build(); + namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker); + } + + @AfterAll + public static void afterAll() { + if (null != namingServer) { + namingServer.terminate(); + } + } + + @Test + public void testCircuitBreaker() { + Configuration configuration = TestUtils.configWithEnvAddress(); + CircuitBreakAPI circuitBreakAPI = CircuitBreakAPIFactory.createCircuitBreakAPIByConfig(configuration); + + PolarisCircuitBreakerFactory polarisCircuitBreakerFactory = new PolarisCircuitBreakerFactory(circuitBreakAPI); + CircuitBreaker cb = polarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER); + + // trigger fallback for 5 times + List resList = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + int finalI = i; + String res = cb.run(() -> { + if (finalI % 2 == 1) { + throw new IllegalArgumentException("invoke failed"); + } + else { + return "invoke success"; + } + }, t -> "fallback"); + resList.add(res); + Utils.sleepUninterrupted(1000); + } + assertThat(resList).isEqualTo(Arrays.asList("invoke success", "fallback", "fallback", "fallback", "fallback")); + + // always fallback + ReactivePolarisCircuitBreakerFactory reactivePolarisCircuitBreakerFactory = new ReactivePolarisCircuitBreakerFactory(circuitBreakAPI); + ReactiveCircuitBreaker rcb = reactivePolarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER); + + assertThat(Mono.just("foobar").transform(it -> rcb.run(it, t -> Mono.just("fallback"))) + .block()).isEqualTo("fallback"); + + assertThat(Mono.error(new RuntimeException("boom")).transform(it -> rcb.run(it, t -> Mono.just("fallback"))) + .block()).isEqualTo("fallback"); + + assertThat(Flux.just("foobar", "hello world").transform(it -> rcb.run(it, t -> Flux.just("fallback", "fallback"))) + .collectList().block()) + .isEqualTo(Arrays.asList("fallback", "fallback")); + + assertThat(Flux.error(new RuntimeException("boom")).transform(it -> rcb.run(it, t -> Flux.just("fallback"))) + .collectList().block()) + .isEqualTo(Collections.singletonList("fallback")); + + + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerTest.java new file mode 100644 index 000000000..bf35fa8f9 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerTest.java @@ -0,0 +1,101 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker; + + +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder; +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration; +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.SERVICE_CIRCUIT_BREAKER; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisCircuitBreaker}. + * + * @author sean yu + */ +@ExtendWith(MockitoExtension.class) +public class PolarisCircuitBreakerTest { + + private static ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + PolarisContextAutoConfiguration.class, + RpcEnhancementAutoConfiguration.class, + LoadBalancerAutoConfiguration.class, + PolarisCircuitBreakerFeignClientAutoConfiguration.class, + PolarisCircuitBreakerAutoConfiguration.class)) + .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); + + private static MockedStatic mockedApplicationContextAwareUtils; + + @BeforeAll + public static void beforeClass() { + mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.namespace")) + .thenReturn(NAMESPACE_TEST); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.service")) + .thenReturn(SERVICE_CIRCUIT_BREAKER); + } + + @AfterAll + public static void afterAll() { + mockedApplicationContextAwareUtils.close(); + } + + @Test + public void run() { + this.contextRunner.run(context -> { + + PolarisCircuitBreakerFactory polarisCircuitBreakerFactory = context.getBean(PolarisCircuitBreakerFactory.class); + CircuitBreaker cb = polarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER); + + PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration configuration = + polarisCircuitBreakerFactory.configBuilder(SERVICE_CIRCUIT_BREAKER).build(); + + polarisCircuitBreakerFactory.configureDefault(id -> configuration); + + assertThat(cb.run(() -> "foobar")).isEqualTo("foobar"); + + assertThat((String) cb.run(() -> { + throw new RuntimeException("boom"); + }, t -> "fallback")).isEqualTo("fallback"); + + }); + } + + + + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerTest.java new file mode 100644 index 000000000..f06502c87 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerTest.java @@ -0,0 +1,103 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker; + +import java.util.Arrays; +import java.util.Collections; + +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder; +import com.tencent.cloud.polaris.circuitbreaker.config.ReactivePolarisCircuitBreakerAutoConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker; +import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.SERVICE_CIRCUIT_BREAKER; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link ReactivePolarisCircuitBreaker}. + * + * @author sean yu + */ +@ExtendWith(MockitoExtension.class) +public class ReactivePolarisCircuitBreakerTest { + + private final ApplicationContextRunner reactiveContextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + PolarisContextAutoConfiguration.class, + RpcEnhancementAutoConfiguration.class, + LoadBalancerAutoConfiguration.class, + ReactivePolarisCircuitBreakerAutoConfiguration.class)) + .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); + + private static MockedStatic mockedApplicationContextAwareUtils; + + @BeforeAll + public static void beforeClass() { + mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.namespace")) + .thenReturn(NAMESPACE_TEST); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.service")) + .thenReturn(SERVICE_CIRCUIT_BREAKER); + } + + @AfterAll + public static void afterAll() { + mockedApplicationContextAwareUtils.close(); + } + + @Test + public void run() { + this.reactiveContextRunner.run(context -> { + ReactivePolarisCircuitBreakerFactory polarisCircuitBreakerFactory = context.getBean(ReactivePolarisCircuitBreakerFactory.class); + ReactiveCircuitBreaker cb = polarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER); + + PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration configuration = + polarisCircuitBreakerFactory.configBuilder(SERVICE_CIRCUIT_BREAKER).build(); + + polarisCircuitBreakerFactory.configureDefault(id -> configuration); + + assertThat(Mono.just("foobar").transform(cb::run).block()).isEqualTo("foobar"); + + assertThat(Mono.error(new RuntimeException("boom")).transform(it -> cb.run(it, t -> Mono.just("fallback"))) + .block()).isEqualTo("fallback"); + + assertThat(Flux.just("foobar", "hello world").transform(cb::run).collectList().block()) + .isEqualTo(Arrays.asList("foobar", "hello world")); + + assertThat(Flux.error(new RuntimeException("boom")).transform(it -> cb.run(it, t -> Flux.just("fallback"))) + .collectList().block()).isEqualTo(Collections.singletonList("fallback")); + }); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerAutoConfigurationTest.java deleted file mode 100644 index f1dcf17a7..000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerAutoConfigurationTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.polaris.circuitbreaker.config; - -import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; -import org.junit.jupiter.api.Test; - -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Test for {@link PolarisCircuitBreakerAutoConfiguration}. - * - * @author Haotian Zhang - */ -public class PolarisCircuitBreakerAutoConfigurationTest { - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of( - PolarisContextAutoConfiguration.class, - RpcEnhancementAutoConfiguration.class, - LoadBalancerAutoConfiguration.class, - RpcEnhancementAutoConfiguration.class, - PolarisCircuitBreakerAutoConfiguration.class)) - .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); - - @Test - public void testDefaultInitialization() { - this.contextRunner.run(context -> { - assertThat(context).hasSingleBean( - PolarisCircuitBreakerAutoConfiguration.CircuitBreakerConfigModifier.class); - }); - } -} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/resources/application-test-gateway.yml b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/resources/application-test-gateway.yml new file mode 100644 index 000000000..6094b46e5 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/resources/application-test-gateway.yml @@ -0,0 +1,36 @@ +spring: + application: + name: GatewayScgService + cloud: + tencent: + plugin: + scg: + staining: + enabled: true + rule-staining: + enabled: true + router: + feature-env: + enabled: true + polaris: + address: grpc://127.0.0.1:8091 + namespace: default + enabled: true + gateway: + routes: + - id: cb-test + uri: http://localhost:${server.port}/hello/1 + predicates: + - Path=/cb-test/** + filters: + - name: CircuitBreaker + args: + statusCodes: 5**,4**,3**,2**,1**,500,400 + fallbackUri: forward:/polaris-fallback +logging: + level: + root: info + com.tencent.polaris.discovery.client.flow.RegisterFlow: off + com.tencent.polaris.plugins.registry.memory.CacheObject: off + com.tencent.cloud.polaris.circuitbreaker: debug + diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/resources/circuitBreakerRule.json b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/resources/circuitBreakerRule.json new file mode 100644 index 000000000..0a1e97f2a --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/resources/circuitBreakerRule.json @@ -0,0 +1,59 @@ +{ + "@type": "type.googleapis.com/v1.CircuitBreakerRule", + "id": "5f1601f01823474d9be39c0bbb26ab87", + "name": "test", + "namespace": "TestCircuitBreakerRule", + "enable": true, + "revision": "10b120c08706429f8fdc3fb44a53224b", + "ctime": "1754-08-31 06:49:24", + "mtime": "2023-02-21 17:35:31", + "etime": "", + "description": "", + "level": "SERVICE", + "ruleMatcher": { + "source": { + "service": "*", + "namespace": "*" + }, + "destination": { + "service": "*", + "namespace": "*", + "method": null + } + }, + "errorConditions": [ + { + "inputType": "RET_CODE", + "condition": { + "type": "NOT_EQUALS", + "value": "200", + "valueType": "TEXT" + } + } + ], + "triggerCondition": [ + { + "triggerType": "CONSECUTIVE_ERROR", + "errorCount": 1, + "errorPercent": 1, + "interval": 5, + "minimumRequest": 5 + } + ], + "maxEjectionPercent": 0, + "recoverCondition": { + "sleepWindow": 60, + "consecutiveSuccess": 3 + }, + "faultDetectConfig": { + "enable": true + }, + "fallbackConfig": { + "enable": false, + "response": { + "code": 0, + "headers": [], + "body": "" + } + } +} \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service/pom.xml similarity index 92% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/pom.xml rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service/pom.xml index 06bb41d0f..d26bbbf42 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/pom.xml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service/pom.xml @@ -10,8 +10,8 @@ 4.0.0 - polaris-circuitbreaker-example-b - Polaris Circuit Breaker Example B + polaris-circuitbreaker-callee-service + Polaris Circuit Breaker Callee Example diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceB.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceB.java similarity index 100% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceB.java rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceB.java diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceBController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceBController.java similarity index 100% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceBController.java rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceBController.java diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service/src/main/resources/bootstrap.yml similarity index 81% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/resources/bootstrap.yml rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service/src/main/resources/bootstrap.yml index d6945b5b4..5fa99c252 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service/src/main/resources/bootstrap.yml @@ -2,7 +2,7 @@ server: port: 48081 spring: application: - name: polaris-circuitbreaker-example-b + name: polaris-circuitbreaker-callee-service cloud: polaris: address: grpc://183.47.111.80:8091 diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service2/pom.xml similarity index 92% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/pom.xml rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service2/pom.xml index 2c941eced..204331a2a 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/pom.xml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service2/pom.xml @@ -10,7 +10,8 @@ 4.0.0 - polaris-circuitbreaker-example-b2 + polaris-circuitbreaker-callee-service2 + Polaris Circuit Breaker Callee Example 2 diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceB2.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceB2.java similarity index 100% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceB2.java rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceB2.java diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceBController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceBController.java similarity index 100% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceBController.java rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceBController.java diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service2/src/main/resources/bootstrap.yml similarity index 81% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/resources/bootstrap.yml rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service2/src/main/resources/bootstrap.yml index 5ef89145c..54cb21355 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-callee-service2/src/main/resources/bootstrap.yml @@ -2,7 +2,7 @@ server: port: 48082 spring: application: - name: polaris-circuitbreaker-example-b + name: polaris-circuitbreaker-callee-service cloud: polaris: address: grpc://183.47.111.80:8091 diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/resources/bootstrap.yml deleted file mode 100644 index de8c607b2..000000000 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/resources/bootstrap.yml +++ /dev/null @@ -1,38 +0,0 @@ -server: - port: 48080 -spring: - application: - name: polaris-circuitbreaker-example-a - cloud: - polaris: - address: grpc://183.47.111.80:8091 - namespace: default - enabled: true - circuitbreaker: - enabled: true - stat: - enabled: true - port: 28081 - loadbalancer: - configurations: polaris - tencent: - rpc-enhancement: - enabled: true - reporter: - ignore-internal-server-error: true - series: server_error - statuses: gateway_timeout, bad_gateway, service_unavailable - -feign: - circuitbreaker: - enabled: true - compression: - request: - enabled: false - mime-types: text/xml,application/xml,application/json - min-request-size: 2048 - response: - enabled: false - -serivceB: - url: http://localhost:48081 diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/pom.xml similarity index 88% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/pom.xml rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/pom.xml index 6811c6230..6f7382e7b 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/pom.xml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/pom.xml @@ -10,8 +10,8 @@ 4.0.0 - polaris-circuitbreaker-example-a - Polaris Circuit Breaker Example A + polaris-circuitbreaker-feign-example + Polaris Circuit Breaker Feign Example @@ -24,11 +24,6 @@ spring-cloud-starter-tencent-polaris-discovery - - com.tencent.cloud - spring-cloud-starter-tencent-polaris-circuitbreaker - - org.springframework.cloud spring-cloud-starter-openfeign @@ -40,8 +35,8 @@ - org.springframework.cloud - spring-cloud-circuitbreaker-spring-retry + com.tencent.cloud + spring-cloud-starter-tencent-polaris-circuitbreaker diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ProviderB.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderB.java similarity index 81% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ProviderB.java rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderB.java index f0c05217a..5e1160877 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ProviderB.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderB.java @@ -15,17 +15,19 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.circuitbreaker.example; +package com.tencent.cloud.polaris.circuitbreaker.feign.example; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.context.annotation.Primary; import org.springframework.web.bind.annotation.GetMapping; /** * Circuit breaker example callee provider. * - * @author Haotian Zhang + * @author sean yu */ -@FeignClient(name = "polaris-circuitbreaker-example-b", fallback = ProviderBFallback.class) +@Primary +@FeignClient(name = "polaris-circuitbreaker-callee-service", fallback = ProviderBFallback.class) public interface ProviderB { /** diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ProviderBFallback.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderBFallback.java similarity index 87% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ProviderBFallback.java rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderBFallback.java index bf47d49dd..e62cbf24c 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ProviderBFallback.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ProviderBFallback.java @@ -15,20 +15,20 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.circuitbreaker.example; +package com.tencent.cloud.polaris.circuitbreaker.feign.example; import org.springframework.stereotype.Component; /** * Circuit breaker example callee fallback. * - * @author Haotian Zhang + * @author sean yu */ @Component public class ProviderBFallback implements ProviderB { @Override public String info() { - return "trigger the refuse for service b"; + return "fallback: trigger the refuse for service b"; } } diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceAController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ServiceAController.java similarity index 64% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceAController.java rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ServiceAController.java index 79ba2c0ef..5c7108716 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceAController.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ServiceAController.java @@ -15,19 +15,18 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.circuitbreaker.example; +package com.tencent.cloud.polaris.circuitbreaker.feign.example; + import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.RestTemplate; /** * Circuit breaker example caller controller. * - * @author Haotian Zhang + * @author sean yu */ @RestController @RequestMapping("/example/service/a") @@ -36,9 +35,6 @@ public class ServiceAController { @Autowired private ProviderB polarisServiceB; - @Autowired - private RestTemplate restTemplate; - /** * Get info of Service B by Feign. * @return info of Service B @@ -48,20 +44,4 @@ public class ServiceAController { return polarisServiceB.info(); } - @GetMapping("/getBServiceInfoByRestTemplate") - public String getBServiceInfoByRestTemplate() { - return restTemplate.getForObject("http://polaris-circuitbreaker-example-b/example/service/b/info", String.class); - } - - /** - * Get info of Service B by RestTemplate. - * @return info of Service B - */ - @GetMapping("/testRest") - public String testRest() { - ResponseEntity entity = restTemplate.getForEntity( - "http://polaris-circuitbreaker-example-b/example/service/b/info", - String.class); - return entity.getBody(); - } } diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ServiceAFeign.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ServiceAFeign.java new file mode 100644 index 000000000..f6a012bb0 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/example/ServiceAFeign.java @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.polaris.circuitbreaker.feign.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * Circuit breaker example caller application. + * + * @author sean yu + */ +@SpringBootApplication +@EnableFeignClients +public class ServiceAFeign { + + public static void main(String[] args) { + SpringApplication.run(ServiceAFeign.class, args); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..3378ae635 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/resources/bootstrap.yml @@ -0,0 +1,39 @@ +server: + port: 48080 +spring: + application: + name: polaris-circuitbreaker-feign-example + cloud: + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true + loadbalancer: + enabled: true + circuitbreaker: + enabled: true +# stat: +# enabled: true +# port: 28081 +# tencent: +# rpc-enhancement: +# enabled: true +# reporter: +# ignore-internal-server-error: true +# series: server_error +# statuses: gateway_timeout, bad_gateway, service_unavailable + +feign: + circuitbreaker: + enabled: true +# compression: +# request: +# enabled: false +# mime-types: text/xml,application/xml,application/json +# min-request-size: 2048 + +logging: + level: + root: info + com.tencent.cloud: debug + diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/pom.xml new file mode 100644 index 000000000..9064f5b88 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/pom.xml @@ -0,0 +1,87 @@ + + + + polaris-circuitbreaker-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + polaris-circuitbreaker-gateway-example + Polaris Circuit Breaker Gateway Example + + + + spring-cloud-starter-tencent-polaris-discovery + com.tencent.cloud + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-circuitbreaker + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-router + + + + com.tencent.cloud + spring-cloud-tencent-gateway-plugin + + + + com.tencent.cloud + spring-cloud-starter-tencent-metadata-transfer + + + + com.tencent.cloud + spring-cloud-tencent-featureenv-plugin + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + + \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/example/FallbackController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/example/FallbackController.java new file mode 100644 index 000000000..c0d964b56 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/example/FallbackController.java @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.gateway.example; + +import reactor.core.publisher.Mono; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * FallbackController. + * + * sean yu + */ +@RestController +public class FallbackController { + + @GetMapping("/polaris-fallback") + Mono getFallback() { + return Mono.just("fallback: trigger the refuse for service b"); + } +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/example/GatewayScgApplication.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/example/GatewayScgApplication.java new file mode 100644 index 000000000..ea042b2f9 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/example/GatewayScgApplication.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.gateway.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * SCG application. + * + * @author sean yu + */ +@SpringBootApplication +public class GatewayScgApplication { + + public static void main(String[] args) { + SpringApplication.run(GatewayScgApplication.class, args); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..399af3ca1 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml @@ -0,0 +1,57 @@ +server: + session-timeout: 1800 + port: 48080 +spring: + application: + name: GatewayScgService + cloud: + tencent: + plugin: + scg: + staining: + enabled: true + rule-staining: + enabled: true + router: + feature-env: + enabled: true + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true + gateway: + discovery: + locator: + enabled: true + 'predicates[0]': + name: Path + args: + patterns: '''/'' + serviceId + ''/**''' + 'filters[0]': + name: RewritePath + args: + regexp: '''/'' + serviceId + ''/(?.*)''' + replacement: '''/$\{remaining}''' + 'filters[1]': + name: CircuitBreaker + args: + statusCodes: '''4**,502''' + fallbackUri: '''forward:/polaris-fallback''' +# routes: +# - id: polaris-circuitbreaker-callee-service +# uri: lb://polaris-circuitbreaker-callee-service +# predicates: +# - Path=/polaris-circuitbreaker-callee-service/** +# filters: +# - StripPrefix=1 +# - name: CircuitBreaker +# args: +# statusCodes: 502 +# fallbackUri: forward:/polaris-fallback +logging: + level: + root: info + com.tencent.polaris.discovery.client.flow.RegisterFlow: off + com.tencent.polaris.plugins.registry.memory.CacheObject: off + com.tencent.cloud.polaris.circuitbreaker: debug + diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/pom.xml new file mode 100644 index 000000000..f86372777 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/pom.xml @@ -0,0 +1,72 @@ + + + + polaris-circuitbreaker-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + polaris-circuitbreaker-resttemplate-example + Polaris Circuit Breaker RestTemplate Example + + + + org.springframework.boot + spring-boot-starter-web + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-circuitbreaker + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + + \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/ServiceAController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/ServiceAController.java new file mode 100644 index 000000000..d76816ed1 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/ServiceAController.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.resttemplate.example; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +/** + * Circuit breaker example caller controller. + * + * @author sean yu + */ +@RestController +@RequestMapping("/example/service/a") +public class ServiceAController { + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private CircuitBreakerFactory circuitBreakerFactory; + + @GetMapping("/getBServiceInfo") + public String getBServiceInfo() { + 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" + ); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/ServiceAResTemplate.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/ServiceAResTemplate.java new file mode 100644 index 000000000..21e2f4778 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/example/ServiceAResTemplate.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.polaris.circuitbreaker.resttemplate.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriBuilderFactory; + +/** + * Circuit breaker example caller application. + * + * @author sean yu + */ +@SpringBootApplication +public class ServiceAResTemplate { + + public static void main(String[] args) { + SpringApplication.run(ServiceAResTemplate.class, args); + } + + @Bean + @LoadBalanced + public RestTemplate restTemplate() { + DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://polaris-circuitbreaker-callee-service"); + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setUriTemplateHandler(uriBuilderFactory); + return restTemplate; + } + +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..16fa8bd3b --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/resources/bootstrap.yml @@ -0,0 +1,30 @@ +server: + port: 48080 +spring: + application: + name: polaris-circuitbreaker-resttemplate-example + cloud: + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true + loadbalancer: + enabled: true + circuitbreaker: + enabled: true +# stat: +# enabled: true +# port: 28081 +# tencent: +# rpc-enhancement: +# enabled: true +# reporter: +# ignore-internal-server-error: true +# series: server_error +# statuses: gateway_timeout, bad_gateway, service_unavailable + +logging: + level: + root: info + com.tencent.cloud: debug + diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/pom.xml new file mode 100644 index 000000000..3305c2c58 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/pom.xml @@ -0,0 +1,72 @@ + + + + polaris-circuitbreaker-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + polaris-circuitbreaker-webclient-example + Polaris Circuit Breaker WebClient Example + + + + org.springframework.boot + spring-boot-starter-webflux + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-circuitbreaker + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + + \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/webclient/example/ServiceAController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/webclient/example/ServiceAController.java new file mode 100644 index 000000000..72f3c80d5 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/webclient/example/ServiceAController.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.webclient.example; + +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * Circuit breaker example caller controller. + * + * @author sean yu + */ +@RestController +@RequestMapping("/example/service/a") +public class ServiceAController { + + @Autowired + private ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory; + + @Autowired + private WebClient.Builder webClientBuilder; + + @GetMapping("/getBServiceInfo") + public Mono getBServiceInfo() { + return webClientBuilder + .build() + .get() + .uri("/example/service/b/info") + .retrieve() + .bodyToMono(String.class) + .transform(it -> + reactiveCircuitBreakerFactory + .create("polaris-circuitbreaker-callee-service#/example/service/b/info") + .run(it, throwable -> Mono.just("fallback: trigger the refuse for service b")) + ); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceA.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/webclient/example/ServiceAWebClient.java similarity index 75% rename from spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceA.java rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/webclient/example/ServiceAWebClient.java index 3db7df8f3..8104ae873 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceA.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/java/com/tencent/cloud/polaris/circuitbreaker/webclient/example/ServiceAWebClient.java @@ -16,31 +16,30 @@ * */ -package com.tencent.cloud.polaris.circuitbreaker.example; +package com.tencent.cloud.polaris.circuitbreaker.webclient.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.client.WebClient; /** * Circuit breaker example caller application. * - * @author Haotian Zhang + * @author sean yu */ @SpringBootApplication -@EnableFeignClients -public class ServiceA { +public class ServiceAWebClient { public static void main(String[] args) { - SpringApplication.run(ServiceA.class, args); + SpringApplication.run(ServiceAWebClient.class, args); } - @Bean @LoadBalanced - public RestTemplate restTemplate() { - return new RestTemplate(); + @Bean + WebClient.Builder webClientBuilder() { + return WebClient.builder() + .baseUrl("http://polaris-circuitbreaker-callee-service"); } } diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..f84dffdea --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/resources/bootstrap.yml @@ -0,0 +1,30 @@ +server: + port: 48080 +spring: + application: + name: polaris-circuitbreaker-webclient-example + cloud: + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true + loadbalancer: + enabled: true + circuitbreaker: + enabled: true +# stat: +# enabled: true +# port: 28081 +# tencent: +# rpc-enhancement: +# enabled: true +# reporter: +# ignore-internal-server-error: true +# series: server_error +# statuses: gateway_timeout, bad_gateway, service_unavailable + +logging: + level: + root: info + com.tencent.cloud: debug + diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/pom.xml index 6b5daa263..8e53f8d77 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/pom.xml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/pom.xml @@ -15,8 +15,11 @@ pom - polaris-circuitbreaker-example-a - polaris-circuitbreaker-example-b - polaris-circuitbreaker-example-b2 + polaris-circuitbreaker-feign-example + polaris-circuitbreaker-gateway-example + polaris-circuitbreaker-resttemplate-example + polaris-circuitbreaker-webclient-example + polaris-circuitbreaker-callee-service + polaris-circuitbreaker-callee-service2