From 55122f0886ca1e5230aafb3ca30d3f3733628255 Mon Sep 17 00:00:00 2001 From: shedfreewu <49236872+shedfreewu@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:27:16 +0800 Subject: [PATCH] 2.0.0.0 serivce (#1471) 1. support change callee server name by user interceptor and support circuit breaker 2. support multi service registry --------- Co-authored-by: shedfreewu --- CHANGELOG.md | 3 +- ...sferMedataRestTemplateInterceptorTest.java | 12 +- .../ReactivePolarisCircuitBreaker.java | 92 ------ .../ReactivePolarisCircuitBreakerFactory.java | 100 ------ .../common/PolarisResultToErrorCode.java | 6 +- ...olarisCircuitBreakerAutoConfiguration.java | 26 +- ...olarisCircuitBreakerAutoConfiguration.java | 34 +- ...sCircuitBreakerBootstrapConfiguration.java | 1 - ...olarisCircuitBreakerAutoConfiguration.java | 86 ----- .../feign/PolarisFeignCircuitBreaker.java | 22 +- ...sFeignCircuitBreakerInvocationHandler.java | 110 ++----- .../PolarisCircuitBreakerFilterFactory.java | 280 ---------------- .../reporter/CircuitBreakerPlugin.java | 117 +++++++ .../ExceptionCircuitBreakerReporter.java | 45 +++ .../SuccessCircuitBreakerReporter.java | 46 ++- ...tBreakerRestTemplateBeanPostProcessor.java | 125 -------- ...CircuitBreakerRestTemplateInterceptor.java | 120 ------- .../PolarisCircuitBreakerPostZuulFilter.java | 152 --------- .../zuul/PolarisCircuitBreakerZuulFilter.java | 156 --------- .../PolarisFeignCircuitBreakerTargeter.java | 25 +- ...rcuitBreakerTargeterAutoConfiguration.java | 5 +- .../main/resources/META-INF/spring.factories | 1 - .../PolarisCircuitBreakerMockServerTest.java | 26 +- .../ReactivePolarisCircuitBreakerTest.java | 120 ------- ...isCircuitBreakerAutoConfigurationTest.java | 18 -- ...cuitBreakerBootstrapConfigurationTest.java | 1 - ...ignIntegrationDisableFeignHystrixTest.java | 57 ---- ...risCircuitBreakerFeignIntegrationTest.java | 228 ------------- ...olarisFeignCircuitBreakerTargeterTest.java | 6 +- ...sCircuitBreakerGatewayIntegrationTest.java | 218 ------------- .../reporter/CircuitBreakerPluginTest.java | 171 ++++++++++ .../ExceptionCircuitBreakerReporterTest.java | 68 ++++ .../SuccessCircuitBreakerReporterTest.java | 81 ++++- ...uitBreakerRestTemplateIntegrationTest.java | 303 ------------------ .../discovery/PolarisServiceDiscovery.java | 2 +- .../polaris/registry/PolarisRegistration.java | 6 +- .../registry/PolarisServiceRegistry.java | 2 + .../main/resources/META-INF/spring.factories | 1 - .../lossless/LosslessRegistryAspectTest.java | 144 +++++++++ .../PolarisLoadBalancerCompositeRule.java | 3 +- .../polaris/router/PolarisRouterContext.java | 2 +- ...dBalancerInterceptorBeanPostProcessor.java | 17 +- ...alancerCompositeRuleBeanPostProcessor.java | 13 +- ...dBalancerInterceptorBeanPostProcessor.java | 16 +- .../config/RouterAutoConfiguration.java | 19 +- .../PolarisLoadBalancerInterceptor.java | 25 +- .../PolarisRetryLoadBalancerInterceptor.java | 22 +- .../zuul/PolarisRibbonRoutingFilter.java | 13 +- .../PolarisLoadBalancerInterceptorTest.java | 10 +- .../zuul/PolarisRibbonRoutingFilterTest.java | 18 +- .../common/constant/ContextConstant.java | 31 +- .../common/metadata/MetadataContext.java | 4 + ...acosDiscoveryAdapterAutoConfiguration.java | 4 - .../config/LosslessAutoConfiguration.java | 37 ++- .../CircuitBreakerStatusCodeException.java | 29 ++ .../loadbalancer/LoadBalancerUtils.java | 10 + .../loadbalancer/PolarisLoadBalancer.java | 22 +- .../PolarisLoadBalancerAutoConfiguration.java | 48 --- .../loadbalancer/PolarisLoadBalancerTest.java | 4 +- .../RpcEnhancementAutoConfiguration.java | 25 +- .../feign/EnhancedFeignClient.java | 49 ++- .../plugin/DefaultEnhancedPluginRunner.java | 26 ++ .../plugin/EnhancedRequestContext.java | 17 + .../plugin/PolarisEnhancedPluginUtils.java | 9 +- ... EnhancedRestTemplateWrapInterceptor.java} | 50 ++- .../scg/EnhancedGatewayGlobalFilter.java | 40 ++- ...ter.java => EnhancedZuulPluginRunner.java} | 37 +-- .../RpcEnhancementAutoConfigurationTest.java | 2 - .../feign/EnhancedFeignClientTest.java | 77 ++++- .../plugin/EnhancedPluginContextTest.java | 3 +- .../PolarisEnhancedPluginUtilsTest.java | 12 +- .../EnhancedRestTemplateInterceptorTest.java | 126 -------- 72 files changed, 1240 insertions(+), 2596 deletions(-) delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreaker.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerFactory.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/ReactivePolarisCircuitBreakerAutoConfiguration.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/PolarisCircuitBreakerFilterFactory.java create mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPlugin.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateBeanPostProcessor.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateInterceptor.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/zuul/PolarisCircuitBreakerPostZuulFilter.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/zuul/PolarisCircuitBreakerZuulFilter.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerTest.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerFeignIntegrationDisableFeignHystrixTest.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerFeignIntegrationTest.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/gateway/PolarisCircuitBreakerGatewayIntegrationTest.java create mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPluginTest.java delete mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateIntegrationTest.java create mode 100644 spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/CircuitBreakerStatusCodeException.java rename spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/{EnhancedRestTemplateInterceptor.java => EnhancedRestTemplateWrapInterceptor.java} (61%) rename spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/zuul/{EnhancedPreZuulFilter.java => EnhancedZuulPluginRunner.java} (74%) delete mode 100644 spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptorTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 41b284d6a3..f733576ced 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,4 +18,5 @@ - [fix: memory not released while using wildcard api call with circuitbreaker enabled](https://github.com/Tencent/spring-cloud-tencent/pull/1335) - [feat: support 2.0.0](https://github.com/Tencent/spring-cloud-tencent/pull/1458) - [feat: support 2.0.0 config](https://github.com/Tencent/spring-cloud-tencent/pull/1463) -- [feat:upgrade trace plugin.](https://github.com/Tencent/spring-cloud-tencent/pull/1467) \ No newline at end of file +- [feat:upgrade trace plugin.](https://github.com/Tencent/spring-cloud-tencent/pull/1467) +- [feat:upgrade 2.0.0 service.](https://github.com/Tencent/spring-cloud-tencent/pull/1471) \ No newline at end of file diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java index 67211fb9e6..a078cf63b9 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java @@ -18,13 +18,10 @@ package com.tencent.cloud.metadata.core; import java.net.URI; -import java.util.Arrays; import java.util.Map; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; -import com.tencent.cloud.rpc.enhancement.plugin.DefaultEnhancedPluginRunner; -import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateInterceptor; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -79,15 +76,10 @@ protected static class TestApplication { @Bean public RestTemplate restTemplate() { - - EncodeTransferMedataRestTemplateEnhancedPlugin plugin = new EncodeTransferMedataRestTemplateEnhancedPlugin(); - EnhancedRestTemplateInterceptor interceptor = new EnhancedRestTemplateInterceptor( - new DefaultEnhancedPluginRunner(Arrays.asList(plugin), new MockRegistration(), null)); - RestTemplate template = new RestTemplate(); - template.setInterceptors(Arrays.asList(interceptor)); - return template; + return new RestTemplate(); } + @RequestMapping("/test") public String test() { return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"); 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 deleted file mode 100644 index e67c58506d..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreaker.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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.cloud.polaris.circuitbreaker.util.PolarisCircuitBreakerUtils; -import com.tencent.polaris.api.core.ConsumerAPI; -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 com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; -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; - - private final ConsumerAPI consumerAPI; - - private final PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf; - - public ReactivePolarisCircuitBreaker(PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf, - ConsumerAPI consumerAPI, - CircuitBreakAPI circuitBreakAPI) { - InvokeContext.RequestContext requestContext = new FunctionalDecoratorRequest( - new ServiceKey(conf.getNamespace(), conf.getService()), conf.getProtocol(), conf.getMethod(), conf.getPath()); - requestContext.setSourceService(new ServiceKey(conf.getSourceNamespace(), conf.getSourceService())); - requestContext.setResultToErrorCode(new PolarisResultToErrorCode()); - this.consumerAPI = consumerAPI; - this.conf = conf; - 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(throwable -> { - if (throwable instanceof CallAbortedException) { - PolarisCircuitBreakerUtils.reportStatus(consumerAPI, conf, (CallAbortedException) throwable); - } - return fallback.apply(throwable); - }); - } - return toReturn; - } - - @Override - public Flux run(Flux toRun, Function> fallback) { - Flux toReturn = toRun.transform(new PolarisCircuitBreakerReactorTransformer<>(invokeHandler)); - if (fallback != null) { - toReturn = toReturn.onErrorResume(throwable -> { - if (throwable instanceof CallAbortedException) { - PolarisCircuitBreakerUtils.reportStatus(consumerAPI, conf, (CallAbortedException) throwable); - } - return fallback.apply(throwable); - }); - } - 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 deleted file mode 100644 index 56c61f9bfb..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerFactory.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - -import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder; -import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerProperties; -import com.tencent.cloud.polaris.circuitbreaker.util.PolarisCircuitBreakerUtils; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.utils.ThreadPoolUtils; -import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; -import com.tencent.polaris.client.util.NamedThreadFactory; - -import org.springframework.beans.factory.DisposableBean; -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 implements DisposableBean { - - private final CircuitBreakAPI circuitBreakAPI; - - private final ConsumerAPI consumerAPI; - - private final ScheduledExecutorService cleanupService = Executors.newSingleThreadScheduledExecutor( - new NamedThreadFactory("sct-reactive-circuitbreaker-cleanup", true)); - - private Function defaultConfiguration = - id -> { - String[] metadata = PolarisCircuitBreakerUtils.resolveCircuitBreakerId(id); - return new PolarisCircuitBreakerConfigBuilder() - .namespace(metadata[0]) - .service(metadata[1]) - .path(metadata[2]) - .protocol(metadata[3]) - .method(metadata[4]) - .build(); - }; - - public ReactivePolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI, - PolarisCircuitBreakerProperties polarisCircuitBreakerProperties) { - this.circuitBreakAPI = circuitBreakAPI; - this.consumerAPI = consumerAPI; - cleanupService.scheduleWithFixedDelay( - () -> { - getConfigurations().clear(); - }, - polarisCircuitBreakerProperties.getConfigurationCleanupInterval(), - polarisCircuitBreakerProperties.getConfigurationCleanupInterval(), TimeUnit.MILLISECONDS); - } - - @Override - public ReactiveCircuitBreaker create(String id) { - PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf = getConfigurations() - .computeIfAbsent(id, defaultConfiguration); - return new ReactivePolarisCircuitBreaker(conf, consumerAPI, circuitBreakAPI); - } - - @Override - protected PolarisCircuitBreakerConfigBuilder configBuilder(String id) { - String[] metadata = PolarisCircuitBreakerUtils.resolveCircuitBreakerId(id); - return new PolarisCircuitBreakerConfigBuilder(metadata[0], metadata[1], metadata[2], metadata[3], metadata[4]); - } - - @Override - public void configureDefault( - Function defaultConfiguration) { - this.defaultConfiguration = defaultConfiguration; - } - - @Override - public void destroy() { - ThreadPoolUtils.waitAndStopThreadPools(new ExecutorService[] {cleanupService}); - } -} 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 index 8e5c889b1f..946f9a6aea 100644 --- 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 @@ -17,7 +17,7 @@ package com.tencent.cloud.polaris.circuitbreaker.common; -import com.tencent.cloud.polaris.circuitbreaker.zuul.PolarisCircuitBreakerPostZuulFilter; +import com.tencent.cloud.polaris.context.CircuitBreakerStatusCodeException; import com.tencent.polaris.circuitbreak.api.pojo.ResultToErrorCode; import feign.FeignException; @@ -50,8 +50,8 @@ else if (checkClassExist("org.springframework.web.reactive.function.client.WebCl && e instanceof WebClientResponseException) { return ((WebClientResponseException) e).getRawStatusCode(); } - else if (e instanceof PolarisCircuitBreakerPostZuulFilter.CircuitBreakerStatusCodeException) { - return ((PolarisCircuitBreakerPostZuulFilter.CircuitBreakerStatusCodeException) e).getRawStatusCode(); + else if (e instanceof CircuitBreakerStatusCodeException) { + return ((CircuitBreakerStatusCodeException) e).getRawStatusCode(); } return -1; } 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 index d7d2a68071..b4b7dfa941 100644 --- 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 @@ -17,21 +17,11 @@ 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; @@ -44,23 +34,9 @@ */ @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}) +@ConditionalOnClass({ DispatcherHandler.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 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 1c7e2070b6..93880b6d4e 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 @@ -24,11 +24,9 @@ import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreakerFactory; import com.tencent.cloud.polaris.circuitbreaker.common.CircuitBreakerConfigModifier; +import com.tencent.cloud.polaris.circuitbreaker.reporter.CircuitBreakerPlugin; import com.tencent.cloud.polaris.circuitbreaker.reporter.ExceptionCircuitBreakerReporter; import com.tencent.cloud.polaris.circuitbreaker.reporter.SuccessCircuitBreakerReporter; -import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerRestTemplateBeanPostProcessor; -import com.tencent.cloud.polaris.circuitbreaker.zuul.PolarisCircuitBreakerPostZuulFilter; -import com.tencent.cloud.polaris.circuitbreaker.zuul.PolarisCircuitBreakerZuulFilter; import com.tencent.cloud.polaris.circuitbreaker.zuul.PolarisZuulFallbackFactory; import com.tencent.cloud.polaris.context.PolarisSDKContextManager; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; @@ -42,10 +40,8 @@ import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; import org.springframework.cloud.client.circuitbreaker.Customizer; import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; /** @@ -68,13 +64,6 @@ public class PolarisCircuitBreakerAutoConfiguration { System.setProperty("hystrix.command.default.circuitBreaker.enabled", "false"); } - @Bean - @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") - public static PolarisCircuitBreakerRestTemplateBeanPostProcessor polarisCircuitBreakerRestTemplateBeanPostProcessor( - ApplicationContext applicationContext) { - return new PolarisCircuitBreakerRestTemplateBeanPostProcessor(applicationContext); - } - @Bean @ConditionalOnMissingBean(SuccessCircuitBreakerReporter.class) public SuccessCircuitBreakerReporter successCircuitBreakerReporter(RpcEnhancementReporterProperties properties, @@ -82,6 +71,12 @@ public SuccessCircuitBreakerReporter successCircuitBreakerReporter(RpcEnhancemen return new SuccessCircuitBreakerReporter(properties, polarisSDKContextManager.getCircuitBreakAPI()); } + @Bean + @ConditionalOnMissingBean(CircuitBreakerPlugin.class) + public CircuitBreakerPlugin circuitBreakerPlugin(CircuitBreakerFactory circuitBreakerFactory) { + return new CircuitBreakerPlugin(circuitBreakerFactory); + } + @Bean @ConditionalOnMissingBean(ExceptionCircuitBreakerReporter.class) public ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter(RpcEnhancementReporterProperties properties, @@ -116,20 +111,5 @@ protected static class PolarisCircuitBreakerZuulFilterConfig { public PolarisZuulFallbackFactory polarisZuulFallbackFactory() { return new PolarisZuulFallbackFactory(zuulFallbackProviders); } - - @Bean - public PolarisCircuitBreakerZuulFilter polarisCircuitBreakerZuulFilter( - CircuitBreakerFactory circuitBreakerFactory, - PolarisZuulFallbackFactory polarisZuulFallbackFactory, - Environment environment) { - return new PolarisCircuitBreakerZuulFilter(circuitBreakerFactory, polarisZuulFallbackFactory, environment); - } - - @Bean - public PolarisCircuitBreakerPostZuulFilter polarisCircuitBreakerPostZuulFilter( - PolarisZuulFallbackFactory polarisZuulFallbackFactory, - Environment environment) { - return new PolarisCircuitBreakerPostZuulFilter(polarisZuulFallbackFactory, environment); - } } } 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 c6b601286d..2abc5099f4 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 @@ -30,7 +30,6 @@ @ConditionalOnProperty("spring.cloud.polaris.enabled") @Import({ PolarisCircuitBreakerAutoConfiguration.class, - ReactivePolarisCircuitBreakerAutoConfiguration.class, PolarisCircuitBreakerFeignClientAutoConfiguration.class, GatewayPolarisCircuitBreakerAutoConfiguration.class }) 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 deleted file mode 100644 index bc77d90e06..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/ReactivePolarisCircuitBreakerAutoConfiguration.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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.polaris.circuitbreaker.reporter.ExceptionCircuitBreakerReporter; -import com.tencent.cloud.polaris.circuitbreaker.reporter.SuccessCircuitBreakerReporter; -import com.tencent.cloud.polaris.context.PolarisSDKContextManager; -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -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 -@EnableConfigurationProperties(PolarisCircuitBreakerProperties.class) -@AutoConfigureAfter(RpcEnhancementAutoConfiguration.class) -public class ReactivePolarisCircuitBreakerAutoConfiguration { - - @Autowired(required = false) - private List> customizers = new ArrayList<>(); - - @Bean - @ConditionalOnMissingBean(SuccessCircuitBreakerReporter.class) - public SuccessCircuitBreakerReporter successCircuitBreakerReporter(RpcEnhancementReporterProperties properties, - PolarisSDKContextManager polarisSDKContextManager) { - return new SuccessCircuitBreakerReporter(properties, polarisSDKContextManager.getCircuitBreakAPI()); - } - - @Bean - @ConditionalOnMissingBean(ExceptionCircuitBreakerReporter.class) - public ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter(RpcEnhancementReporterProperties properties, - PolarisSDKContextManager polarisSDKContextManager) { - return new ExceptionCircuitBreakerReporter(properties, polarisSDKContextManager.getCircuitBreakAPI()); - } - - @Bean - @ConditionalOnMissingBean(ReactiveCircuitBreakerFactory.class) - public ReactiveCircuitBreakerFactory polarisReactiveCircuitBreakerFactory(PolarisSDKContextManager polarisSDKContextManager, - PolarisCircuitBreakerProperties polarisCircuitBreakerProperties) { - ReactivePolarisCircuitBreakerFactory factory = new ReactivePolarisCircuitBreakerFactory( - polarisSDKContextManager.getCircuitBreakAPI(), polarisSDKContextManager.getConsumerAPI(), polarisCircuitBreakerProperties); - customizers.forEach(customizer -> customizer.customize(factory)); - return factory; - } - - @Bean - @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/PolarisFeignCircuitBreaker.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreaker.java index 66d74b594a..4044430c49 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreaker.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreaker.java @@ -23,7 +23,6 @@ import feign.Target; import feign.codec.Decoder; -import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.cloud.openfeign.FeignCircuitBreaker; import org.springframework.util.ReflectionUtils; @@ -51,28 +50,9 @@ public static Builder builder() { */ public static final class Builder extends Feign.Builder { - private CircuitBreakerFactory circuitBreakerFactory; - private String feignClientName; - private PolarisCircuitBreakerNameResolver circuitBreakerNameResolver; - public Builder() { } - public Builder circuitBreakerFactory(CircuitBreakerFactory circuitBreakerFactory) { - this.circuitBreakerFactory = circuitBreakerFactory; - return this; - } - - public Builder feignClientName(String feignClientName) { - this.feignClientName = feignClientName; - return this; - } - - public Builder circuitBreakerNameResolver(PolarisCircuitBreakerNameResolver circuitBreakerNameResolver) { - this.circuitBreakerNameResolver = circuitBreakerNameResolver; - return this; - } - public T target(Target target, T fallback) { return build(fallback != null ? new FallbackFactory.Default(fallback) : null).newInstance(target); } @@ -91,7 +71,7 @@ public Feign build(final FallbackFactory nullableFallbackFactory) { field.setAccessible(true); Decoder decoder = (Decoder) ReflectionUtils.getField(field, this); this.invocationHandlerFactory((target, dispatch) -> new PolarisFeignCircuitBreakerInvocationHandler( - circuitBreakerFactory, feignClientName, target, dispatch, nullableFallbackFactory, circuitBreakerNameResolver, decoder)); + target, dispatch, nullableFallbackFactory, decoder)); return this.build(); } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerInvocationHandler.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerInvocationHandler.java index 94cd8b9efa..24e26f7dfc 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerInvocationHandler.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerInvocationHandler.java @@ -23,20 +23,14 @@ import java.lang.reflect.Proxy; import java.util.LinkedHashMap; import java.util.Map; -import java.util.function.Function; -import java.util.function.Supplier; import com.tencent.cloud.polaris.circuitbreaker.exception.FallbackWrapperException; import feign.InvocationHandlerFactory; import feign.Target; import feign.codec.Decoder; -import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; -import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException; import org.springframework.cloud.openfeign.FallbackFactory; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; import static feign.Util.checkNotNull; @@ -47,10 +41,6 @@ */ public class PolarisFeignCircuitBreakerInvocationHandler implements InvocationHandler { - private final CircuitBreakerFactory factory; - - private final String feignClientName; - private final Target target; private final Map dispatch; @@ -59,41 +49,19 @@ public class PolarisFeignCircuitBreakerInvocationHandler implements InvocationHa private final Map fallbackMethodMap; - private final PolarisCircuitBreakerNameResolver circuitBreakerNameResolver; - private final Decoder decoder; - public PolarisFeignCircuitBreakerInvocationHandler(CircuitBreakerFactory factory, String feignClientName, Target target, - Map dispatch, FallbackFactory nullableFallbackFactory, - PolarisCircuitBreakerNameResolver circuitBreakerNameResolver, Decoder decoder) { - this.factory = factory; - this.feignClientName = feignClientName; + public PolarisFeignCircuitBreakerInvocationHandler(Target target, + Map dispatch, + FallbackFactory nullableFallbackFactory, Decoder decoder) { + this.target = checkNotNull(target, "target"); this.dispatch = checkNotNull(dispatch, "dispatch"); this.fallbackMethodMap = toFallbackMethod(dispatch); this.nullableFallbackFactory = nullableFallbackFactory; - this.circuitBreakerNameResolver = circuitBreakerNameResolver; this.decoder = decoder; } - /** - * If the method param of {@link InvocationHandler#invoke(Object, Method, Object[])} - * is not accessible, i.e in a package-private interface, the fallback call will cause - * of access restrictions. But methods in dispatch are copied methods. So setting - * access to dispatch method doesn't take effect to the method in - * InvocationHandler.invoke. Use map to store a copy of method to invoke the fallback - * to bypass this and reducing the count of reflection calls. - * @return cached methods map for fallback invoking - */ - static Map toFallbackMethod(Map dispatch) { - Map result = new LinkedHashMap<>(); - for (Method method : dispatch.keySet()) { - method.setAccessible(true); - result.put(method, method); - } - return result; - } - @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { // early exit if the invoked method is from java.lang.Object @@ -113,18 +81,12 @@ else if ("hashCode".equals(method.getName())) { else if ("toString".equals(method.getName())) { return toString(); } - // disable feign.hystrix - if (circuitBreakerNameResolver == null) { + try { return this.dispatch.get(method).invoke(args); } - - String circuitName = circuitBreakerNameResolver.resolveCircuitBreakerName(feignClientName, target, method); - CircuitBreaker circuitBreaker = factory.create(circuitName); - Supplier supplier = asSupplier(method, args); - Function fallbackFunction; - if (this.nullableFallbackFactory != null) { - fallbackFunction = throwable -> { - Object fallback = this.nullableFallbackFactory.create(throwable); + catch (Exception e) { + if (this.nullableFallbackFactory != null) { + Object fallback = this.nullableFallbackFactory.create(e); try { return this.fallbackMethodMap.get(method).invoke(fallback, args); } @@ -132,21 +94,12 @@ else if ("toString".equals(method.getName())) { unwrapAndRethrow(exception); } return null; - }; - } - else { - fallbackFunction = throwable -> { + } + else { PolarisCircuitBreakerFallbackFactory.DefaultFallback fallback = - (PolarisCircuitBreakerFallbackFactory.DefaultFallback) new PolarisCircuitBreakerFallbackFactory(this.decoder).create(throwable); + (PolarisCircuitBreakerFallbackFactory.DefaultFallback) new PolarisCircuitBreakerFallbackFactory(this.decoder).create(e); return fallback.fallback(method); - }; - } - try { - return circuitBreaker.run(supplier, fallbackFunction); - } - catch (FallbackWrapperException e) { - // unwrap And Rethrow - throw e.getCause(); + } } } @@ -163,29 +116,22 @@ private void unwrapAndRethrow(Exception exception) { } } - private Supplier asSupplier(final Method method, final Object[] args) { - final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); - final Thread caller = Thread.currentThread(); - return () -> { - boolean isAsync = caller != Thread.currentThread(); - try { - if (isAsync) { - RequestContextHolder.setRequestAttributes(requestAttributes); - } - return dispatch.get(method).invoke(args); - } - catch (RuntimeException throwable) { - throw throwable; - } - catch (Throwable throwable) { - throw new RuntimeException(throwable); - } - finally { - if (isAsync) { - RequestContextHolder.resetRequestAttributes(); - } - } - }; + /** + * If the method param of {@link InvocationHandler#invoke(Object, Method, Object[])} + * is not accessible, i.e in a package-private interface, the fallback call will cause + * of access restrictions. But methods in dispatch are copied methods. So setting + * access to dispatch method doesn't take effect to the method in + * InvocationHandler.invoke. Use map to store a copy of method to invoke the fallback + * to bypass this and reducing the count of reflection calls. + * @return cached methods map for fallback invoking + */ + static Map toFallbackMethod(Map dispatch) { + Map result = new LinkedHashMap<>(); + for (Method method : dispatch.keySet()) { + method.setAccessible(true); + result.put(method, method); + } + return result; } @Override 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 deleted file mode 100644 index 68819f2740..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/gateway/PolarisCircuitBreakerFilterFactory.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.polaris.api.pojo.CircuitBreakerStatus; -import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; -import reactor.core.publisher.Flux; -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.route.Route; -import org.springframework.cloud.gateway.support.HttpStatusHolder; -import org.springframework.cloud.gateway.support.ServiceUnavailableException; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.http.HttpStatus; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.util.CollectionUtils; -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.GATEWAY_ROUTE_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 final ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory; - private final ObjectProvider dispatcherHandlerProvider; - private String routeIdPrefix; - // 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); - } - - private Set getDefaultStatus() { - return Arrays.stream(HttpStatus.values()) - .filter(HttpStatus::is5xxServerError) - .collect(Collectors.toSet()); - } - - @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()); - if (CollectionUtils.isEmpty(statuses)) { - statuses.addAll(getDefaultStatus()); - } - String circuitBreakerId = getCircuitBreakerId(config); - return new GatewayFilter() { - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); - String serviceName = circuitBreakerId; - if (route != null) { - serviceName = route.getUri().getHost(); - } - String path = exchange.getRequest().getPath().value(); - String method = exchange.getRequest().getMethod() == null ? - "GET" : exchange.getRequest().getMethod().name(); - ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(MetadataContext.LOCAL_NAMESPACE + "#" + serviceName + "#" + path + "#http#" + method); - return cb.run( - chain.filter(exchange).doOnSuccess(v -> { - // throw CircuitBreakerStatusCodeException by default for all need checking status - // so polaris can report right error status - Set statusNeedToCheck = new HashSet<>(); - statusNeedToCheck.addAll(statuses); - statusNeedToCheck.addAll(getDefaultStatus()); - HttpStatus status = exchange.getResponse().getStatusCode(); - if (statusNeedToCheck.contains(status)) { - throw new CircuitBreakerStatusCodeException(status); - } - }), - t -> { - // pre-check CircuitBreakerStatusCodeException's status matches input status - if (t instanceof CircuitBreakerStatusCodeException) { - HttpStatus status = ((CircuitBreakerStatusCodeException) t).getStatusCode(); - // no need to fallback - if (!statuses.contains(status)) { - return Mono.error(t); - } - } - // do fallback - if (config.getFallbackUri() == null) { - // polaris checking - if (t instanceof CallAbortedException) { - CircuitBreakerStatus.FallbackInfo fallbackInfo = ((CallAbortedException) t).getFallbackInfo(); - if (fallbackInfo != null) { - ServerHttpResponse response = exchange.getResponse(); - response.setRawStatusCode(fallbackInfo.getCode()); - if (fallbackInfo.getHeaders() != null) { - fallbackInfo.getHeaders() - .forEach((k, v) -> response.getHeaders().add(k, v)); - } - DataBuffer bodyBuffer = null; - if (fallbackInfo.getBody() != null) { - byte[] bytes = fallbackInfo.getBody().getBytes(StandardCharsets.UTF_8); - bodyBuffer = response.bufferFactory().wrap(bytes); - } - return bodyBuffer != null ? response.writeWith(Flux.just(bodyBuffer)) : response.setComplete(); - } - } - return 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)); - } - - @Override - public String toString() { - return filterToStringCreator(PolarisCircuitBreakerFilterFactory.this) - .append("name", config.getName()).append("fallback", config.getFallbackUri()).toString(); - } - }; - - } - - @Override - protected Mono handleErrorWithoutFallback(Throwable t) { - if (t instanceof java.util.concurrent.TimeoutException) { - return Mono.error(new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, t.getMessage(), t)); - } - if (t instanceof CallAbortedException) { - return Mono.error(new ServiceUnavailableException()); - } - if (t instanceof CircuitBreakerStatusCodeException) { - return Mono.empty(); - } - return Mono.error(t); - } - -} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPlugin.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPlugin.java new file mode 100644 index 0000000000..07655acbc6 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPlugin.java @@ -0,0 +1,117 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 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.reporter; + +import com.tencent.cloud.common.constant.ContextConstant; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreaker; +import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerHttpResponse; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; +import com.tencent.cloud.rpc.enhancement.plugin.reporter.SuccessPolarisReporter; +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; +import com.tencent.polaris.metadata.core.MetadataType; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; + +import static com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant.ClientPluginOrder.CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER; + +/** + * CircuitBreakerPlugin, do circuit breaker in enhance plugin and record info into metadata. + * + * @author Shedfree Wu + */ +public class CircuitBreakerPlugin implements EnhancedPlugin { + + private static final Logger LOG = LoggerFactory.getLogger(SuccessPolarisReporter.class); + + private CircuitBreakerFactory circuitBreakerFactory; + + public CircuitBreakerPlugin(CircuitBreakerFactory circuitBreakerFactory) { + this.circuitBreakerFactory = circuitBreakerFactory; + } + + @Override + public String getName() { + return CircuitBreakerPlugin.class.getName(); + } + + @Override + public EnhancedPluginType getType() { + return EnhancedPluginType.Client.PRE; + } + + @Override + public void run(EnhancedPluginContext context) throws Throwable { + + EnhancedRequestContext request = context.getRequest(); + EnhancedResponseContext response = context.getResponse(); + + String governanceNamespace = MetadataContextHolder.get().getDisposableMetadata().get(ContextConstant.POLARIS_GOVERNANCE_NAMESPACE); + if (StringUtils.isEmpty(governanceNamespace)) { + governanceNamespace = MetadataContext.LOCAL_NAMESPACE; + } + + String host = request.getServiceUrl() != null ? request.getServiceUrl().getHost() : request.getUrl().getHost(); + String path = request.getServiceUrl() != null ? request.getServiceUrl().getPath() : request.getUrl().getPath(); + String httpMethod = request.getHttpMethod().name(); + + CircuitBreaker circuitBreaker = circuitBreakerFactory.create(governanceNamespace + "#" + host + "#" + path + "#http#" + httpMethod); + if (circuitBreaker instanceof PolarisCircuitBreaker) { + PolarisCircuitBreaker polarisCircuitBreaker = (PolarisCircuitBreaker) circuitBreaker; + putMetadataObjectValue(ContextConstant.CircuitBreaker.POLARIS_CIRCUIT_BREAKER, polarisCircuitBreaker); + putMetadataObjectValue(ContextConstant.CircuitBreaker.CIRCUIT_BREAKER_START_TIME, System.currentTimeMillis()); + + try { + polarisCircuitBreaker.acquirePermission(); + } + catch (CallAbortedException e) { + LOG.debug("[CircuitBreakerPlugin] request is aborted. request service url=[{}]", request.getServiceUrl()); + if (e.getFallbackInfo() != null) { + Object fallbackResponse = new PolarisCircuitBreakerHttpResponse(e.getFallbackInfo()); + putMetadataObjectValue(ContextConstant.CircuitBreaker.CIRCUIT_BREAKER_FALLBACK_HTTP_RESPONSE, fallbackResponse); + } + throw e; + } + } + } + + @Override + public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) { + LOG.error("SuccessCircuitBreakerReporter runs failed. context=[{}].", + context, throwable); + } + + @Override + public int getOrder() { + return CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER; + } + + private void putMetadataObjectValue(String key, Object value) { + MetadataContextHolder.get().getMetadataContainer(MetadataType.APPLICATION, true). + putMetadataObjectValue(key, value); + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/ExceptionCircuitBreakerReporter.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/ExceptionCircuitBreakerReporter.java index 2a8f495193..9968bbf426 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/ExceptionCircuitBreakerReporter.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/ExceptionCircuitBreakerReporter.java @@ -18,7 +18,11 @@ package com.tencent.cloud.polaris.circuitbreaker.reporter; import java.util.Optional; +import java.util.concurrent.TimeUnit; +import com.tencent.cloud.common.constant.ContextConstant; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreaker; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; @@ -27,6 +31,9 @@ import com.tencent.cloud.rpc.enhancement.plugin.PolarisEnhancedPluginUtils; import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat; import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.circuitbreak.api.pojo.InvokeContext; +import com.tencent.polaris.metadata.core.MetadataObjectValue; +import com.tencent.polaris.metadata.core.MetadataType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,6 +81,10 @@ public void run(EnhancedPluginContext context) throws Throwable { ServiceInstance serviceInstance = Optional.ofNullable(context.getTargetServiceInstance()) .orElse(new DefaultServiceInstance()); + if (LOG.isDebugEnabled()) { + String governanceNamespace = MetadataContextHolder.get().getDisposableMetadata().get(ContextConstant.POLARIS_GOVERNANCE_NAMESPACE); + LOG.debug("governanceNamespace={}, serviceInstance:{}", governanceNamespace, serviceInstance); + } ResourceStat resourceStat = PolarisEnhancedPluginUtils.createInstanceResourceStat( serviceInstance.getServiceId(), serviceInstance.getHost(), @@ -90,6 +101,34 @@ public void run(EnhancedPluginContext context) throws Throwable { circuitBreakAPI.report(resourceStat); + MetadataObjectValue circuitBreakerObject = MetadataContextHolder.get(). + getMetadataContainer(MetadataType.APPLICATION, true). + getMetadataValue(ContextConstant.CircuitBreaker.POLARIS_CIRCUIT_BREAKER); + + MetadataObjectValue startTimeMilliObject = MetadataContextHolder.get(). + getMetadataContainer(MetadataType.APPLICATION, true). + getMetadataValue(ContextConstant.CircuitBreaker.CIRCUIT_BREAKER_START_TIME); + + boolean existCircuitBreaker = existMetadataValue(circuitBreakerObject); + boolean existStartTime = existMetadataValue(startTimeMilliObject); + + if (existCircuitBreaker && existStartTime) { + PolarisCircuitBreaker polarisCircuitBreaker = circuitBreakerObject.getObjectValue().get(); + Long startTimeMillis = startTimeMilliObject.getObjectValue().get(); + + long delay = System.currentTimeMillis() - startTimeMillis; + InvokeContext.ResponseContext responseContext = new InvokeContext.ResponseContext(); + responseContext.setDuration(delay); + responseContext.setDurationUnit(TimeUnit.MILLISECONDS); + responseContext.setError(context.getThrowable()); + + if (responseContext.getError() == null) { + polarisCircuitBreaker.onSuccess(responseContext); + } + else { + polarisCircuitBreaker.onError(responseContext); + } + } } @Override @@ -102,4 +141,10 @@ public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) public int getOrder() { return CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER; } + + private static boolean existMetadataValue(MetadataObjectValue metadataObjectValue) { + return Optional.ofNullable(metadataObjectValue).map(MetadataObjectValue::getObjectValue). + map(Optional::isPresent).orElse(false); + } + } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/SuccessCircuitBreakerReporter.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/SuccessCircuitBreakerReporter.java index bbe5ba52fb..0634c96bda 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/SuccessCircuitBreakerReporter.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/SuccessCircuitBreakerReporter.java @@ -18,7 +18,12 @@ package com.tencent.cloud.polaris.circuitbreaker.reporter; import java.util.Optional; +import java.util.concurrent.TimeUnit; +import com.tencent.cloud.common.constant.ContextConstant; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreaker; +import com.tencent.cloud.polaris.context.CircuitBreakerStatusCodeException; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; @@ -29,11 +34,15 @@ import com.tencent.cloud.rpc.enhancement.plugin.reporter.SuccessPolarisReporter; import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat; import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.circuitbreak.api.pojo.InvokeContext; +import com.tencent.polaris.metadata.core.MetadataObjectValue; +import com.tencent.polaris.metadata.core.MetadataType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.ServiceInstance; +import org.springframework.http.HttpStatus; import static com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant.ClientPluginOrder.CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER; @@ -75,7 +84,6 @@ public void run(EnhancedPluginContext context) throws Throwable { EnhancedResponseContext response = context.getResponse(); ServiceInstance serviceInstance = Optional.ofNullable(context.getTargetServiceInstance()) .orElse(new DefaultServiceInstance()); - ResourceStat resourceStat = PolarisEnhancedPluginUtils.createInstanceResourceStat( serviceInstance.getServiceId(), serviceInstance.getHost(), @@ -91,6 +99,37 @@ public void run(EnhancedPluginContext context) throws Throwable { .getPath(), response.getHttpStatus(), context.getDelay()); circuitBreakAPI.report(resourceStat); + + MetadataObjectValue circuitBreakerObject = MetadataContextHolder.get(). + getMetadataContainer(MetadataType.APPLICATION, true). + getMetadataValue(ContextConstant.CircuitBreaker.POLARIS_CIRCUIT_BREAKER); + + MetadataObjectValue startTimeMilliObject = MetadataContextHolder.get(). + getMetadataContainer(MetadataType.APPLICATION, true). + getMetadataValue(ContextConstant.CircuitBreaker.CIRCUIT_BREAKER_START_TIME); + + boolean existCircuitBreaker = existMetadataValue(circuitBreakerObject); + boolean existStartTime = existMetadataValue(startTimeMilliObject); + + if (existCircuitBreaker && existStartTime) { + PolarisCircuitBreaker polarisCircuitBreaker = circuitBreakerObject.getObjectValue().get(); + Long startTimeMillis = startTimeMilliObject.getObjectValue().get(); + long delay = System.currentTimeMillis() - startTimeMillis; + InvokeContext.ResponseContext responseContext = new InvokeContext.ResponseContext(); + responseContext.setDuration(delay); + responseContext.setDurationUnit(TimeUnit.MILLISECONDS); + HttpStatus status = HttpStatus.resolve(response.getHttpStatus()); + if (status != null && (status.is5xxServerError() || status.is4xxClientError())) { + Throwable throwable = new CircuitBreakerStatusCodeException(status); + responseContext.setError(throwable); + } + if (responseContext.getError() == null) { + polarisCircuitBreaker.onSuccess(responseContext); + } + else { + polarisCircuitBreaker.onError(responseContext); + } + } } @Override @@ -103,4 +142,9 @@ public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) public int getOrder() { return CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER; } + + private static boolean existMetadataValue(MetadataObjectValue metadataObjectValue) { + return Optional.ofNullable(metadataObjectValue).map(MetadataObjectValue::getObjectValue). + map(Optional::isPresent).orElse(false); + } } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateBeanPostProcessor.java deleted file mode 100644 index 646725ab85..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateBeanPostProcessor.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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; - -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.core.type.MethodMetadata; -import org.springframework.core.type.StandardMethodMetadata; -import org.springframework.util.StringUtils; -import org.springframework.web.client.RestTemplate; - -/** - * PolarisCircuitBreakerRestTemplateBeanPostProcessor. - * - * @author sean yu - */ -public class PolarisCircuitBreakerRestTemplateBeanPostProcessor implements MergedBeanDefinitionPostProcessor { - - private final ApplicationContext applicationContext; - private final ConcurrentHashMap cache = new ConcurrentHashMap<>(); - - public PolarisCircuitBreakerRestTemplateBeanPostProcessor(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - private void checkPolarisCircuitBreakerRestTemplate(PolarisCircuitBreaker polarisCircuitBreaker) { - if ( - StringUtils.hasText(polarisCircuitBreaker.fallback()) && - !PolarisCircuitBreakerFallback.class.toGenericString() - .equals(polarisCircuitBreaker.fallbackClass().toGenericString()) - ) { - throw new IllegalArgumentException("PolarisCircuitBreaker's fallback and fallbackClass could not set at sametime !"); - } - } - - @Override - public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { - if (checkAnnotated(beanDefinition, beanType, beanName)) { - PolarisCircuitBreaker polarisCircuitBreaker; - if (beanDefinition.getSource() instanceof StandardMethodMetadata) { - polarisCircuitBreaker = ((StandardMethodMetadata) beanDefinition.getSource()).getIntrospectedMethod() - .getAnnotation(PolarisCircuitBreaker.class); - } - else { - polarisCircuitBreaker = beanDefinition.getResolvedFactoryMethod() - .getAnnotation(PolarisCircuitBreaker.class); - } - checkPolarisCircuitBreakerRestTemplate(polarisCircuitBreaker); - cache.put(beanName, polarisCircuitBreaker); - } - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (cache.containsKey(beanName)) { - // add interceptor for each RestTemplate with @PolarisCircuitBreaker annotation - StringBuilder interceptorBeanNamePrefix = new StringBuilder(); - PolarisCircuitBreaker polarisCircuitBreaker = cache.get(beanName); - interceptorBeanNamePrefix - .append(StringUtils.uncapitalize( - PolarisCircuitBreaker.class.getSimpleName())) - .append("_") - .append(polarisCircuitBreaker.fallback()) - .append("_") - .append(polarisCircuitBreaker.fallbackClass().getSimpleName()); - RestTemplate restTemplate = (RestTemplate) bean; - String interceptorBeanName = interceptorBeanNamePrefix + "@" + bean; - CircuitBreakerFactory circuitBreakerFactory = this.applicationContext.getBean(CircuitBreakerFactory.class); - registerBean(interceptorBeanName, polarisCircuitBreaker, applicationContext, circuitBreakerFactory, restTemplate); - PolarisCircuitBreakerRestTemplateInterceptor polarisCircuitBreakerRestTemplateInterceptor = applicationContext - .getBean(interceptorBeanName, PolarisCircuitBreakerRestTemplateInterceptor.class); - restTemplate.getInterceptors().add(0, polarisCircuitBreakerRestTemplateInterceptor); - } - return bean; - } - - private boolean checkAnnotated(RootBeanDefinition beanDefinition, - Class beanType, String beanName) { - return beanName != null && beanType == RestTemplate.class - && beanDefinition.getSource() instanceof MethodMetadata - && ((MethodMetadata) beanDefinition.getSource()) - .isAnnotated(PolarisCircuitBreaker.class.getName()); - } - - private void registerBean(String interceptorBeanName, PolarisCircuitBreaker polarisCircuitBreaker, - ApplicationContext applicationContext, CircuitBreakerFactory circuitBreakerFactory, RestTemplate restTemplate) { - // register PolarisCircuitBreakerRestTemplateInterceptor bean - DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext - .getAutowireCapableBeanFactory(); - BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder - .genericBeanDefinition(PolarisCircuitBreakerRestTemplateInterceptor.class); - beanDefinitionBuilder.addConstructorArgValue(polarisCircuitBreaker); - beanDefinitionBuilder.addConstructorArgValue(applicationContext); - beanDefinitionBuilder.addConstructorArgValue(circuitBreakerFactory); - beanDefinitionBuilder.addConstructorArgValue(restTemplate); - BeanDefinition interceptorBeanDefinition = beanDefinitionBuilder - .getRawBeanDefinition(); - beanFactory.registerBeanDefinition(interceptorBeanName, - interceptorBeanDefinition); - } - -} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateInterceptor.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateInterceptor.java deleted file mode 100644 index 932075bca6..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateInterceptor.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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; - -import java.io.IOException; -import java.lang.reflect.Method; - -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.polaris.circuitbreaker.exception.FallbackWrapperException; -import com.tencent.polaris.api.pojo.CircuitBreakerStatus; -import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; - -import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; -import org.springframework.web.client.ResponseErrorHandler; -import org.springframework.web.client.RestTemplate; - -/** - * PolarisCircuitBreakerRestTemplateInterceptor. - * - * @author sean yu - */ -public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpRequestInterceptor { - - private final PolarisCircuitBreaker polarisCircuitBreaker; - - private final ApplicationContext applicationContext; - - private final CircuitBreakerFactory circuitBreakerFactory; - - private final RestTemplate restTemplate; - - public PolarisCircuitBreakerRestTemplateInterceptor( - PolarisCircuitBreaker polarisCircuitBreaker, - ApplicationContext applicationContext, - CircuitBreakerFactory circuitBreakerFactory, - RestTemplate restTemplate - ) { - this.polarisCircuitBreaker = polarisCircuitBreaker; - this.applicationContext = applicationContext; - this.circuitBreakerFactory = circuitBreakerFactory; - this.restTemplate = restTemplate; - } - - @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { - try { - String httpMethod = "GET"; - if (request.getMethod() != null) { - httpMethod = request.getMethod().name(); - } - return circuitBreakerFactory.create(MetadataContext.LOCAL_NAMESPACE + "#" + request.getURI() - .getHost() + "#" + request.getURI().getPath() + "#http#" + httpMethod).run( - () -> { - try { - ClientHttpResponse response = execution.execute(request, body); - ResponseErrorHandler errorHandler = restTemplate.getErrorHandler(); - if (errorHandler.hasError(response)) { - errorHandler.handleError(request.getURI(), request.getMethod(), response); - } - return response; - } - catch (IOException e) { - throw new IllegalStateException(e); - } - }, - t -> { - if (StringUtils.hasText(polarisCircuitBreaker.fallback())) { - CircuitBreakerStatus.FallbackInfo fallbackInfo = new CircuitBreakerStatus.FallbackInfo(200, null, polarisCircuitBreaker.fallback()); - return new PolarisCircuitBreakerHttpResponse(fallbackInfo); - } - if (!PolarisCircuitBreakerFallback.class.toGenericString() - .equals(polarisCircuitBreaker.fallbackClass().toGenericString())) { - Method method = ReflectionUtils.findMethod(PolarisCircuitBreakerFallback.class, "fallback"); - PolarisCircuitBreakerFallback polarisCircuitBreakerFallback = applicationContext.getBean(polarisCircuitBreaker.fallbackClass()); - return (PolarisCircuitBreakerHttpResponse) ReflectionUtils.invokeMethod(method, polarisCircuitBreakerFallback); - } - if (t instanceof CallAbortedException) { - CircuitBreakerStatus.FallbackInfo fallbackInfo = ((CallAbortedException) t).getFallbackInfo(); - if (fallbackInfo != null) { - return new PolarisCircuitBreakerHttpResponse(fallbackInfo); - } - } - throw new FallbackWrapperException(t); - } - ); - } - catch (FallbackWrapperException e) { - // unwrap And Rethrow - Throwable underlyingException = e.getCause(); - if (underlyingException instanceof RuntimeException) { - throw (RuntimeException) underlyingException; - } - throw new IllegalStateException(underlyingException); - } - - } - -} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/zuul/PolarisCircuitBreakerPostZuulFilter.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/zuul/PolarisCircuitBreakerPostZuulFilter.java deleted file mode 100644 index 784b73227e..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/zuul/PolarisCircuitBreakerPostZuulFilter.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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.zuul; - -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.servlet.http.HttpServletResponse; - -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; -import com.netflix.zuul.exception.ZuulException; -import com.tencent.cloud.common.constant.OrderConstant; -import com.tencent.cloud.common.util.ZuulFilterUtils; -import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreaker; -import com.tencent.polaris.circuitbreak.api.pojo.InvokeContext; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; -import org.springframework.core.env.Environment; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; -import org.springframework.web.client.HttpStatusCodeException; - -import static com.tencent.cloud.common.constant.ContextConstant.Zuul.POLARIS_CIRCUIT_BREAKER; -import static com.tencent.cloud.common.constant.ContextConstant.Zuul.POLARIS_PRE_ROUTE_TIME; -import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.POST_TYPE; - -/** - * Polaris circuit breaker post-processing. Including reporting. - * - * @author Haotian Zhang - */ -public class PolarisCircuitBreakerPostZuulFilter extends ZuulFilter { - - private static final Logger LOGGER = LoggerFactory.getLogger(PolarisCircuitBreakerPostZuulFilter.class); - - private final PolarisZuulFallbackFactory polarisZuulFallbackFactory; - - private final Environment environment; - - public PolarisCircuitBreakerPostZuulFilter(PolarisZuulFallbackFactory polarisZuulFallbackFactory, - Environment environment) { - this.polarisZuulFallbackFactory = polarisZuulFallbackFactory; - this.environment = environment; - } - - @Override - public String filterType() { - return POST_TYPE; - } - - @Override - public int filterOrder() { - return OrderConstant.Client.Zuul.CIRCUIT_BREAKER_POST_FILTER_ORDER; - } - - @Override - public boolean shouldFilter() { - String enabled = environment.getProperty("spring.cloud.polaris.circuitbreaker.enabled"); - return StringUtils.isEmpty(enabled) || enabled.equals("true"); - } - - @Override - public Object run() throws ZuulException { - RequestContext context = RequestContext.getCurrentContext(); - - HttpServletResponse response = context.getResponse(); - HttpStatus status = HttpStatus.resolve(response.getStatus()); - - Object polarisCircuitBreakerObject = context.get(POLARIS_CIRCUIT_BREAKER); - Object startTimeMilliObject = context.get(POLARIS_PRE_ROUTE_TIME); - if (polarisCircuitBreakerObject != null && polarisCircuitBreakerObject instanceof PolarisCircuitBreaker - && startTimeMilliObject != null && startTimeMilliObject instanceof Long) { - PolarisCircuitBreaker polarisCircuitBreaker = (PolarisCircuitBreaker) polarisCircuitBreakerObject; - Long startTimeMilli = (Long) startTimeMilliObject; - long delay = System.currentTimeMillis() - startTimeMilli; - InvokeContext.ResponseContext responseContext = new InvokeContext.ResponseContext(); - responseContext.setDuration(delay); - responseContext.setDurationUnit(TimeUnit.MILLISECONDS); - - if (status != null && status.is5xxServerError()) { - Throwable throwable = new CircuitBreakerStatusCodeException(status); - responseContext.setError(throwable); - - // fallback if FallbackProvider is implemented. - String serviceId = ZuulFilterUtils.getServiceId(context); - FallbackProvider fallbackProvider = polarisZuulFallbackFactory.getFallbackProvider(serviceId); - if (fallbackProvider != null) { - ClientHttpResponse clientHttpResponse = fallbackProvider.fallbackResponse(serviceId, throwable); - try { - // set status code - context.setResponseStatusCode(clientHttpResponse.getRawStatusCode()); - // set headers - HttpHeaders headers = clientHttpResponse.getHeaders(); - for (String key : headers.keySet()) { - List values = headers.get(key); - if (!CollectionUtils.isEmpty(values)) { - for (String value : values) { - context.addZuulResponseHeader(key, value); - } - } - } - // set body - context.getResponse().setCharacterEncoding("UTF-8"); - context.setResponseBody(IOUtils.toString(clientHttpResponse.getBody(), StandardCharsets.UTF_8)); - } - catch (Exception e) { - LOGGER.error("error filter exception", e); - } - } - } - - if (responseContext.getError() == null) { - polarisCircuitBreaker.onSuccess(responseContext); - } - else { - polarisCircuitBreaker.onError(responseContext); - } - } - return null; - } - - public class CircuitBreakerStatusCodeException extends HttpStatusCodeException { - - public CircuitBreakerStatusCodeException(HttpStatus statusCode) { - super(statusCode); - } - - } -} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/zuul/PolarisCircuitBreakerZuulFilter.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/zuul/PolarisCircuitBreakerZuulFilter.java deleted file mode 100644 index 927020cf0e..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/zuul/PolarisCircuitBreakerZuulFilter.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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.zuul; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.List; - -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; -import com.netflix.zuul.exception.ZuulException; -import com.tencent.cloud.common.constant.OrderConstant; -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.util.ZuulFilterUtils; -import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreaker; -import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerHttpResponse; -import com.tencent.cloud.polaris.circuitbreaker.util.PolarisCircuitBreakerUtils; -import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; -import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; -import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; -import org.springframework.core.env.Environment; -import org.springframework.http.HttpHeaders; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -import static com.tencent.cloud.common.constant.ContextConstant.Zuul.POLARIS_CIRCUIT_BREAKER; -import static com.tencent.cloud.common.constant.ContextConstant.Zuul.POLARIS_IS_IN_ROUTING_STATE; -import static com.tencent.cloud.common.constant.ContextConstant.Zuul.POLARIS_PRE_ROUTE_TIME; -import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; - -/** - * Polaris circuit breaker implement in Zuul. - * - * @author Haotian Zhang - */ -public class PolarisCircuitBreakerZuulFilter extends ZuulFilter { - - private static final Logger LOGGER = LoggerFactory.getLogger(PolarisCircuitBreakerZuulFilter.class); - - private final CircuitBreakerFactory circuitBreakerFactory; - - private final PolarisZuulFallbackFactory polarisZuulFallbackFactory; - - private final Environment environment; - - public PolarisCircuitBreakerZuulFilter( - CircuitBreakerFactory circuitBreakerFactory, - PolarisZuulFallbackFactory polarisZuulFallbackFactory, - Environment environment) { - this.circuitBreakerFactory = circuitBreakerFactory; - this.polarisZuulFallbackFactory = polarisZuulFallbackFactory; - this.environment = environment; - } - - @Override - public String filterType() { - return PRE_TYPE; - } - - /** - * ServiceId is set after PreDecorationFilter. - * - * @return filter order - */ - @Override - public int filterOrder() { - return OrderConstant.Client.Zuul.CIRCUIT_BREAKER_FILTER_ORDER; - } - - @Override - public boolean shouldFilter() { - String enabled = environment.getProperty("spring.cloud.polaris.circuitbreaker.enabled"); - return StringUtils.isEmpty(enabled) || enabled.equals("true"); - } - - @Override - public Object run() throws ZuulException { - RequestContext context = RequestContext.getCurrentContext(); - - String serviceId = ZuulFilterUtils.getServiceId(context); - String path = ZuulFilterUtils.getPath(context); - String circuitName = com.tencent.polaris.api.utils.StringUtils.isBlank(path) ? - MetadataContext.LOCAL_NAMESPACE + "#" + serviceId : - MetadataContext.LOCAL_NAMESPACE + "#" + serviceId + "#" + path + "#http#" + context.getRequest() - .getMethod(); - CircuitBreaker circuitBreaker = circuitBreakerFactory.create(circuitName); - if (circuitBreaker instanceof PolarisCircuitBreaker) { - PolarisCircuitBreaker polarisCircuitBreaker = (PolarisCircuitBreaker) circuitBreaker; - context.set(POLARIS_CIRCUIT_BREAKER, circuitBreaker); - try { - polarisCircuitBreaker.acquirePermission(); - } - catch (CallAbortedException exception) { - FallbackProvider fallbackProvider = polarisZuulFallbackFactory.getFallbackProvider(serviceId); - ClientHttpResponse clientHttpResponse; - if (fallbackProvider != null) { - clientHttpResponse = fallbackProvider.fallbackResponse(serviceId, exception); - } - else if (exception.getFallbackInfo() != null) { - clientHttpResponse = new PolarisCircuitBreakerHttpResponse(exception.getFallbackInfo()); - } - else { - throw new IllegalStateException(exception); - } - try { - context.setSendZuulResponse(false); - // set status code - context.setResponseStatusCode(clientHttpResponse.getRawStatusCode()); - // set headers - HttpHeaders headers = clientHttpResponse.getHeaders(); - for (String key : headers.keySet()) { - List values = headers.get(key); - if (!CollectionUtils.isEmpty(values)) { - for (String value : values) { - context.addZuulResponseHeader(key, value); - } - } - } - // set body - context.getResponse().setCharacterEncoding("UTF-8"); - context.setResponseBody(IOUtils.toString(clientHttpResponse.getBody(), StandardCharsets.UTF_8)); - LOGGER.debug("PolarisCircuitBreaker CallAbortedException: {}", exception.getMessage()); - PolarisCircuitBreakerUtils.reportStatus(polarisCircuitBreaker.getConsumerAPI(), polarisCircuitBreaker.getConf(), exception); - } - catch (IOException e) { - LOGGER.error("Return circuit breaker fallback info failed: {}", e.getMessage()); - throw new IllegalStateException(e); - } - } - context.set(POLARIS_PRE_ROUTE_TIME, Long.valueOf(System.currentTimeMillis())); - context.set(POLARIS_IS_IN_ROUTING_STATE, true); - } - return null; - } -} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/org/springframework/cloud/openfeign/PolarisFeignCircuitBreakerTargeter.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/org/springframework/cloud/openfeign/PolarisFeignCircuitBreakerTargeter.java index 4fd75c2630..0d94fe9734 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/org/springframework/cloud/openfeign/PolarisFeignCircuitBreakerTargeter.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/org/springframework/cloud/openfeign/PolarisFeignCircuitBreakerTargeter.java @@ -17,12 +17,10 @@ package org.springframework.cloud.openfeign; -import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisCircuitBreakerNameResolver; import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignCircuitBreaker; import feign.Feign; import feign.Target; -import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; import org.springframework.util.StringUtils; /** @@ -32,16 +30,6 @@ */ public class PolarisFeignCircuitBreakerTargeter implements Targeter { - private final CircuitBreakerFactory circuitBreakerFactory; - - private final PolarisCircuitBreakerNameResolver circuitBreakerNameResolver; - - public PolarisFeignCircuitBreakerTargeter(CircuitBreakerFactory circuitBreakerFactory, - PolarisCircuitBreakerNameResolver circuitBreakerNameResolver) { - this.circuitBreakerFactory = circuitBreakerFactory; - this.circuitBreakerNameResolver = circuitBreakerNameResolver; - } - @Override public T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget target) { @@ -58,20 +46,20 @@ public T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignCo if (fallbackFactory != void.class) { return targetWithFallbackFactory(name, context, target, builder, fallbackFactory); } - return builder(name, builder).target(target); + return builder.target(target); } private T targetWithFallbackFactory(String feignClientName, FeignContext context, Target.HardCodedTarget target, PolarisFeignCircuitBreaker.Builder builder, Class fallbackFactoryClass) { FallbackFactory fallbackFactory = (FallbackFactory) getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class); - return builder(feignClientName, builder).target(target, fallbackFactory); + return builder.target(target, fallbackFactory); } private T targetWithFallback(String feignClientName, FeignContext context, Target.HardCodedTarget target, PolarisFeignCircuitBreaker.Builder builder, Class fallback) { T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type()); - return builder(feignClientName, builder).target(target, fallbackInstance); + return builder.target(target, fallbackInstance); } private T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context, @@ -91,11 +79,4 @@ private T getFromContext(String fallbackMechanism, String feignClientName, F return (T) fallbackInstance; } - private PolarisFeignCircuitBreaker.Builder builder(String feignClientName, PolarisFeignCircuitBreaker.Builder builder) { - return builder - .circuitBreakerFactory(circuitBreakerFactory) - .feignClientName(feignClientName) - .circuitBreakerNameResolver(circuitBreakerNameResolver); - } - } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/org/springframework/cloud/openfeign/PolarisFeignCircuitBreakerTargeterAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/org/springframework/cloud/openfeign/PolarisFeignCircuitBreakerTargeterAutoConfiguration.java index 559218eb35..933ae6e340 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/org/springframework/cloud/openfeign/PolarisFeignCircuitBreakerTargeterAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/org/springframework/cloud/openfeign/PolarisFeignCircuitBreakerTargeterAutoConfiguration.java @@ -18,7 +18,6 @@ package org.springframework.cloud.openfeign; import com.tencent.cloud.polaris.circuitbreaker.config.ConditionalOnPolarisCircuitBreakerEnabled; -import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisCircuitBreakerNameResolver; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -46,7 +45,7 @@ public class PolarisFeignCircuitBreakerTargeterAutoConfiguration { @Primary @ConditionalOnBean(CircuitBreakerFactory.class) @ConditionalOnMissingBean(Targeter.class) - public Targeter polarisFeignCircuitBreakerTargeter(CircuitBreakerFactory circuitBreakerFactory, PolarisCircuitBreakerNameResolver circuitBreakerNameResolver) { - return new PolarisFeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerNameResolver); + public Targeter polarisFeignCircuitBreakerTargeter() { + return new PolarisFeignCircuitBreakerTargeter(); } } 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 a00e5d3c13..33036b92b7 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,6 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 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.openfeign.PolarisFeignCircuitBreakerTargeterAutoConfiguration,\ 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 index ec73339ce8..95c34a578c 100644 --- 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 @@ -24,7 +24,6 @@ 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; @@ -50,11 +49,8 @@ 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; @@ -62,7 +58,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * Test for {@link PolarisCircuitBreaker} and {@link ReactivePolarisCircuitBreaker} using real mock server. + * Test for {@link PolarisCircuitBreaker} using real mock server. * * @author sean yu */ @@ -138,26 +134,6 @@ public void testCircuitBreaker() { Utils.sleepUninterrupted(2000); } assertThat(resList).isEqualTo(Arrays.asList("invoke success", "fallback", "fallback", "fallback", "fallback")); - - // always fallback - ReactivePolarisCircuitBreakerFactory reactivePolarisCircuitBreakerFactory = new ReactivePolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI, polarisCircuitBreakerProperties); - 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/ReactivePolarisCircuitBreakerTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerTest.java deleted file mode 100644 index c2d972464c..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/ReactivePolarisCircuitBreakerTest.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; - -import com.tencent.cloud.common.util.ApplicationContextAwareUtils; -import com.tencent.cloud.common.util.ReflectionUtils; -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 com.tencent.polaris.client.util.Utils; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -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 static MockedStatic mockedApplicationContextAwareUtils; - private final ApplicationContextRunner reactiveContextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of( - PolarisContextAutoConfiguration.class, - RpcEnhancementAutoConfiguration.class, - LoadBalancerAutoConfiguration.class, - ReactivePolarisCircuitBreakerAutoConfiguration.class)) - .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true") - .withPropertyValues("spring.cloud.polaris.circuitbreaker.configuration-cleanup-interval=5000"); - - @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")); - - Method getConfigurationsMethod = ReflectionUtils.findMethod(PolarisCircuitBreakerFactory.class, - "getConfigurations"); - Assertions.assertNotNull(getConfigurationsMethod); - ReflectionUtils.makeAccessible(getConfigurationsMethod); - Map values = (Map) ReflectionUtils.invokeMethod(getConfigurationsMethod, polarisCircuitBreakerFactory); - Assertions.assertNotNull(values); - Assertions.assertTrue(values.size() >= 0); - - Utils.sleepUninterrupted(10 * 1000); - // clear by cleanupService in ReactivePolarisCircuitBreakerFactory - Assertions.assertEquals(0, values.size()); - }); - } - -} 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 index e5933fa2d8..1c1f6529da 100644 --- 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 @@ -26,7 +26,6 @@ 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 static org.assertj.core.api.Assertions.assertThat; @@ -46,14 +45,6 @@ public class PolarisCircuitBreakerAutoConfigurationTest { 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 -> { @@ -64,13 +55,4 @@ public void testDefaultInitialization() { }); } - @Test - public void testReactiveInitialization() { - this.reactiveContextRunner.run(context -> { - assertThat(context).hasSingleBean(ReactivePolarisCircuitBreakerAutoConfiguration.class); - assertThat(context).hasSingleBean(ReactiveCircuitBreakerFactory.class); - assertThat(context).hasSingleBean(CircuitBreakerConfigModifier.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/config/PolarisCircuitBreakerBootstrapConfigurationTest.java index 1b0e900ff7..8b704de81e 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/config/PolarisCircuitBreakerBootstrapConfigurationTest.java @@ -54,7 +54,6 @@ public void testDefaultInitialization() { this.contextRunner.run(context -> { 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/feign/PolarisCircuitBreakerFeignIntegrationDisableFeignHystrixTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerFeignIntegrationDisableFeignHystrixTest.java deleted file mode 100644 index 5fdb7b8c08..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerFeignIntegrationDisableFeignHystrixTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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 org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - -/** - * @author Shedfree Wu - */ -@ExtendWith(SpringExtension.class) -@SpringBootTest(webEnvironment = RANDOM_PORT, - classes = PolarisCircuitBreakerFeignIntegrationTest.TestConfig.class, - properties = { - "feign.hystrix.enabled=false", - "spring.cloud.gateway.enabled=false", - "feign.circuitbreaker.enabled=true", - "spring.cloud.polaris.namespace=" + NAMESPACE_TEST, - "spring.cloud.polaris.service=test" - }) -public class PolarisCircuitBreakerFeignIntegrationDisableFeignHystrixTest { - - @Autowired - private PolarisCircuitBreakerFeignIntegrationTest.EchoService echoService; - - @Test - public void testFeignClient() { - assertThatThrownBy(() -> { - echoService.echo("test"); - }).isInstanceOf(RuntimeException.class) - .hasMessageContaining("Load balancer does not have available server for client"); - } -} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerFeignIntegrationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerFeignIntegrationTest.java deleted file mode 100644 index 2b2813fa18..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisCircuitBreakerFeignIntegrationTest.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.InvocationTargetException; -import java.nio.charset.StandardCharsets; -import java.util.stream.Collectors; - -import com.google.protobuf.util.JsonFormat; -import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration; -import com.tencent.cloud.polaris.context.PolarisSDKContextManager; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.client.util.Utils; -import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto; -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.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.client.circuitbreaker.NoFallbackAvailableException; -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.context.annotation.Primary; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; -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 = { - "feign.hystrix.enabled=true", - "spring.cloud.gateway.enabled=false", - "spring.cloud.polaris.address=grpc://127.0.0.1:10081", - "feign.circuitbreaker.enabled=true", - "spring.cloud.polaris.namespace=" + NAMESPACE_TEST, - "spring.cloud.polaris.service=test" - }) -public class PolarisCircuitBreakerFeignIntegrationTest { - - private static final String TEST_SERVICE_NAME = "test-service-callee"; - - private static NamingServer namingServer; - - @Autowired - private EchoService echoService; - - @Autowired - private FooService fooService; - - @Autowired - private BarService barService; - - @Autowired - private BazService bazService; - - @BeforeAll - static void beforeAll() throws Exception { - PolarisSDKContextManager.innerDestroy(); - namingServer = NamingServer.startNamingServer(10081); - ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME); - - CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder(); - InputStream inputStream = PolarisCircuitBreakerFeignIntegrationTest.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 - static void afterAll() { - if (null != namingServer) { - namingServer.terminate(); - } - } - - @Test - public void contextLoads() { - assertThat(echoService).isNotNull(); - assertThat(fooService).isNotNull(); - } - - @Test - public void testFeignClient() throws InvocationTargetException { - assertThat(echoService.echo("test")).isEqualTo("echo fallback"); - Utils.sleepUninterrupted(2000); - assertThatThrownBy(() -> { - echoService.echo(null); - }).isInstanceOf(InvocationTargetException.class); - assertThatThrownBy(() -> { - fooService.echo("test"); - }).isInstanceOf(NoFallbackAvailableException.class); - Utils.sleepUninterrupted(2000); - assertThat(barService.bar()).isEqualTo("\"fallback from polaris server\""); - Utils.sleepUninterrupted(2000); - assertThat(bazService.baz()).isEqualTo("\"fallback from polaris server\""); - assertThat(fooService.toString()).isNotEqualTo(echoService.toString()); - assertThat(fooService.hashCode()).isNotEqualTo(echoService.hashCode()); - assertThat(echoService.equals(fooService)).isEqualTo(Boolean.FALSE); - } - - @FeignClient(value = TEST_SERVICE_NAME, contextId = "1", fallback = EchoServiceFallback.class) - @Primary - public interface EchoService { - - @RequestMapping(path = "echo/{str}") - String echo(@RequestParam("str") String param) throws InvocationTargetException; - - } - - @FeignClient(value = TEST_SERVICE_NAME, contextId = "2", fallbackFactory = CustomFallbackFactory.class) - public interface FooService { - - @RequestMapping("echo/{str}") - String echo(@RequestParam("str") String param); - - } - - @FeignClient(value = TEST_SERVICE_NAME, contextId = "3") - public interface BarService { - - @RequestMapping(path = "bar") - String bar(); - - } - - public interface BazService { - - @RequestMapping(path = "baz") - String baz(); - - } - - @FeignClient(value = TEST_SERVICE_NAME, contextId = "4") - public interface BazClient extends BazService { - - } - - @Configuration - @EnableAutoConfiguration - @ImportAutoConfiguration({PolarisCircuitBreakerFeignClientAutoConfiguration.class}) - @EnableFeignClients - public static class TestConfig { - - @Bean - public EchoServiceFallback echoServiceFallback() { - return new EchoServiceFallback(); - } - - @Bean - public CustomFallbackFactory customFallbackFactory() { - return new CustomFallbackFactory(); - } - } - - public static class EchoServiceFallback implements EchoService { - - @Override - public String echo(@RequestParam("str") String param) throws InvocationTargetException { - if (param == null) { - throw new InvocationTargetException(new Exception(), "test InvocationTargetException"); - } - return "echo fallback"; - } - - } - - public static class FooServiceFallback implements FooService { - - @Override - public String echo(@RequestParam("str") String param) { - throw new NoFallbackAvailableException("fallback", new RuntimeException()); - } - - } - - public static class CustomFallbackFactory - implements FallbackFactory { - - private final 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/feign/PolarisFeignCircuitBreakerTargeterTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerTargeterTest.java index df15d93de6..94ed5e849b 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerTargeterTest.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignCircuitBreakerTargeterTest.java @@ -50,13 +50,13 @@ public class PolarisFeignCircuitBreakerTargeterTest { @Test public void testTarget() { - PolarisFeignCircuitBreakerTargeter targeter = new PolarisFeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerNameResolver); + PolarisFeignCircuitBreakerTargeter targeter = new PolarisFeignCircuitBreakerTargeter(); targeter.target(new FeignClientFactoryBean(), new Feign.Builder(), new FeignContext(), new Target.HardCodedTarget<>(TestApi.class, "/test")); } @Test public void testTarget2() { - PolarisFeignCircuitBreakerTargeter targeter = new PolarisFeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerNameResolver); + PolarisFeignCircuitBreakerTargeter targeter = new PolarisFeignCircuitBreakerTargeter(); FeignClientFactoryBean feignClientFactoryBean = mock(FeignClientFactoryBean.class); doReturn(TestApi.class).when(feignClientFactoryBean).getFallback(); doReturn("test").when(feignClientFactoryBean).getName(); @@ -69,7 +69,7 @@ public void testTarget2() { @Test public void testTarget3() { - PolarisFeignCircuitBreakerTargeter targeter = new PolarisFeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerNameResolver); + PolarisFeignCircuitBreakerTargeter targeter = new PolarisFeignCircuitBreakerTargeter(); FeignClientFactoryBean feignClientFactoryBean = mock(FeignClientFactoryBean.class); doReturn(void.class).when(feignClientFactoryBean).getFallback(); doReturn(TestApi.class).when(feignClientFactoryBean).getFallbackFactory(); diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/gateway/PolarisCircuitBreakerGatewayIntegrationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/gateway/PolarisCircuitBreakerGatewayIntegrationTest.java deleted file mode 100644 index c4263491ed..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/gateway/PolarisCircuitBreakerGatewayIntegrationTest.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; - -import com.google.protobuf.util.JsonFormat; -import com.tencent.cloud.polaris.context.PolarisSDKContextManager; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.client.util.Utils; -import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto; -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 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.gateway.filter.factory.SpringCloudCircuitBreakerFilterFactory; -import org.springframework.cloud.gateway.route.RouteLocator; -import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; -import org.springframework.context.ApplicationContext; -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.tencent.polaris.test.common.Consts.NAMESPACE_TEST; -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=" + NAMESPACE_TEST, - "spring.cloud.polaris.service=test", - "spring.main.web-application-type=reactive" - }, - classes = PolarisCircuitBreakerGatewayIntegrationTest.TestApplication.class -) -@ActiveProfiles("test-gateway") -@AutoConfigureWebTestClient(timeout = "1000000") -public class PolarisCircuitBreakerGatewayIntegrationTest { - - private static final String TEST_SERVICE_NAME = "test-service-callee"; - - private static NamingServer namingServer; - - @Autowired - private WebTestClient webClient; - - @Autowired - private ApplicationContext applicationContext; - - @BeforeAll - static void beforeAll() throws Exception { - PolarisSDKContextManager.innerDestroy(); - namingServer = NamingServer.startNamingServer(10081); - ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME); - - CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder(); - InputStream inputStream = PolarisCircuitBreakerGatewayIntegrationTest.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 - static void afterAll() { - if (null != namingServer) { - namingServer.terminate(); - } - } - - @Test - public void fallback() throws Exception { - SpringCloudCircuitBreakerFilterFactory.Config config = new SpringCloudCircuitBreakerFilterFactory.Config(); - applicationContext.getBean(PolarisCircuitBreakerFilterFactory.class).apply(config).toString(); - - webClient - .get().uri("/err") - .header("Host", "www.circuitbreaker.com") - .exchange() - .expectStatus().isOk() - .expectBody() - .consumeWith( - response -> assertThat(response.getResponseBody()).isEqualTo("fallback".getBytes())); - - Utils.sleepUninterrupted(2000); - - webClient - .get().uri("/err-skip-fallback") - .header("Host", "www.circuitbreaker-skip-fallback.com") - .exchange() - .expectStatus(); - - Utils.sleepUninterrupted(2000); - - // this should be 200, but for some unknown reason, GitHub action run failed in windows, so we skip this check - webClient - .get().uri("/err-skip-fallback") - .header("Host", "www.circuitbreaker-skip-fallback.com") - .exchange() - .expectStatus(); - - Utils.sleepUninterrupted(2000); - - webClient - .get().uri("/err-no-fallback") - .header("Host", "www.circuitbreaker-no-fallback.com") - .exchange() - .expectStatus(); - - Utils.sleepUninterrupted(2000); - - webClient - .get().uri("/err-no-fallback") - .header("Host", "www.circuitbreaker-no-fallback.com") - .exchange() - .expectStatus(); - - Utils.sleepUninterrupted(2000); - - webClient - .get().uri("/err-no-fallback") - .header("Host", "www.circuitbreaker-no-fallback.com") - .exchange() - .expectStatus(); - } - - - @Configuration - @EnableAutoConfiguration - public static class TestApplication { - - @Bean - public RouteLocator myRoutes(RouteLocatorBuilder builder) { - 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") - .setName(TEST_SERVICE_NAME) - )) - .uri("http://httpbin.org:80")) - .route(p -> p - .host("*.circuitbreaker-skip-fallback.com") - .filters(f -> f - .circuitBreaker(config -> config - .setStatusCodes(Collections.singleton("5**")) - .setName(TEST_SERVICE_NAME) - )) - .uri("http://httpbin.org:80")) - .route(p -> p - .host("*.circuitbreaker-no-fallback.com") - .filters(f -> f - .circuitBreaker(config -> config - .setName(TEST_SERVICE_NAME) - )) - .uri("lb://" + TEST_SERVICE_NAME)) - .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/reporter/CircuitBreakerPluginTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPluginTest.java new file mode 100644 index 0000000000..9e777c5c25 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPluginTest.java @@ -0,0 +1,171 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 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.reporter; + +import java.net.URI; +import java.util.HashMap; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreaker; +import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.circuitbreak.api.InvokeHandler; +import com.tencent.polaris.circuitbreak.api.pojo.InvokeContext; +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * CircuitBreakerPluginTest. + * + * @author Shedfree Wu + */ +@ExtendWith(MockitoExtension.class) +public class CircuitBreakerPluginTest { + + private static MockedStatic mockedApplicationContextAwareUtils; + @InjectMocks + private CircuitBreakerPlugin circuitBreakerPlugin; + @Mock + private CircuitBreakAPI circuitBreakAPI; + @Mock + private CircuitBreakerFactory circuitBreakerFactory; + + @Mock + private ConsumerAPI consumerAPI; + + + @BeforeAll + static void beforeAll() { + mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn("unit-test"); + } + + @AfterAll + static void afterAll() { + mockedApplicationContextAwareUtils.close(); + } + + @BeforeEach + void setUp() { + MetadataContext.LOCAL_NAMESPACE = NAMESPACE_TEST; + MetadataContext.LOCAL_SERVICE = SERVICE_PROVIDER; + } + + @Test + public void testGetName() { + assertThat(circuitBreakerPlugin.getName()).isEqualTo(CircuitBreakerPlugin.class.getName()); + } + + @Test + public void testType() { + assertThat(circuitBreakerPlugin.getType()).isEqualTo(EnhancedPluginType.Client.PRE); + } + + @Test + public void testRun() throws Throwable { + when(circuitBreakAPI.makeInvokeHandler(any())).thenReturn(new MockInvokeHandler()); + + PolarisCircuitBreakerConfigBuilder polarisCircuitBreakerConfigBuilder = new PolarisCircuitBreakerConfigBuilder(); + PolarisCircuitBreaker polarisCircuitBreaker = new PolarisCircuitBreaker(polarisCircuitBreakerConfigBuilder.build(), consumerAPI, circuitBreakAPI); + when(circuitBreakerFactory.create(anyString())).thenReturn(polarisCircuitBreaker); + + + EnhancedPluginContext pluginContext = new EnhancedPluginContext(); + EnhancedRequestContext request = EnhancedRequestContext.builder() + .httpMethod(HttpMethod.GET) + .url(URI.create("http://0.0.0.0/")) + .httpHeaders(new HttpHeaders()) + .build(); + EnhancedResponseContext response = EnhancedResponseContext.builder() + .httpStatus(200) + .build(); + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(SERVICE_PROVIDER); + + pluginContext.setRequest(request); + pluginContext.setResponse(response); + pluginContext.setTargetServiceInstance(serviceInstance, null); + pluginContext.setThrowable(new RuntimeException()); + + assertThatThrownBy(() -> circuitBreakerPlugin.run(pluginContext)).isExactlyInstanceOf(CallAbortedException.class); + circuitBreakerPlugin.getOrder(); + circuitBreakerPlugin.getName(); + circuitBreakerPlugin.getType(); + } + + @Test + public void testHandlerThrowable() { + // mock request + EnhancedRequestContext request = mock(EnhancedRequestContext.class); + // mock response + EnhancedResponseContext response = mock(EnhancedResponseContext.class); + + EnhancedPluginContext context = new EnhancedPluginContext(); + context.setRequest(request); + context.setResponse(response); + circuitBreakerPlugin.handlerThrowable(context, new RuntimeException("Mock exception.")); + } + + static class MockInvokeHandler implements InvokeHandler { + @Override + public void acquirePermission() { + throw new CallAbortedException("mock", new CircuitBreakerStatus.FallbackInfo(0, new HashMap<>(), "")); + } + + @Override + public void onSuccess(InvokeContext.ResponseContext responseContext) { + + } + + @Override + public void onError(InvokeContext.ResponseContext responseContext) { + + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/ExceptionCircuitBreakerReporterTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/ExceptionCircuitBreakerReporterTest.java index 4f2382c9f6..487978f8ff 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/ExceptionCircuitBreakerReporterTest.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/ExceptionCircuitBreakerReporterTest.java @@ -19,8 +19,11 @@ import java.net.URI; +import com.tencent.cloud.common.constant.ContextConstant; import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreaker; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; @@ -28,6 +31,7 @@ import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.metadata.core.MetadataType; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -69,6 +73,8 @@ public class ExceptionCircuitBreakerReporterTest { private ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter; @Mock private CircuitBreakAPI circuitBreakAPI; + @Mock + private PolarisCircuitBreaker polarisCircuitBreaker; @BeforeAll static void beforeAll() { @@ -142,4 +148,66 @@ public void testHandlerThrowable() { context.setResponse(response); exceptionCircuitBreakerReporter.handlerThrowable(context, new RuntimeException("Mock exception.")); } + + @Test + public void testExistCircuitBreaker() throws Throwable { + + doReturn(true).when(reporterProperties).isEnabled(); + + EnhancedPluginContext pluginContext = new EnhancedPluginContext(); + EnhancedRequestContext request = EnhancedRequestContext.builder() + .httpMethod(HttpMethod.GET) + .url(URI.create("http://0.0.0.0/")) + .build(); + EnhancedResponseContext response = EnhancedResponseContext.builder() + .httpStatus(300) + .build(); + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(SERVICE_PROVIDER); + + pluginContext.setRequest(request); + pluginContext.setResponse(response); + pluginContext.setTargetServiceInstance(serviceInstance, null); + pluginContext.setThrowable(new RuntimeException()); + + MetadataContextHolder.get().getMetadataContainer(MetadataType.APPLICATION, true). + putMetadataObjectValue(ContextConstant.CircuitBreaker.POLARIS_CIRCUIT_BREAKER, polarisCircuitBreaker); + MetadataContextHolder.get().getMetadataContainer(MetadataType.APPLICATION, true). + putMetadataObjectValue(ContextConstant.CircuitBreaker.CIRCUIT_BREAKER_START_TIME, System.currentTimeMillis()); + + exceptionCircuitBreakerReporter.run(pluginContext); + + response = EnhancedResponseContext.builder() + .httpStatus(500) + .build(); + pluginContext.setResponse(response); + exceptionCircuitBreakerReporter.run(pluginContext); + } + + @Test + public void testExistCircuitBreaker2() throws Throwable { + + doReturn(true).when(reporterProperties).isEnabled(); + + EnhancedPluginContext pluginContext = new EnhancedPluginContext(); + EnhancedRequestContext request = EnhancedRequestContext.builder() + .httpMethod(HttpMethod.GET) + .url(URI.create("http://0.0.0.0/")) + .build(); + EnhancedResponseContext response = EnhancedResponseContext.builder() + .httpStatus(300) + .build(); + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(SERVICE_PROVIDER); + + pluginContext.setRequest(request); + pluginContext.setResponse(response); + pluginContext.setTargetServiceInstance(serviceInstance, null); + pluginContext.setThrowable(new RuntimeException()); + // not exist circuit CIRCUIT_BREAKER_START_TIME + MetadataContextHolder.get().getMetadataContainer(MetadataType.APPLICATION, true). + putMetadataObjectValue(ContextConstant.CircuitBreaker.POLARIS_CIRCUIT_BREAKER, polarisCircuitBreaker); + + exceptionCircuitBreakerReporter.run(pluginContext); + } } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/SuccessCircuitBreakerReporterTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/SuccessCircuitBreakerReporterTest.java index e6a227beee..4545e7cf1d 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/SuccessCircuitBreakerReporterTest.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/SuccessCircuitBreakerReporterTest.java @@ -17,10 +17,16 @@ package com.tencent.cloud.polaris.circuitbreaker.reporter; +import java.lang.reflect.Field; import java.net.URI; +import com.tencent.cloud.common.constant.ContextConstant; import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.StaticMetadataManager; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreaker; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; @@ -28,6 +34,7 @@ import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.metadata.core.MetadataType; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -62,16 +69,18 @@ public class SuccessCircuitBreakerReporterTest { private static MockedStatic mockedApplicationContextAwareUtils; @Mock - private SDKContext sdkContext; - @Mock private RpcEnhancementReporterProperties reporterProperties; + @Mock + private SDKContext sdkContext; @InjectMocks private SuccessCircuitBreakerReporter successCircuitBreakerReporter; @Mock private CircuitBreakAPI circuitBreakAPI; + @Mock + private PolarisCircuitBreaker polarisCircuitBreaker; @BeforeAll - static void beforeAll() { + static void beforeAll() throws Exception { mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) .thenReturn("unit-test"); @@ -81,6 +90,12 @@ static void beforeAll() { .when(applicationContext).getBean(RpcEnhancementReporterProperties.class); mockedApplicationContextAwareUtils.when(ApplicationContextAwareUtils::getApplicationContext) .thenReturn(applicationContext); + + StaticMetadataManager metadataManager = new StaticMetadataManager(new MetadataLocalProperties(), null); + + Field field = MetadataContextHolder.class.getDeclaredField("staticMetadataManager"); + field.setAccessible(true); + field.set(null, metadataManager); } @AfterAll @@ -147,4 +162,64 @@ public void testHandlerThrowable() { context.setResponse(response); successCircuitBreakerReporter.handlerThrowable(context, new RuntimeException("Mock exception.")); } + + @Test + public void testExistCircuitBreaker() throws Throwable { + + doReturn(true).when(reporterProperties).isEnabled(); + + EnhancedPluginContext pluginContext = new EnhancedPluginContext(); + EnhancedRequestContext request = EnhancedRequestContext.builder() + .httpMethod(HttpMethod.GET) + .url(URI.create("http://0.0.0.0/")) + .build(); + EnhancedResponseContext response = EnhancedResponseContext.builder() + .httpStatus(300) + .build(); + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(SERVICE_PROVIDER); + + pluginContext.setRequest(request); + pluginContext.setResponse(response); + pluginContext.setTargetServiceInstance(serviceInstance, null); + + MetadataContextHolder.get().getMetadataContainer(MetadataType.APPLICATION, true). + putMetadataObjectValue(ContextConstant.CircuitBreaker.POLARIS_CIRCUIT_BREAKER, polarisCircuitBreaker); + MetadataContextHolder.get().getMetadataContainer(MetadataType.APPLICATION, true). + putMetadataObjectValue(ContextConstant.CircuitBreaker.CIRCUIT_BREAKER_START_TIME, System.currentTimeMillis()); + + successCircuitBreakerReporter.run(pluginContext); + + response = EnhancedResponseContext.builder() + .httpStatus(500) + .build(); + pluginContext.setResponse(response); + successCircuitBreakerReporter.run(pluginContext); + } + + @Test + public void testExistCircuitBreaker2() throws Throwable { + + doReturn(true).when(reporterProperties).isEnabled(); + + EnhancedPluginContext pluginContext = new EnhancedPluginContext(); + EnhancedRequestContext request = EnhancedRequestContext.builder() + .httpMethod(HttpMethod.GET) + .url(URI.create("http://0.0.0.0/")) + .build(); + EnhancedResponseContext response = EnhancedResponseContext.builder() + .httpStatus(300) + .build(); + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(SERVICE_PROVIDER); + + pluginContext.setRequest(request); + pluginContext.setResponse(response); + pluginContext.setTargetServiceInstance(serviceInstance, null); + // not exist circuit CIRCUIT_BREAKER_START_TIME + MetadataContextHolder.get().getMetadataContainer(MetadataType.APPLICATION, true). + putMetadataObjectValue(ContextConstant.CircuitBreaker.POLARIS_CIRCUIT_BREAKER, polarisCircuitBreaker); + + successCircuitBreakerReporter.run(pluginContext); + } } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateIntegrationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateIntegrationTest.java deleted file mode 100644 index 61e95ae7e7..0000000000 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerRestTemplateIntegrationTest.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.stream.Collectors; - -import com.google.protobuf.util.JsonFormat; -import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration; -import com.tencent.cloud.polaris.context.PolarisSDKContextManager; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.client.util.Utils; -import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto; -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.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.ImportAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.web.client.ExpectedCount; -import org.springframework.test.web.client.MockRestServiceServer; -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; -import org.springframework.web.util.DefaultUriBuilderFactory; - -import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; -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; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; - -/** - * @author sean yu - */ -@ExtendWith(SpringExtension.class) -@SpringBootTest(webEnvironment = RANDOM_PORT, - classes = PolarisCircuitBreakerRestTemplateIntegrationTest.TestConfig.class, - properties = { - "spring.cloud.gateway.enabled=false", - "spring.cloud.polaris.address=grpc://127.0.0.1:10081", - "feign.circuitbreaker.enabled=true", - "spring.cloud.polaris.namespace=" + NAMESPACE_TEST, - "spring.cloud.polaris.service=test" - }) -public class PolarisCircuitBreakerRestTemplateIntegrationTest { - - private static final String TEST_SERVICE_NAME = "test-service-callee"; - - private static NamingServer namingServer; - - @Autowired - @Qualifier("defaultRestTemplate") - private RestTemplate defaultRestTemplate; - - @Autowired - @Qualifier("restTemplateFallbackFromPolaris") - private RestTemplate restTemplateFallbackFromPolaris; - - @Autowired - @Qualifier("restTemplateFallbackFromCode") - private RestTemplate restTemplateFallbackFromCode; - - @Autowired - @Qualifier("restTemplateFallbackFromCode2") - private RestTemplate restTemplateFallbackFromCode2; - - @Autowired - @Qualifier("restTemplateFallbackFromCode3") - private RestTemplate restTemplateFallbackFromCode3; - - @Autowired - @Qualifier("restTemplateFallbackFromCode4") - private RestTemplate restTemplateFallbackFromCode4; - - @Autowired - private ApplicationContext applicationContext; - - @BeforeAll - static void beforeAll() throws Exception { - PolarisSDKContextManager.innerDestroy(); - namingServer = NamingServer.startNamingServer(10081); - ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME); - CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder(); - InputStream inputStream = PolarisCircuitBreakerRestTemplateIntegrationTest.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 - static void afterAll() { - if (null != namingServer) { - namingServer.terminate(); - } - } - - @Test - public void testRestTemplate() throws URISyntaxException { - MockRestServiceServer mockServer = MockRestServiceServer.createServer(defaultRestTemplate); - mockServer - .expect(ExpectedCount.once(), requestTo(new URI("http://localhost:18001/example/service/b/info"))) - .andExpect(method(HttpMethod.GET)) - .andRespond(withStatus(HttpStatus.OK).body("OK")); - assertThat(defaultRestTemplate.getForObject("http://localhost:18001/example/service/b/info", String.class)).isEqualTo("OK"); - mockServer.verify(); - mockServer.reset(); - HttpHeaders headers = new HttpHeaders(); - mockServer - .expect(ExpectedCount.once(), requestTo(new URI("http://localhost:18001/example/service/b/info"))) - .andExpect(method(HttpMethod.GET)) - .andRespond(withStatus(HttpStatus.BAD_GATEWAY).headers(headers).body("BAD_GATEWAY")); - assertThat(defaultRestTemplate.getForObject("http://localhost:18001/example/service/b/info", String.class)).isEqualTo("fallback"); - mockServer.verify(); - mockServer.reset(); - assertThatThrownBy(() -> { - restTemplateFallbackFromPolaris.getForObject("/example/service/b/info", String.class); - }).isInstanceOf(IllegalStateException.class); - assertThat(restTemplateFallbackFromCode.getForObject("/example/service/b/info", String.class)).isEqualTo("\"this is a fallback class\""); - Utils.sleepUninterrupted(2000); - assertThat(restTemplateFallbackFromCode2.getForObject("/example/service/b/info", String.class)).isEqualTo("\"this is a fallback class\""); - Utils.sleepUninterrupted(2000); - assertThat(restTemplateFallbackFromCode3.getForEntity("/example/service/b/info", String.class) - .getStatusCode()).isEqualTo(HttpStatus.OK); - Utils.sleepUninterrupted(2000); - assertThat(restTemplateFallbackFromCode4.getForObject("/example/service/b/info", String.class)).isEqualTo("fallback"); - Utils.sleepUninterrupted(2000); - assertThat(restTemplateFallbackFromPolaris.getForObject("/example/service/b/info", String.class)).isEqualTo("\"fallback from polaris server\""); - // just for code coverage - PolarisCircuitBreakerHttpResponse response = ((CustomPolarisCircuitBreakerFallback) applicationContext.getBean("customPolarisCircuitBreakerFallback")).fallback(); - assertThat(response.getStatusText()).isEqualTo("OK"); - assertThat(response.getFallbackInfo().getCode()).isEqualTo(200); - } - - @Configuration - @EnableAutoConfiguration - @ImportAutoConfiguration({PolarisCircuitBreakerFeignClientAutoConfiguration.class}) - @EnableFeignClients - public static class TestConfig { - - @Bean - @com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker(fallback = "fallback") - public RestTemplate defaultRestTemplate() { - return new RestTemplate(); - } - - @Bean - @LoadBalanced - @com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker - public RestTemplate restTemplateFallbackFromPolaris() { - DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME); - RestTemplate restTemplate = new RestTemplate(); - restTemplate.setUriTemplateHandler(uriBuilderFactory); - return restTemplate; - } - - @Bean - @LoadBalanced - @com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback.class) - public RestTemplate restTemplateFallbackFromCode() { - DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME); - RestTemplate restTemplate = new RestTemplate(); - restTemplate.setUriTemplateHandler(uriBuilderFactory); - return restTemplate; - } - - @Bean - @LoadBalanced - @com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback2.class) - public RestTemplate restTemplateFallbackFromCode2() { - DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME); - RestTemplate restTemplate = new RestTemplate(); - restTemplate.setUriTemplateHandler(uriBuilderFactory); - return restTemplate; - } - - @Bean - @LoadBalanced - @com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback3.class) - public RestTemplate restTemplateFallbackFromCode3() { - DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME); - RestTemplate restTemplate = new RestTemplate(); - restTemplate.setUriTemplateHandler(uriBuilderFactory); - return restTemplate; - } - - @Bean - @LoadBalanced - @PolarisCircuitBreaker(fallback = "fallback") - public RestTemplate restTemplateFallbackFromCode4() { - DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME); - RestTemplate restTemplate = new RestTemplate(); - restTemplate.setUriTemplateHandler(uriBuilderFactory); - return restTemplate; - } - - @Bean - public CustomPolarisCircuitBreakerFallback customPolarisCircuitBreakerFallback() { - return new CustomPolarisCircuitBreakerFallback(); - } - - @Bean - public CustomPolarisCircuitBreakerFallback2 customPolarisCircuitBreakerFallback2() { - return new CustomPolarisCircuitBreakerFallback2(); - } - - @Bean - public CustomPolarisCircuitBreakerFallback3 customPolarisCircuitBreakerFallback3() { - return new CustomPolarisCircuitBreakerFallback3(); - } - - @RestController - @RequestMapping("/example/service/b") - public class ServiceBController { - - /** - * Get service information. - * - * @return service information - */ - @GetMapping("/info") - public String info() { - return "hello world ! I'm a service B1"; - } - - } - - } - - public static class CustomPolarisCircuitBreakerFallback implements PolarisCircuitBreakerFallback { - @Override - public PolarisCircuitBreakerHttpResponse fallback() { - return new PolarisCircuitBreakerHttpResponse( - 200, - new HashMap() {{ - put("xxx", "xxx"); - }}, - "\"this is a fallback class\""); - } - } - - public static class CustomPolarisCircuitBreakerFallback2 implements PolarisCircuitBreakerFallback { - @Override - public PolarisCircuitBreakerHttpResponse fallback() { - return new PolarisCircuitBreakerHttpResponse( - 200, - "\"this is a fallback class\"" - ); - } - } - - public static class CustomPolarisCircuitBreakerFallback3 implements PolarisCircuitBreakerFallback { - @Override - public PolarisCircuitBreakerHttpResponse fallback() { - return new PolarisCircuitBreakerHttpResponse( - 200 - ); - } - } -} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java index 1aef247d64..a99f99fb66 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java @@ -84,7 +84,7 @@ public List getServices() throws PolarisException { return Collections.emptyList(); } return polarisDiscoveryHandler.getServices().getServices().stream() - .map(ServiceInfo::getService).collect(Collectors.toList()); + .map(ServiceInfo::getService).distinct().collect(Collectors.toList()); } } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java index 8a0c471fda..9a0f999366 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java @@ -57,7 +57,7 @@ public class PolarisRegistration implements Registration { private final StaticMetadataManager staticMetadataManager; - private final String serviceId; + private String serviceId; private final String host; private final boolean isSecure; private final ServletWebServerApplicationContext servletWebServerApplicationContext; @@ -164,6 +164,10 @@ public void customize() { } } + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + @Override public String getServiceId() { return serviceId; diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java index bb07733a02..ff4c87c4b1 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java @@ -23,6 +23,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.StaticMetadataManager; import com.tencent.cloud.common.util.OkHttpUtil; import com.tencent.cloud.common.util.OtUtils; @@ -106,6 +107,7 @@ public void register(PolarisRegistration registration) { } registration.customize(); String serviceId = registration.getServiceId(); + MetadataContext.setLocalService(serviceId); // Register instance. InstanceRegisterRequest instanceRegisterRequest = new InstanceRegisterRequest(); diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories index dc65b01e03..036bfd4641 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories @@ -3,7 +3,6 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration,\ com.tencent.cloud.polaris.registry.PolarisServiceRegistryAutoConfiguration,\ com.tencent.cloud.polaris.endpoint.PolarisDiscoveryEndpointAutoConfiguration,\ - com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerAutoConfiguration,\ com.tencent.cloud.polaris.tsf.registry.TsfDiscoveryRegistryAutoConfiguration org.springframework.cloud.bootstrap.BootstrapConfiguration=\ com.tencent.cloud.polaris.DiscoveryPropertiesBootstrapAutoConfiguration diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java index f8e1c2e98d..f9a6eb77aa 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java @@ -17,7 +17,10 @@ package com.tencent.cloud.plugin.lossless; +import java.lang.reflect.Field; +import java.net.URI; import java.util.Collections; +import java.util.Map; import com.tencent.cloud.common.util.OkHttpUtil; import com.tencent.cloud.plugin.lossless.config.LosslessAutoConfiguration; @@ -28,6 +31,7 @@ import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; import com.tencent.cloud.polaris.registry.PolarisRegistration; import com.tencent.cloud.polaris.registry.PolarisServiceRegistry; +import com.tencent.cloud.rpc.enhancement.transformer.RegistrationTransformer; import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.test.mock.discovery.NamingServer; import org.junit.jupiter.api.AfterAll; @@ -40,6 +44,9 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationUtils; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; @@ -106,6 +113,31 @@ public class LosslessRegistryAspectTest { .withPropertyValues("spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST) .withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx"); + private final WebApplicationContextRunner contextRunner3 = new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + MockDiscoveryConfiguration.class, + LosslessAutoConfiguration.class, + LosslessPropertiesBootstrapConfiguration.class, + PolarisContextAutoConfiguration.class, + PolarisPropertiesConfiguration.class, + PolarisDiscoveryClientConfiguration.class, + PolarisDiscoveryAutoConfiguration.class)) + .withPropertyValues("spring.cloud.nacos.discovery.enabled=false") + .withPropertyValues("spring.cloud.polaris.lossless.enabled=true") + .withPropertyValues("spring.cloud.polaris.lossless.healthCheckInterval=1000") + .withPropertyValues("spring.cloud.polaris.lossless.healthCheckPath=/test") + .withPropertyValues("spring.cloud.polaris.admin.port=28082") + .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) + .withPropertyValues("server.port=" + APPLICATION_PORT) + .withPropertyValues("spring.cloud.polaris.localIpAddress=" + HOST) + .withPropertyValues("spring.cloud.polaris.localPort=" + APPLICATION_PORT) + .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") + .withPropertyValues("spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST) + .withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx") + .withPropertyValues("spring.autoconfigure.exclude=" + + "org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration"); + + @BeforeAll static void beforeAll() throws Exception { namingServer = NamingServer.startNamingServer(10081); @@ -185,9 +217,121 @@ public void testRegister2() { }); } + @Test + public void testRegister3() { + this.contextRunner3.run(context -> { + + AbstractAutoServiceRegistration autoServiceRegistration = context.getBean(AbstractAutoServiceRegistration.class); + + assertThatCode(() -> { + AutoServiceRegistrationUtils.register(autoServiceRegistration); + }).doesNotThrowAnyException(); + + Thread.sleep(2000); + + assertThatCode(() -> { + AutoServiceRegistrationUtils.deRegister(autoServiceRegistration); + }).doesNotThrowAnyException(); + + LosslessRegistryAspect losslessRegistryAspect = context.getBean(LosslessRegistryAspect.class); + Field field = LosslessRegistryAspect.class.getDeclaredField("registrationTransformer"); + field.setAccessible(true); + RegistrationTransformer registrationTransformer = (RegistrationTransformer) field.get(losslessRegistryAspect); + assertThat(registrationTransformer.getClass().getName().contains("PolarisRegistrationTransformer")); + + field = LosslessRegistryAspect.class.getDeclaredField("registration"); + field.setAccessible(true); + Registration registration = (Registration) field.get(losslessRegistryAspect); + assertThat(registration.getClass().getName().contains("PolarisRegistration")); + + field = LosslessRegistryAspect.class.getDeclaredField("serviceRegistry"); + field.setAccessible(true); + ServiceRegistry serviceRegistry = (ServiceRegistry) field.get(losslessRegistryAspect); + assertThat(serviceRegistry.getClass().getName().contains("PolarisServiceRegistry")); + }); + } + @Configuration @EnableAutoConfiguration static class PolarisPropertiesConfiguration { } + + @Configuration + static class MockDiscoveryConfiguration { + @Bean + public ServiceRegistry mockServiceRegistry() { + return new ServiceRegistry() { + @Override + public void register(Registration registration) { + + } + + @Override + public void deregister(Registration registration) { + + } + + @Override + public void close() { + + } + + @Override + public void setStatus(Registration registration, String status) { + + } + + @Override + public Object getStatus(Registration registration) { + return null; + } + }; + } + + @Bean + public Registration mockRegistration() { + return new Registration() { + @Override + public String getServiceId() { + return null; + } + + @Override + public String getHost() { + return null; + } + + @Override + public int getPort() { + return 0; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public URI getUri() { + return null; + } + + @Override + public Map getMetadata() { + return null; + } + }; + } + + @Bean + public RegistrationTransformer mockRegistrationTransformer() { + return new RegistrationTransformer() { + @Override + public String getRegistry() { + return null; + } + }; + } + } } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java index 5ce4131a96..dd7c26ae03 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java @@ -131,7 +131,6 @@ public Server choose(Object key) { } PolarisLoadBalancer polarisLoadBalancer = (PolarisLoadBalancer) loadBalancer; - // 1. get all servers from polaris client List allServers = polarisLoadBalancer.getReachableServersWithoutCache(); if (CollectionUtils.isEmpty(allServers)) { @@ -196,6 +195,8 @@ List doRouter(List allServers, PolarisRouterContext routerContex filteredInstances.add(new PolarisServer(serviceInstances, instance)); } + filteredInstances.addAll(LoadBalancerUtils.filterNonPolarisServers(allServers)); + return filteredInstances; } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java index e8d476e5dd..45cbe9f443 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java @@ -35,7 +35,7 @@ */ public class PolarisRouterContext { - private Map> labels; + private Map> labels = new HashMap<>(); public Map getLabels(String labelType) { if (CollectionUtils.isEmpty(labels)) { diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java index 8cd059caf0..d4dc73ac98 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java @@ -19,6 +19,7 @@ import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerInterceptor; import com.tencent.cloud.polaris.router.resttemplate.RouterContextFactory; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; @@ -27,6 +28,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor; import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory; +import org.springframework.core.Ordered; import org.springframework.lang.NonNull; /** @@ -35,7 +37,11 @@ * * @author lepdou 2022-05-18 */ -public class LoadBalancerInterceptorBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { +public class LoadBalancerInterceptorBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware, Ordered { + /** + * The order of the bean post processor. if user want to wrap it(CustomLoadBalancerInterceptor -> PolarisLoadBalancerInterceptor), CustomLoadBalancerInterceptorBeanPostProcessor's order should be bigger than ${@link POLARIS_LOAD_BALANCER_INTERCEPTOR_POST_PROCESSOR_ORDER}. + */ + public static final int POLARIS_LOAD_BALANCER_INTERCEPTOR_POST_PROCESSOR_ORDER = 0; private BeanFactory factory; @@ -52,9 +58,14 @@ public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull Str LoadBalancerRequestFactory requestFactory = this.factory.getBean(LoadBalancerRequestFactory.class); LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class); RouterContextFactory routerContextFactory = this.factory.getBean(RouterContextFactory.class); - - return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory, routerContextFactory); + EnhancedPluginRunner pluginRunner = this.factory.getBean(EnhancedPluginRunner.class); + return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory, routerContextFactory, pluginRunner); } return bean; } + + @Override + public int getOrder() { + return POLARIS_LOAD_BALANCER_INTERCEPTOR_POST_PROCESSOR_ORDER; + } } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/PolarisLoadBalancerCompositeRuleBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/PolarisLoadBalancerCompositeRuleBeanPostProcessor.java index fe0d613ada..af7e272ca9 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/PolarisLoadBalancerCompositeRuleBeanPostProcessor.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/PolarisLoadBalancerCompositeRuleBeanPostProcessor.java @@ -33,6 +33,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.core.Ordered; import org.springframework.lang.NonNull; /** @@ -40,7 +41,12 @@ * * @author derekyi 2022-08-01 */ -public class PolarisLoadBalancerCompositeRuleBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { +public class PolarisLoadBalancerCompositeRuleBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware, Ordered { + + /** + * The order of the bean post processor. if user want to wrap it(CustomRule -> PolarisLoadBalancerCompositeRule), CustomRuleBeanPostProcessor's order should be bigger than ${@link POLARIS_LOAD_BALANCER_COMPOSITE_RULE_POST_PROCESSOR_ORDER}. + */ + public static final int POLARIS_LOAD_BALANCER_COMPOSITE_RULE_POST_PROCESSOR_ORDER = 0; private BeanFactory beanFactory; @@ -64,4 +70,9 @@ public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull Str public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } + + @Override + public int getOrder() { + return POLARIS_LOAD_BALANCER_COMPOSITE_RULE_POST_PROCESSOR_ORDER; + } } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/RetryLoadBalancerInterceptorBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/RetryLoadBalancerInterceptorBeanPostProcessor.java index abed67ab46..c29d96d0cb 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/RetryLoadBalancerInterceptorBeanPostProcessor.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/RetryLoadBalancerInterceptorBeanPostProcessor.java @@ -19,6 +19,7 @@ import com.tencent.cloud.polaris.router.resttemplate.PolarisRetryLoadBalancerInterceptor; import com.tencent.cloud.polaris.router.resttemplate.RouterContextFactory; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; @@ -29,6 +30,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory; import org.springframework.cloud.client.loadbalancer.LoadBalancerRetryProperties; import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor; +import org.springframework.core.Ordered; import org.springframework.lang.NonNull; /** @@ -37,7 +39,11 @@ * * @author lepdou 2022-10-09 */ -public class RetryLoadBalancerInterceptorBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { +public class RetryLoadBalancerInterceptorBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware, Ordered { + /** + * The order of the bean post processor. if user want to wrap it(CustomLoadBalancerInterceptor -> PolarisRetryLoadBalancerInterceptor), CustomLoadBalancerInterceptorBeanPostProcessor's order should be bigger than ${@link POLARIS_RETRY_LOAD_BALANCER_INTERCEPTOR_POST_PROCESSOR_ORDER}. + */ + public static final int POLARIS_RETRY_LOAD_BALANCER_INTERCEPTOR_POST_PROCESSOR_ORDER = 0; private BeanFactory factory; @@ -58,10 +64,16 @@ public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull Str LoadBalancerRequestFactory requestFactory = this.factory.getBean(LoadBalancerRequestFactory.class); LoadBalancedRetryFactory lbRetryFactory = this.factory.getBean(LoadBalancedRetryFactory.class); RouterContextFactory routerContextFactory = this.factory.getBean(RouterContextFactory.class); + EnhancedPluginRunner pluginRunner = this.factory.getBean(EnhancedPluginRunner.class); return new PolarisRetryLoadBalancerInterceptor(loadBalancerClient, lbProperties, requestFactory, lbRetryFactory, - routerContextFactory); + routerContextFactory, pluginRunner); } return bean; } + + @Override + public int getOrder() { + return POLARIS_RETRY_LOAD_BALANCER_INTERCEPTOR_POST_PROCESSOR_ORDER; + } } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java index 9e20d4fa6d..7fdf3dc9ba 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java @@ -43,7 +43,8 @@ import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver; import com.tencent.cloud.polaris.router.zuul.PolarisRibbonRoutingFilter; import com.tencent.cloud.polaris.router.zuul.RouterLabelZuulFilter; -import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateInterceptor; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; +import com.tencent.cloud.rpc.enhancement.zuul.EnhancedZuulPluginRunner; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.Autowired; @@ -56,6 +57,7 @@ import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.core.annotation.Order; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.web.client.RestTemplate; @@ -165,9 +167,12 @@ public PolarisRibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper, RibbonCommandFactory ribbonCommandFactory, StaticMetadataManager staticMetadataManager, RouterRuleLabelResolver routerRuleLabelResolver, - List routerLabelResolvers) { + List routerLabelResolvers, + @Lazy EnhancedPluginRunner pluginRunner) { + EnhancedZuulPluginRunner enhancedZuulPluginRunner = new EnhancedZuulPluginRunner(pluginRunner); + return new PolarisRibbonRoutingFilter(helper, ribbonCommandFactory, staticMetadataManager, - routerRuleLabelResolver, routerLabelResolvers); + routerRuleLabelResolver, routerLabelResolvers, enhancedZuulPluginRunner); } @Bean @@ -198,13 +203,7 @@ public RouterLabelRestTemplateInterceptor routerLabelRestTemplateInterceptor() { public SmartInitializingSingleton addRouterLabelInterceptorForRestTemplate(RouterLabelRestTemplateInterceptor interceptor) { return () -> restTemplates.forEach(restTemplate -> { List list = new ArrayList<>(restTemplate.getInterceptors()); - int addIndex = list.size(); - for (int i = 0; i < list.size(); i++) { - if (list.get(i) instanceof EnhancedRestTemplateInterceptor) { - addIndex = i; - } - } - list.add(addIndex, interceptor); + list.add(interceptor); restTemplate.setInterceptors(list); }); } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java index b10f2645b0..7e69245552 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java @@ -21,6 +21,8 @@ import java.net.URI; import com.tencent.cloud.polaris.router.PolarisRouterContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; +import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateWrapInterceptor; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor; @@ -42,15 +44,18 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor { private final LoadBalancerClient loadBalancer; private final LoadBalancerRequestFactory requestFactory; private final RouterContextFactory routerContextFactory; + + private final EnhancedPluginRunner enhancedPluginRunner; + private final boolean isRibbonLoadBalanceClient; - public PolarisLoadBalancerInterceptor(LoadBalancerClient loadBalancer, - LoadBalancerRequestFactory requestFactory, - RouterContextFactory routerContextFactory) { + public PolarisLoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory, + RouterContextFactory routerContextFactory, EnhancedPluginRunner enhancedPluginRunner) { super(loadBalancer, requestFactory); this.loadBalancer = loadBalancer; this.requestFactory = requestFactory; this.routerContextFactory = routerContextFactory; + this.enhancedPluginRunner = enhancedPluginRunner; this.isRibbonLoadBalanceClient = loadBalancer instanceof RibbonLoadBalancerClient; } @@ -70,9 +75,17 @@ public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttp RouterContextHelper.setRouterContextToRequest(request, routerContext); //3. do loadbalancer and execute request - ClientHttpResponse response = ((RibbonLoadBalancerClient) loadBalancer).execute(peerServiceName, - this.requestFactory.createRequest(request, body, execution), routerContext); - + ClientHttpResponse response; + if (enhancedPluginRunner != null) { + EnhancedRestTemplateWrapInterceptor enhancedRestTemplatePluginRunnerInterceptor = + new EnhancedRestTemplateWrapInterceptor(enhancedPluginRunner, (RibbonLoadBalancerClient) loadBalancer); + response = enhancedRestTemplatePluginRunnerInterceptor.intercept( + request, peerServiceName, this.requestFactory.createRequest(request, body, execution), routerContext); + } + else { + response = ((RibbonLoadBalancerClient) loadBalancer).execute(peerServiceName, + this.requestFactory.createRequest(request, body, execution), routerContext); + } //4. set router context to response RouterContextHelper.setRouterContextToResponse(routerContext, response); return response; diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisRetryLoadBalancerInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisRetryLoadBalancerInterceptor.java index abe73abc26..7bf0e9e206 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisRetryLoadBalancerInterceptor.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisRetryLoadBalancerInterceptor.java @@ -21,6 +21,8 @@ import java.net.URI; import com.tencent.cloud.polaris.router.PolarisRouterContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; +import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateWrapInterceptor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -34,6 +36,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory; import org.springframework.cloud.client.loadbalancer.LoadBalancerRetryProperties; import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpResponse; @@ -64,14 +67,18 @@ public class PolarisRetryLoadBalancerInterceptor extends RetryLoadBalancerInterc private final RouterContextFactory routerContextFactory; + private final EnhancedPluginRunner enhancedPluginRunner; + public PolarisRetryLoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRetryProperties lbProperties, - LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory lbRetryFactory, RouterContextFactory routerContextFactory) { + LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory lbRetryFactory, + RouterContextFactory routerContextFactory, EnhancedPluginRunner enhancedPluginRunner) { super(loadBalancer, lbProperties, requestFactory, lbRetryFactory); this.loadBalancer = loadBalancer; this.lbProperties = lbProperties; this.requestFactory = requestFactory; this.lbRetryFactory = lbRetryFactory; this.routerContextFactory = routerContextFactory; + this.enhancedPluginRunner = enhancedPluginRunner; } @Override @@ -111,8 +118,17 @@ public ClientHttpResponse intercept(final HttpRequest request, final byte[] body } //3. execute request - ClientHttpResponse response = loadBalancer.execute(serviceName, serviceInstance, - requestFactory.createRequest(request, body, execution)); + ClientHttpResponse response; + if (enhancedPluginRunner != null) { + EnhancedRestTemplateWrapInterceptor enhancedRestTemplatePluginRunnerInterceptor = + new EnhancedRestTemplateWrapInterceptor(enhancedPluginRunner, (RibbonLoadBalancerClient) loadBalancer); + response = enhancedRestTemplatePluginRunnerInterceptor.intercept( + request, serviceName, this.requestFactory.createRequest(request, body, execution), routerContext); + } + else { + response = loadBalancer.execute(serviceName, serviceInstance, + requestFactory.createRequest(request, body, execution)); + } //4. set router context to response RouterContextHelper.setRouterContextToResponse(routerContext, response); diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilter.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilter.java index e0e0fd5d70..a36c48b7c8 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilter.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilter.java @@ -37,6 +37,7 @@ import com.tencent.cloud.polaris.router.PolarisRouterContext; import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; import com.tencent.cloud.polaris.router.spi.ServletRouterLabelResolver; +import com.tencent.cloud.rpc.enhancement.zuul.EnhancedZuulPluginRunner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,11 +72,14 @@ public class PolarisRibbonRoutingFilter extends RibbonRoutingFilter implements B private BeanFactory factory; private boolean useServlet31 = true; + private EnhancedZuulPluginRunner enhancedZuulPluginRunner; + public PolarisRibbonRoutingFilter(ProxyRequestHelper helper, RibbonCommandFactory ribbonCommandFactory, StaticMetadataManager staticMetadataManager, RouterRuleLabelResolver routerRuleLabelResolver, - List routerLabelResolvers) { + List routerLabelResolvers, + EnhancedZuulPluginRunner enhancedZuulPluginRunner) { super(helper, ribbonCommandFactory, Collections.emptyList()); this.staticMetadataManager = staticMetadataManager; this.routerRuleLabelResolver = routerRuleLabelResolver; @@ -95,6 +99,7 @@ public PolarisRibbonRoutingFilter(ProxyRequestHelper helper, catch (NoSuchMethodException e) { useServlet31 = false; } + this.enhancedZuulPluginRunner = enhancedZuulPluginRunner; } @@ -188,4 +193,10 @@ private Map getExpressionLabels(HttpServletRequest request, Set< private void init() { this.requestCustomizers = BeanFactoryUtils.getBeans(factory, RibbonRequestCustomizer.class); } + + @Override + public Object run() { + enhancedZuulPluginRunner.run(); + return super.run(); + } } diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java index 3f4c261dad..46fa2d6b39 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java @@ -30,6 +30,7 @@ import com.tencent.cloud.common.util.ApplicationContextAwareUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.polaris.router.PolarisRouterContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -69,6 +70,9 @@ public class PolarisLoadBalancerInterceptorTest { private RibbonLoadBalancerClient loadBalancerClient; @Mock private LoadBalancerRequestFactory loadBalancerRequestFactory; + + @Mock + private EnhancedPluginRunner enhancedPluginRunner; @Mock private RouterContextFactory routerContextFactory; @@ -90,7 +94,7 @@ public void testProxyRibbonLoadBalance() throws Exception { mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) .thenReturn(callerService); - MetadataContext metadataContext = Mockito.mock(MetadataContext.class); + MetadataContext metadataContext = new MetadataContext(); try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) { mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); @@ -100,7 +104,7 @@ public void testProxyRibbonLoadBalance() throws Exception { when(loadBalancerRequestFactory.createRequest(request, null, null)).thenReturn(loadBalancerRequest); PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor = new PolarisLoadBalancerInterceptor(loadBalancerClient, - loadBalancerRequestFactory, routerContextFactory); + loadBalancerRequestFactory, routerContextFactory, enhancedPluginRunner); ClientHttpResponse mockedResponse = new MockClientHttpResponse(new byte[] {}, HttpStatus.OK); when(loadBalancerClient.execute(eq(calleeService), eq(loadBalancerRequest), any(PolarisRouterContext.class))).thenReturn(mockedResponse); @@ -133,7 +137,7 @@ public void testNotProxyRibbonLoadBalance() throws IOException { when(notRibbonLoadBalancerClient.execute(calleeService, loadBalancerRequest)).thenReturn(mockedResponse); PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor = new PolarisLoadBalancerInterceptor( - notRibbonLoadBalancerClient, loadBalancerRequestFactory, routerContextFactory); + notRibbonLoadBalancerClient, loadBalancerRequestFactory, routerContextFactory, enhancedPluginRunner); ClientHttpResponse response = polarisLoadBalancerInterceptor.intercept(request, null, null); diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilterTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilterTest.java index ff6492b789..b6a6fda1cd 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilterTest.java +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilterTest.java @@ -37,6 +37,7 @@ import com.tencent.cloud.polaris.router.PolarisRouterContext; import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; import com.tencent.cloud.polaris.router.spi.ServletRouterLabelResolver; +import com.tencent.cloud.rpc.enhancement.zuul.EnhancedZuulPluginRunner; import okhttp3.OkHttpClient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -95,6 +96,9 @@ public class PolarisRibbonRoutingFilterTest { @Mock private PolarisLoadBalancer polarisLoadBalancer; + @Mock + private EnhancedZuulPluginRunner enhancedZuulPluginRunner; + @BeforeAll static void beforeAll() { mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); @@ -123,7 +127,7 @@ static void afterAll() { public void testGenRouterContext() { PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter(proxyRequestHelper, ribbonCommandFactory, staticMetadataManager, routerRuleLabelResolver, - Lists.newArrayList(routerLabelResolver)); + Lists.newArrayList(routerLabelResolver), enhancedZuulPluginRunner); Map localMetadata = new HashMap<>(); localMetadata.put("env", "blue"); @@ -160,7 +164,7 @@ public void testHttpCallWithoutRouter() { PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter( new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, staticMetadataManager, - routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver)); + routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver), enhancedZuulPluginRunner); MockHttpServletRequest request = new MockHttpServletRequest(); request.setRequestURI("http://" + calleeService + "/users"); @@ -195,7 +199,7 @@ public void testRestCallWithoutRouter() { PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter( new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, staticMetadataManager, - routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver)); + routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver), enhancedZuulPluginRunner); MockHttpServletRequest request = new MockHttpServletRequest(); request.setRequestURI("http://" + calleeService + "/users"); @@ -231,7 +235,7 @@ public void testOkHttpCallWithoutRouter() { PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter( new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, staticMetadataManager, - routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver)); + routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver), enhancedZuulPluginRunner); MockHttpServletRequest request = new MockHttpServletRequest(); request.setRequestURI("http://" + calleeService + "/users"); @@ -266,7 +270,7 @@ public void testHttpCallWithRouter() { PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter( new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, staticMetadataManager, - routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver)); + routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver), enhancedZuulPluginRunner); MockHttpServletRequest request = new MockHttpServletRequest(); request.setRequestURI("http://" + calleeService + "/users"); @@ -301,7 +305,7 @@ public void testRestCallWithRouter() { PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter( new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, staticMetadataManager, - routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver)); + routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver), enhancedZuulPluginRunner); MockHttpServletRequest request = new MockHttpServletRequest(); request.setRequestURI("http://" + calleeService + "/users"); @@ -338,7 +342,7 @@ public void testOkHttpCallWithRouter() { PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter( new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, staticMetadataManager, - routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver)); + routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver), enhancedZuulPluginRunner); MockHttpServletRequest request = new MockHttpServletRequest(); request.setRequestURI("http://" + calleeService + "/users"); diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java index 1823b1c12c..e1acf9d41f 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java @@ -30,6 +30,10 @@ public final class ContextConstant { * Name of Polaris. */ public static final String POLARIS = "POLARIS"; + /** + * Metadata key for target governance namespace. + */ + public static final String POLARIS_GOVERNANCE_NAMESPACE = "POLARIS_GOVERNANCE_NAMESPACE"; /** * SCT Default Charset . @@ -46,26 +50,35 @@ private ContextConstant() { public static final class Zuul { - /** - * polaris circuit breaker. - */ - public static final String POLARIS_CIRCUIT_BREAKER = "PolarisCircuitBreaker"; - /** * timestamp before route. */ public static final String POLARIS_PRE_ROUTE_TIME = "PolarisPreRouteTime"; /** - * is in routing state. + * polaris circuit breaker. */ - public static final String POLARIS_IS_IN_ROUTING_STATE = "PolarisIsInRoutingState"; + public static final String ENHANCED_PLUGIN_CONTEXT = "EnhancedPluginContext"; + private Zuul() { + } + } + + public static final class CircuitBreaker { /** * polaris circuit breaker. */ - public static final String ENHANCED_PLUGIN_CONTEXT = "EnhancedPluginContext"; + public static final String POLARIS_CIRCUIT_BREAKER = "PolarisCircuitBreaker"; + /** + * circuit breaker start time. + */ + public static final String CIRCUIT_BREAKER_START_TIME = "CIRCUIT_BREAKER_START_TIME"; + /** + * circuit breaker fallback http response. + */ + public static final String CIRCUIT_BREAKER_FALLBACK_HTTP_RESPONSE = "CIRCUIT_BREAKER_FALLBACK_HTTP_RESPONSE"; + + private CircuitBreaker() { - private Zuul() { } } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java index fb41763bb2..9af0859d2d 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java @@ -319,4 +319,8 @@ public void putFragmentContext(String fragment, Map context) { break; } } + + public static void setLocalService(String service) { + LOCAL_SERVICE = service; + } } diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java index cfa5538d18..b704ba7839 100644 --- a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java @@ -19,11 +19,9 @@ import com.tencent.cloud.plugin.discovery.adapter.transformer.NacosInstanceTransformer; import com.tencent.cloud.plugin.discovery.adapter.transformer.NacosRegistrationTransformer; -import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerAutoConfiguration; import com.tencent.cloud.polaris.loadbalancer.transformer.InstanceTransformer; import com.tencent.cloud.rpc.enhancement.transformer.RegistrationTransformer; -import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled; @@ -37,7 +35,6 @@ */ @Configuration(proxyBeanMethods = false) @ConditionalOnDiscoveryEnabled -@AutoConfigureBefore(PolarisLoadBalancerAutoConfiguration.class) public class NacosDiscoveryAdapterAutoConfiguration { @Bean @@ -48,7 +45,6 @@ public InstanceTransformer instanceTransformer() { } @Bean - @ConditionalOnMissingBean @ConditionalOnClass(name = "com.alibaba.cloud.nacos.registry.NacosRegistration") public RegistrationTransformer registrationTransformer() { return new NacosRegistrationTransformer(); diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java index 4c902dd2a4..37e4272d13 100644 --- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java @@ -17,6 +17,8 @@ package com.tencent.cloud.plugin.lossless.config; +import java.util.List; + import com.tencent.cloud.plugin.lossless.LosslessRegistryAspect; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import com.tencent.cloud.polaris.context.PolarisSDKContextManager; @@ -43,11 +45,36 @@ public class LosslessAutoConfiguration { @Bean @ConditionalOnMissingBean public LosslessRegistryAspect losslessRegistryAspect( - ServiceRegistry serviceRegistry, Registration registration, PolarisContextProperties properties, - LosslessProperties losslessProperties, PolarisSDKContextManager polarisSDKContextManager, - RegistrationTransformer registrationTransformer) { - return new LosslessRegistryAspect(serviceRegistry, registration, properties, losslessProperties, - polarisSDKContextManager, registrationTransformer); + List serviceRegistryList, List registrationList, List registrationTransformerList, + PolarisContextProperties properties, LosslessProperties losslessProperties, PolarisSDKContextManager polarisSDKContextManager) { + + ServiceRegistry targetServiceRegistry = serviceRegistryList.size() > 0 ? serviceRegistryList.get(0) : null; + Registration targetRegistration = registrationList.size() > 0 ? registrationList.get(0) : null; + RegistrationTransformer targetRegistrationTransformer = registrationTransformerList.size() > 0 ? registrationTransformerList.get(0) : null; + // if contains multiple service registry, find the polaris service registr + if (serviceRegistryList.size() > 1) { + for (ServiceRegistry serviceRegistry : serviceRegistryList) { + if (serviceRegistry.getClass().getName().contains("PolarisServiceRegistry")) { + targetServiceRegistry = serviceRegistry; + } + } + } + if (registrationList.size() > 1) { + for (Registration registration : registrationList) { + if (registration.getClass().getName().contains("PolarisRegistration")) { + targetRegistration = registration; + } + } + } + if (registrationTransformerList.size() > 1) { + for (RegistrationTransformer registrationTransformer : registrationTransformerList) { + if (registrationTransformer.getClass().getName().contains("PolarisRegistrationTransformer")) { + targetRegistrationTransformer = registrationTransformer; + } + } + } + return new LosslessRegistryAspect(targetServiceRegistry, targetRegistration, properties, losslessProperties, + polarisSDKContextManager, targetRegistrationTransformer); } } diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/CircuitBreakerStatusCodeException.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/CircuitBreakerStatusCodeException.java new file mode 100644 index 0000000000..2de2b12d90 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/CircuitBreakerStatusCodeException.java @@ -0,0 +1,29 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 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.context; + +import org.springframework.http.HttpStatus; +import org.springframework.web.client.HttpStatusCodeException; + +public class CircuitBreakerStatusCodeException extends HttpStatusCodeException { + + public CircuitBreakerStatusCodeException(HttpStatus statusCode) { + super(statusCode); + } + +} diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java index 3b643c8c6c..61c2a838cd 100644 --- a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java @@ -57,4 +57,14 @@ public static ServiceInstances transferServersToServiceInstances(List se return new DefaultServiceInstances(serviceKey, instances); } + + public static List filterNonPolarisServers(List servers) { + List result = new ArrayList<>(servers.size()); + for (Server server : servers) { + if (!(server instanceof PolarisServer)) { + result.add(server); + } + } + return result; + } } diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java index 82f9796560..9468322997 100644 --- a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java @@ -23,6 +23,7 @@ import java.util.List; import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ConfigurationBasedServerList; import com.netflix.loadbalancer.DynamicServerListLoadBalancer; import com.netflix.loadbalancer.IPing; import com.netflix.loadbalancer.IRule; @@ -59,6 +60,8 @@ public class PolarisLoadBalancer extends DynamicServerListLoadBalancer { private final PolarisLoadBalancerProperties polarisLoadBalancerProperties; private final InstanceTransformer instanceTransformer; + // when configuring by user, use origin getReachableServers and getAllServers to get instances. + private final boolean isPolarisDefaultServerList; public PolarisLoadBalancer(IClientConfig config, IRule rule, IPing ping, ServerList serverList, ConsumerAPI consumerAPI, PolarisLoadBalancerProperties properties, InstanceTransformer instanceTransformer) { @@ -66,6 +69,8 @@ public PolarisLoadBalancer(IClientConfig config, IRule rule, IPing ping, ServerL this.consumerAPI = consumerAPI; this.polarisLoadBalancerProperties = properties; this.instanceTransformer = instanceTransformer; + // PolarisDiscoveryRibbonAutoConfiguration will not load, so default server list is ConfigurationBasedServerList. + this.isPolarisDefaultServerList = serverList instanceof ConfigurationBasedServerList; } @Override @@ -79,10 +84,18 @@ public List getReachableServers() { if (THREAD_CACHE_SERVERS.get() != null) { return THREAD_CACHE_SERVERS.get(); } - return getReachableServersWithoutCache(); + if (isPolarisDefaultServerList) { + return getReachableServersWithoutCache(); + } + else { + return super.getReachableServers(); + } } public List getReachableServersWithoutCache() { + if (!isPolarisDefaultServerList) { + return super.getReachableServers(); + } ServiceInstances serviceInstances; if (polarisLoadBalancerProperties.getDiscoveryType().equals(ContextConstant.POLARIS)) { serviceInstances = getPolarisDiscoveryServiceInstances(); @@ -142,7 +155,12 @@ private ServiceInstances getExtendDiscoveryServiceInstances() { @Override public List getAllServers() { - return getReachableServers(); + if (isPolarisDefaultServerList) { + return getReachableServers(); + } + else { + return super.getAllServers(); + } } /** diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java index 7a52419217..df9f8df50c 100644 --- a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java @@ -17,25 +17,15 @@ package com.tencent.cloud.polaris.loadbalancer.config; -import java.util.ArrayList; -import java.util.List; - import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; -import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateInterceptor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor; -import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer; -import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; import org.springframework.cloud.netflix.ribbon.RibbonClients; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.util.CollectionUtils; /** * Auto-configuration Ribbon for Polaris. @@ -55,42 +45,4 @@ public PolarisLoadBalancerProperties polarisLoadBalancerProperties() { return new PolarisLoadBalancerProperties(); } - @Bean - public RestTemplateCustomizer polarisRestTemplateCustomizer( - @Autowired(required = false) RetryLoadBalancerInterceptor retryLoadBalancerInterceptor, - @Autowired(required = false) LoadBalancerInterceptor loadBalancerInterceptor) { - return restTemplate -> { - List list = new ArrayList<>(restTemplate.getInterceptors()); - // LoadBalancerInterceptor must invoke before EnhancedRestTemplateInterceptor - int addIndex = list.size(); - if (CollectionUtils.containsInstance(list, retryLoadBalancerInterceptor) || CollectionUtils.containsInstance(list, loadBalancerInterceptor)) { - ClientHttpRequestInterceptor enhancedRestTemplateInterceptor = null; - for (int i = 0; i < list.size(); i++) { - if (list.get(i) instanceof EnhancedRestTemplateInterceptor) { - enhancedRestTemplateInterceptor = list.get(i); - addIndex = i; - } - } - if (enhancedRestTemplateInterceptor != null) { - list.remove(addIndex); - list.add(enhancedRestTemplateInterceptor); - } - } - else { - if (retryLoadBalancerInterceptor != null || loadBalancerInterceptor != null) { - for (int i = 0; i < list.size(); i++) { - if (list.get(i) instanceof EnhancedRestTemplateInterceptor) { - addIndex = i; - } - } - list.add(addIndex, - retryLoadBalancerInterceptor != null - ? retryLoadBalancerInterceptor - : loadBalancerInterceptor); - } - } - restTemplate.setInterceptors(list); - }; - } - } diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerTest.java b/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerTest.java index c490e2bfe2..8cf2443e85 100644 --- a/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerTest.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerTest.java @@ -23,6 +23,7 @@ import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ConfigurationBasedServerList; import com.netflix.loadbalancer.DummyPing; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerList; @@ -103,7 +104,8 @@ public void testPolarisLoadBalancer() { .thenReturn("TestServer"); PolarisLoadBalancerProperties properties = new PolarisLoadBalancerProperties(); - ServerList emptyServerList = new StaticServerList<>(); + ConfigurationBasedServerList emptyServerList = new ConfigurationBasedServerList(); + emptyServerList.initWithNiwsConfig(config); PolarisLoadBalancer balancer = new PolarisLoadBalancer(config, rule, new DummyPing(), emptyServerList, consumerAPI, properties, null); diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java index 74b94684b7..ddcc715c80 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java @@ -32,7 +32,6 @@ import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; import com.tencent.cloud.rpc.enhancement.plugin.reporter.ExceptionPolarisReporter; import com.tencent.cloud.rpc.enhancement.plugin.reporter.SuccessPolarisReporter; -import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateInterceptor; import com.tencent.cloud.rpc.enhancement.resttemplate.PolarisLoadBalancerRequestTransformer; import com.tencent.cloud.rpc.enhancement.scg.EnhancedGatewayGlobalFilter; import com.tencent.cloud.rpc.enhancement.transformer.InstanceTransformer; @@ -44,7 +43,6 @@ import com.tencent.cloud.rpc.enhancement.webclient.RibbonLoadBalancerClientAspect; import com.tencent.cloud.rpc.enhancement.zuul.EnhancedErrorZuulFilter; import com.tencent.cloud.rpc.enhancement.zuul.EnhancedPostZuulFilter; -import com.tencent.cloud.rpc.enhancement.zuul.EnhancedPreZuulFilter; import com.tencent.cloud.rpc.enhancement.zuul.EnhancedRouteZuulFilter; import org.springframework.beans.factory.SmartInitializingSingleton; @@ -96,7 +94,6 @@ public InstanceTransformer instanceTransformer() { } @Bean - @ConditionalOnMissingBean @ConditionalOnClass(name = "com.tencent.cloud.polaris.registry.PolarisRegistration") public RegistrationTransformer registrationTransformer() { return new PolarisRegistrationTransformer(); @@ -106,9 +103,9 @@ public RegistrationTransformer registrationTransformer() { @Lazy public EnhancedPluginRunner enhancedFeignPluginRunner( @Autowired(required = false) List enhancedPlugins, - @Autowired(required = false) Registration registration, + @Autowired(required = false) List registrations, PolarisSDKContextManager polarisSDKContextManager) { - return new DefaultEnhancedPluginRunner(enhancedPlugins, registration, polarisSDKContextManager.getSDKContext()); + return new DefaultEnhancedPluginRunner(enhancedPlugins, registrations, polarisSDKContextManager.getSDKContext()); } @Bean @@ -187,19 +184,6 @@ protected static class PolarisRestTemplateAutoConfiguration { @Autowired(required = false) private List restTemplates = Collections.emptyList(); - @Bean - public EnhancedRestTemplateInterceptor enhancedPolarisRestTemplateReporter(@Lazy EnhancedPluginRunner pluginRunner) { - return new EnhancedRestTemplateInterceptor(pluginRunner); - } - - @Bean - public SmartInitializingSingleton setPolarisReporterForRestTemplate(EnhancedRestTemplateInterceptor reporter) { - return () -> { - for (RestTemplate restTemplate : restTemplates) { - restTemplate.getInterceptors().add(reporter); - } - }; - } @Bean @ConditionalOnMissingBean @@ -272,11 +256,6 @@ public EnhancedGatewayGlobalFilter enhancedPolarisGatewayReporter(@Lazy Enhanced @ConditionalOnClass(name = "com.netflix.zuul.http.ZuulServlet") protected static class PolarisCircuitBreakerZuulFilterConfig { - @Bean - public EnhancedPreZuulFilter enhancedPreZuulFilter(@Lazy EnhancedPluginRunner pluginRunner, Environment environment) { - return new EnhancedPreZuulFilter(pluginRunner, environment); - } - @Bean public EnhancedRouteZuulFilter enhancedZuulRouteFilter(@Lazy EnhancedPluginRunner pluginRunner, Environment environment) { return new EnhancedRouteZuulFilter(pluginRunner, environment); diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java index 8650c36f83..1f912325cc 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java @@ -19,16 +19,24 @@ import java.io.IOException; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; import feign.Client; import feign.Request; import feign.Request.Options; +import feign.RequestTemplate; import feign.Response; import org.springframework.cloud.client.DefaultServiceInstance; @@ -61,16 +69,19 @@ public Response execute(Request request, Options options) throws IOException { request.headers().forEach((s, strings) -> requestHeaders.addAll(s, new ArrayList<>(strings))); URI url = URI.create(request.url()); + URI serviceUrl = url.resolve(request.requestTemplate().url()); + EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder() .httpHeaders(requestHeaders) .httpMethod(HttpMethod.valueOf(request.httpMethod().name())) .url(url) + .serviceUrl(serviceUrl) .build(); enhancedPluginContext.setRequest(enhancedRequestContext); enhancedPluginContext.setOriginRequest(request); enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance()); - String svcName = request.requestTemplate().feignTarget().name(); + String svcName = serviceUrl.getHost(); DefaultServiceInstance serviceInstance = new DefaultServiceInstance( String.format("%s-%s-%d", svcName, url.getHost(), url.getPort()), svcName, url.getHost(), url.getPort(), url.getScheme().equals("https")); @@ -82,11 +93,11 @@ public Response execute(Request request, Options options) throws IOException { enhancedPluginContext.setTargetServiceInstance(serviceInstance, url); } - // Run pre enhanced plugins. - pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext); - long startMillis = System.currentTimeMillis(); try { + // Run pre enhanced plugins. + pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext); + Response response = delegate.execute(request, options); enhancedPluginContext.setDelay(System.currentTimeMillis() - startMillis); @@ -103,6 +114,15 @@ public Response execute(Request request, Options options) throws IOException { pluginRunner.run(EnhancedPluginType.Client.POST, enhancedPluginContext); return response; } + catch (CallAbortedException callAbortedException) { + // circuit breaker fallback, not need to run post/exception enhanced plugins. + if (callAbortedException.getFallbackInfo() != null) { + return getFallbackResponse(callAbortedException.getFallbackInfo()); + } + else { + throw callAbortedException; + } + } catch (IOException origin) { enhancedPluginContext.setDelay(System.currentTimeMillis() - startMillis); enhancedPluginContext.setThrowable(origin); @@ -115,4 +135,25 @@ public Response execute(Request request, Options options) throws IOException { pluginRunner.run(EnhancedPluginType.Client.FINALLY, enhancedPluginContext); } } + + private Response getFallbackResponse(CircuitBreakerStatus.FallbackInfo fallbackInfo) { + + Response.Builder responseBuilder = Response.builder() + .status(fallbackInfo.getCode()); + if (fallbackInfo.getHeaders() != null) { + Map> headers = new HashMap<>(); + fallbackInfo.getHeaders().forEach((k, v) -> headers.put(k, Collections.singleton(v))); + responseBuilder.headers(headers); + } + if (fallbackInfo.getBody() != null) { + responseBuilder.body(fallbackInfo.getBody(), StandardCharsets.UTF_8); + } + // Feign Response need a nonnull Request, + // which is not important in fallback response (no real request), + // so we create a fake one + Request fakeRequest = Request.create(Request.HttpMethod.GET, "/", new HashMap<>(), Request.Body.empty(), new RequestTemplate()); + responseBuilder.request(fakeRequest); + + return responseBuilder.build(); + } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/DefaultEnhancedPluginRunner.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/DefaultEnhancedPluginRunner.java index f46596991a..f45e296895 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/DefaultEnhancedPluginRunner.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/DefaultEnhancedPluginRunner.java @@ -23,6 +23,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; import com.tencent.polaris.client.api.SDKContext; import org.springframework.cloud.client.DefaultServiceInstance; @@ -41,6 +42,16 @@ public class DefaultEnhancedPluginRunner implements EnhancedPluginRunner { private final ServiceInstance localServiceInstance; + public DefaultEnhancedPluginRunner( + List enhancedPlugins, + List registration, + SDKContext sdkContext + ) { + this(enhancedPlugins, + getPolarisRegistration(registration), + sdkContext); + } + public DefaultEnhancedPluginRunner( List enhancedPlugins, Registration registration, @@ -74,6 +85,9 @@ public void run(EnhancedPluginType pluginType, EnhancedPluginContext context) { try { plugin.run(context); } + catch (CallAbortedException callAbortedException) { + throw callAbortedException; + } catch (Throwable throwable) { plugin.handlerThrowable(context, throwable); } @@ -85,4 +99,16 @@ public ServiceInstance getLocalServiceInstance() { return this.localServiceInstance; } + private static Registration getPolarisRegistration(List registration) { + + if (CollectionUtils.isEmpty(registration)) { + return null; + } + for (Registration reg : registration) { + if ("com.tencent.cloud.polaris.registry.PolarisRegistration".equals(reg.getClass().getName())) { + return reg; + } + } + return registration.get(0); + } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedRequestContext.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedRequestContext.java index 086c14fcc0..6f48b3bf94 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedRequestContext.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedRequestContext.java @@ -35,6 +35,8 @@ public class EnhancedRequestContext { private URI url; + private URI serviceUrl; + public HttpMethod getHttpMethod() { return httpMethod; } @@ -59,6 +61,14 @@ public void setUrl(URI url) { this.url = url; } + public URI getServiceUrl() { + return serviceUrl; + } + + public void setServiceUrl(URI serviceUrl) { + this.serviceUrl = serviceUrl; + } + public static EnhancedContextRequestBuilder builder() { return new EnhancedContextRequestBuilder(); } @@ -76,6 +86,7 @@ public static final class EnhancedContextRequestBuilder { private HttpMethod httpMethod; private HttpHeaders httpHeaders; private URI url; + private URI serviceUrl; private EnhancedContextRequestBuilder() { } @@ -95,11 +106,17 @@ public EnhancedContextRequestBuilder url(URI url) { return this; } + public EnhancedContextRequestBuilder serviceUrl(URI serviceUrl) { + this.serviceUrl = serviceUrl; + return this; + } + public EnhancedRequestContext build() { EnhancedRequestContext enhancedRequestContext = new EnhancedRequestContext(); enhancedRequestContext.httpMethod = this.httpMethod; enhancedRequestContext.url = this.url; enhancedRequestContext.httpHeaders = this.httpHeaders; + enhancedRequestContext.serviceUrl = this.serviceUrl; return enhancedRequestContext; } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PolarisEnhancedPluginUtils.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PolarisEnhancedPluginUtils.java index 779f4bb005..c93905c3ac 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PolarisEnhancedPluginUtils.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PolarisEnhancedPluginUtils.java @@ -32,9 +32,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.tencent.cloud.common.constant.ContextConstant; import com.tencent.cloud.common.constant.HeaderConstant; import com.tencent.cloud.common.constant.RouterConstant; import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; import com.tencent.cloud.common.util.RequestLabelUtils; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; @@ -133,7 +135,12 @@ public static ServiceCallResult createServiceCallResult(String callerHost, public static ResourceStat createInstanceResourceStat( @Nullable String calleeServiceName, @Nullable String calleeHost, @Nullable Integer calleePort, URI uri, @Nullable Integer statusCode, long delay, @Nullable Throwable exception) { - ServiceKey calleeServiceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, StringUtils.isBlank(calleeServiceName) ? uri.getHost() : calleeServiceName); + + String governanceNamespace = MetadataContextHolder.get().getDisposableMetadata().get(ContextConstant.POLARIS_GOVERNANCE_NAMESPACE); + if (StringUtils.isBlank(governanceNamespace)) { + governanceNamespace = MetadataContext.LOCAL_NAMESPACE; + } + ServiceKey calleeServiceKey = new ServiceKey(governanceNamespace, StringUtils.isBlank(calleeServiceName) ? uri.getHost() : calleeServiceName); ServiceKey callerServiceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); Resource resource = new InstanceResource( calleeServiceKey, diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateWrapInterceptor.java similarity index 61% rename from spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java rename to spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateWrapInterceptor.java index fdadc59deb..880fa4d4d7 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateWrapInterceptor.java @@ -18,18 +18,24 @@ package com.tencent.cloud.rpc.enhancement.resttemplate; import java.io.IOException; +import java.net.URI; +import java.util.Optional; +import com.tencent.cloud.common.constant.ContextConstant; import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; +import com.tencent.polaris.metadata.core.MetadataObjectValue; +import com.tencent.polaris.metadata.core.MetadataType; import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest; +import org.springframework.cloud.client.loadbalancer.ServiceRequestWrapper; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient; import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import static com.tencent.cloud.rpc.enhancement.resttemplate.PolarisLoadBalancerRequestTransformer.LOAD_BALANCER_SERVICE_INSTANCE; @@ -39,37 +45,49 @@ * * @author sean yu */ -public class EnhancedRestTemplateInterceptor implements ClientHttpRequestInterceptor { +public class EnhancedRestTemplateWrapInterceptor { private final EnhancedPluginRunner pluginRunner; - public EnhancedRestTemplateInterceptor(EnhancedPluginRunner pluginRunner) { + private final RibbonLoadBalancerClient delegate; + + public EnhancedRestTemplateWrapInterceptor(EnhancedPluginRunner pluginRunner, + RibbonLoadBalancerClient delegate) { this.pluginRunner = pluginRunner; + this.delegate = delegate; } - @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + public ClientHttpResponse intercept(HttpRequest request, String serviceId, + LoadBalancerRequest loadBalancerRequest, Object hint) throws IOException { EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext(); + URI serviceUrl = request.getURI(); + if (request instanceof ServiceRequestWrapper) { + serviceUrl = ((ServiceRequestWrapper) request).getRequest().getURI(); + } + EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder() .httpHeaders(request.getHeaders()) .httpMethod(request.getMethod()) .url(request.getURI()) + .serviceUrl(serviceUrl) .build(); enhancedPluginContext.setRequest(enhancedRequestContext); enhancedPluginContext.setOriginRequest(request); enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance()); - enhancedPluginContext.setTargetServiceInstance((ServiceInstance) MetadataContextHolder.get() - .getLoadbalancerMetadata().get(LOAD_BALANCER_SERVICE_INSTANCE), request.getURI()); + // Run pre enhanced plugins. pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext); long startMillis = System.currentTimeMillis(); try { - ClientHttpResponse response = execution.execute(request, body); + ClientHttpResponse response = delegate.execute(serviceId, loadBalancerRequest, hint); + // get target instance after execute + enhancedPluginContext.setTargetServiceInstance((ServiceInstance) MetadataContextHolder.get() + .getLoadbalancerMetadata().get(LOAD_BALANCER_SERVICE_INSTANCE), request.getURI()); enhancedPluginContext.setDelay(System.currentTimeMillis() - startMillis); EnhancedResponseContext enhancedResponseContext = EnhancedResponseContext.builder() @@ -80,6 +98,20 @@ public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttp // Run post enhanced plugins. pluginRunner.run(EnhancedPluginType.Client.POST, enhancedPluginContext); + + MetadataObjectValue fallbackResponseValue = MetadataContextHolder.get(). + getMetadataContainer(MetadataType.APPLICATION, true). + getMetadataValue(ContextConstant.CircuitBreaker.CIRCUIT_BREAKER_FALLBACK_HTTP_RESPONSE); + + boolean existFallback = Optional.ofNullable(fallbackResponseValue). + map(MetadataObjectValue::getObjectValue).map(Optional::isPresent).orElse(false); + + if (existFallback) { + Object fallbackResponse = fallbackResponseValue.getObjectValue().orElse(null); + if (fallbackResponse instanceof ClientHttpResponse) { + return (ClientHttpResponse) fallbackResponse; + } + } return response; } catch (IOException e) { diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java index e6f2bed2ef..91276976bd 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java @@ -18,6 +18,9 @@ package com.tencent.cloud.rpc.enhancement.scg; import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.Optional; import com.tencent.cloud.common.constant.OrderConstant; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; @@ -25,6 +28,8 @@ import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; import reactor.core.publisher.Mono; import org.springframework.cloud.client.DefaultServiceInstance; @@ -32,6 +37,9 @@ import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.route.Route; import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.server.ServerWebExchange; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; @@ -52,18 +60,37 @@ public EnhancedGatewayGlobalFilter(EnhancedPluginRunner pluginRunner) { @Override public Mono filter(ServerWebExchange originExchange, GatewayFilterChain chain) { + EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext(); EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder() .httpHeaders(originExchange.getRequest().getHeaders()) .httpMethod(originExchange.getRequest().getMethod()) .url(originExchange.getRequest().getURI()) + .serviceUrl(getServiceUri(originExchange)) .build(); enhancedPluginContext.setRequest(enhancedRequestContext); enhancedPluginContext.setOriginRequest(originExchange); // Run pre enhanced plugins. - pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext); + try { + pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext); + } + catch (CallAbortedException e) { + if (e.getFallbackInfo() == null) { + throw e; + } + // circuit breaker fallback, not need to run post/exception enhanced plugins. + ServerHttpResponse response = originExchange.getResponse(); + HttpStatus httpStatus = HttpStatus.resolve(e.getFallbackInfo().getCode()); + response.setStatusCode(httpStatus != null ? httpStatus : HttpStatus.INTERNAL_SERVER_ERROR); + if (CollectionUtils.isNotEmpty(e.getFallbackInfo().getHeaders())) { + e.getFallbackInfo().getHeaders().forEach(response.getHeaders()::set); + } + String body = Optional.of(e.getFallbackInfo().getBody()).orElse(""); + DataBuffer dataBuffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8)); + return response.writeWith(Mono.just(dataBuffer)); + } // Exchange may be changed in plugin ServerWebExchange exchange = (ServerWebExchange) enhancedPluginContext.getOriginRequest(); long startTime = System.currentTimeMillis(); @@ -113,4 +140,15 @@ public Mono filter(ServerWebExchange originExchange, GatewayFilterChain ch public int getOrder() { return OrderConstant.Client.Scg.ENHANCED_FILTER_ORDER; } + + private URI getServiceUri(ServerWebExchange originExchange) { + Route routeAttr = originExchange.getAttribute(GATEWAY_ROUTE_ATTR); + try { + return new URI(originExchange.getRequest().getURI().getScheme(), + routeAttr.getUri().getHost(), originExchange.getRequest().getURI().getPath(), originExchange.getRequest().getURI().getRawQuery()); + } + catch (URISyntaxException e) { + return null; + } + } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/zuul/EnhancedPreZuulFilter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/zuul/EnhancedZuulPluginRunner.java similarity index 74% rename from spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/zuul/EnhancedPreZuulFilter.java rename to spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/zuul/EnhancedZuulPluginRunner.java index 7334b5e6bd..70b2d34fe7 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/zuul/EnhancedPreZuulFilter.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/zuul/EnhancedZuulPluginRunner.java @@ -22,11 +22,8 @@ import java.util.Collections; import java.util.Enumeration; -import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; -import com.netflix.zuul.exception.ZuulException; import com.tencent.cloud.common.constant.ContextConstant; -import com.tencent.cloud.common.constant.OrderConstant; import com.tencent.cloud.common.util.ZuulFilterUtils; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; @@ -35,49 +32,25 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.core.env.Environment; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import org.springframework.util.StringUtils; - -import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; /** * Polaris circuit breaker implement in Zuul. * * @author Haotian Zhang */ -public class EnhancedPreZuulFilter extends ZuulFilter { +public class EnhancedZuulPluginRunner { - private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedPreZuulFilter.class); + private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedZuulPluginRunner.class); private final EnhancedPluginRunner pluginRunner; - private final Environment environment; - - public EnhancedPreZuulFilter(EnhancedPluginRunner pluginRunner, Environment environment) { + public EnhancedZuulPluginRunner(EnhancedPluginRunner pluginRunner) { this.pluginRunner = pluginRunner; - this.environment = environment; - } - - @Override - public String filterType() { - return PRE_TYPE; - } - - @Override - public int filterOrder() { - return OrderConstant.Client.Zuul.ENHANCED_ROUTE_FILTER_ORDER; - } - - @Override - public boolean shouldFilter() { - String enabled = environment.getProperty("spring.cloud.tencent.rpc-enhancement.reporter"); - return StringUtils.isEmpty(enabled) || enabled.equals("true"); } - @Override - public Object run() throws ZuulException { + public void run() { EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext(); RequestContext context = RequestContext.getCurrentContext(); context.set(ContextConstant.Zuul.ENHANCED_PLUGIN_CONTEXT, enhancedPluginContext); @@ -96,6 +69,7 @@ public Object run() throws ZuulException { .httpHeaders(requestHeaders) .httpMethod(HttpMethod.resolve(context.getRequest().getMethod())) .url(uri) + .serviceUrl(uri) .build(); enhancedPluginContext.setRequest(enhancedRequestContext); @@ -108,6 +82,5 @@ public Object run() throws ZuulException { catch (URISyntaxException e) { LOGGER.error("Generate URI failed.", e); } - return null; } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfigurationTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfigurationTest.java index b84fa63435..11977254ae 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfigurationTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfigurationTest.java @@ -22,7 +22,6 @@ import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; import com.tencent.cloud.rpc.enhancement.plugin.reporter.ExceptionPolarisReporter; import com.tencent.cloud.rpc.enhancement.plugin.reporter.SuccessPolarisReporter; -import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateInterceptor; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -58,7 +57,6 @@ public void testDefaultInitialization() { assertThat(context).hasSingleBean(EnhancedFeignBeanPostProcessor.class); assertThat(context).hasSingleBean(SuccessPolarisReporter.class); assertThat(context).hasSingleBean(ExceptionPolarisReporter.class); - assertThat(context).hasSingleBean(EnhancedRestTemplateInterceptor.class); assertThat(context).hasSingleBean(RestTemplate.class); }); } diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClientTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClientTest.java index debbd1d949..396a26c4e2 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClientTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClientTest.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import com.tencent.cloud.polaris.context.PolarisSDKContextManager; @@ -27,6 +28,8 @@ import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; import feign.Client; import feign.Request; import feign.RequestTemplate; @@ -81,7 +84,7 @@ public void testConstructor() { List enhancedPlugins = getMockEnhancedFeignPlugins(); try { new EnhancedFeignClient(mock(Client.class), - new DefaultEnhancedPluginRunner(enhancedPlugins, null, polarisSDKContextManager.getSDKContext())); + new DefaultEnhancedPluginRunner(enhancedPlugins, new ArrayList<>(), polarisSDKContextManager.getSDKContext())); } catch (Throwable e) { fail("Exception encountered.", e); @@ -111,7 +114,7 @@ else if (request.httpMethod().equals(Request.HttpMethod.POST)) { requestTemplate.feignTarget(target); EnhancedFeignClient polarisFeignClient = new EnhancedFeignClient(delegate, - new DefaultEnhancedPluginRunner(getMockEnhancedFeignPlugins(), null, polarisSDKContextManager.getSDKContext())); + new DefaultEnhancedPluginRunner(getMockEnhancedFeignPlugins(), new ArrayList<>(), polarisSDKContextManager.getSDKContext())); // 200 Response response = polarisFeignClient.execute(Request.create(Request.HttpMethod.GET, "http://localhost:8080/test", @@ -135,6 +138,52 @@ else if (request.httpMethod().equals(Request.HttpMethod.POST)) { } } + @Test + public void testExecuteCallAbortedException() throws IOException { + // mock Client.class + Client delegate = mock(Client.class); + doAnswer(invocation -> { + Request request = invocation.getArgument(0); + if (request.httpMethod().equals(Request.HttpMethod.GET)) { + return Response.builder().request(request).status(200).build(); + } + else if (request.httpMethod().equals(Request.HttpMethod.POST)) { + return Response.builder().request(request).status(502).build(); + } + throw new IOException("Mock exception."); + }).when(delegate).execute(any(Request.class), nullable(Request.Options.class)); + + // mock target + Target target = Target.EmptyTarget.create(Object.class); + + // mock RequestTemplate.class + RequestTemplate requestTemplate = new RequestTemplate(); + requestTemplate.feignTarget(target); + + EnhancedFeignClient polarisFeignClient = new EnhancedFeignClient(delegate, + new DefaultEnhancedPluginRunner(getMockCallAbortedExceptionEnhancedFeignPlugins(null), new ArrayList<>(), polarisSDKContextManager.getSDKContext())); + + // Exception + try { + polarisFeignClient.execute(Request.create(Request.HttpMethod.GET, "http://localhost:8080/test", + Collections.emptyMap(), null, requestTemplate), null); + fail("CallAbortedException should be thrown."); + } + catch (CallAbortedException t) { + assertThat(t).isInstanceOf(CallAbortedException.class); + assertThat(t.getMessage()).contains("Mock CallAbortedException"); + } + + polarisFeignClient = new EnhancedFeignClient(delegate, + new DefaultEnhancedPluginRunner(getMockCallAbortedExceptionEnhancedFeignPlugins( + new CircuitBreakerStatus.FallbackInfo(200, new HashMap<>(), "mock ok")), new ArrayList<>(), polarisSDKContextManager.getSDKContext())); + + // fallback 200 + Response response = polarisFeignClient.execute(Request.create(Request.HttpMethod.GET, "http://localhost:8080/test", + Collections.emptyMap(), null, requestTemplate), null); + assertThat(response.status()).isEqualTo(200); + } + private List getMockEnhancedFeignPlugins() { List enhancedPlugins = new ArrayList<>(); @@ -230,6 +279,30 @@ public int getOrder() { } + private List getMockCallAbortedExceptionEnhancedFeignPlugins(CircuitBreakerStatus.FallbackInfo fallbackInfo) { + List enhancedPlugins = new ArrayList<>(); + + enhancedPlugins.add(new EnhancedPlugin() { + + @Override + public int getOrder() { + return 0; + } + + @Override + public EnhancedPluginType getType() { + return EnhancedPluginType.Client.PRE; + } + + @Override + public void run(EnhancedPluginContext context) throws Throwable { + throw new CallAbortedException("Mock CallAbortedException", fallbackInfo); + } + }); + + return enhancedPlugins; + } + @SpringBootApplication protected static class TestApplication { diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContextTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContextTest.java index 4c2aa5ed54..28319b884d 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContextTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContextTest.java @@ -19,6 +19,7 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -150,7 +151,7 @@ public void testGetAndSet() throws Throwable { doReturn(configuration).when(sdkContext).getConfig(); - enhancedPluginRunner = new DefaultEnhancedPluginRunner(Collections.singletonList(enhancedPlugin2), null, sdkContext); + enhancedPluginRunner = new DefaultEnhancedPluginRunner(Collections.singletonList(enhancedPlugin2), new ArrayList<>(), sdkContext); enhancedPluginRunner.run(EnhancedPluginType.Client.POST, enhancedPluginContext); } diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/PolarisEnhancedPluginUtilsTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/PolarisEnhancedPluginUtilsTest.java index be296c26fb..26e7187d1f 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/PolarisEnhancedPluginUtilsTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/PolarisEnhancedPluginUtilsTest.java @@ -18,6 +18,7 @@ package com.tencent.cloud.rpc.enhancement.plugin; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; import java.net.SocketTimeoutException; import java.net.URI; import java.net.URISyntaxException; @@ -28,6 +29,9 @@ import com.tencent.cloud.common.constant.HeaderConstant; import com.tencent.cloud.common.constant.RouterConstant; import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.StaticMetadataManager; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat; @@ -70,7 +74,7 @@ public class PolarisEnhancedPluginUtilsTest { private SDKContext sdkContext; @BeforeAll - static void beforeAll() { + static void beforeAll() throws Exception { mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) .thenReturn("unit-test"); @@ -80,6 +84,12 @@ static void beforeAll() { .when(applicationContext).getBean(RpcEnhancementReporterProperties.class); mockedApplicationContextAwareUtils.when(ApplicationContextAwareUtils::getApplicationContext) .thenReturn(applicationContext); + + StaticMetadataManager metadataManager = new StaticMetadataManager(new MetadataLocalProperties(), null); + + Field field = MetadataContextHolder.class.getDeclaredField("staticMetadataManager"); + field.setAccessible(true); + field.set(null, metadataManager); } @AfterAll diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptorTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptorTest.java deleted file mode 100644 index 659d1cd15e..0000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptorTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making spring-cloud-tencent available. - * - * Copyright (C) 2021 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.rpc.enhancement.resttemplate; - -import java.io.IOException; -import java.net.SocketTimeoutException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; - -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.metadata.StaticMetadataManager; -import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; -import com.tencent.cloud.common.util.ApplicationContextAwareUtils; -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.cloud.rpc.enhancement.plugin.DefaultEnhancedPluginRunner; -import com.tencent.polaris.client.api.SDKContext; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpResponse; - -import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; -import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; - -@ExtendWith(MockitoExtension.class) -public class EnhancedRestTemplateInterceptorTest { - - private static MockedStatic mockedApplicationContextAwareUtils; - @Mock - private RpcEnhancementReporterProperties reporterProperties; - @Mock - private SDKContext sdkContext; - @Mock - Registration registration; - @Mock - private ClientHttpRequestExecution mockClientHttpRequestExecution; - @Mock - private ClientHttpResponse mockClientHttpResponse; - @Mock - private HttpRequest mockHttpRequest; - @Mock - private HttpHeaders mockHttpHeaders; - - @BeforeAll - static void beforeAll() { - mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); - mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) - .thenReturn("unit-test"); - ApplicationContext applicationContext = mock(ApplicationContext.class); - MetadataLocalProperties metadataLocalProperties = mock(MetadataLocalProperties.class); - StaticMetadataManager staticMetadataManager = mock(StaticMetadataManager.class); - doReturn(metadataLocalProperties).when(applicationContext).getBean(MetadataLocalProperties.class); - doReturn(staticMetadataManager).when(applicationContext).getBean(StaticMetadataManager.class); - mockedApplicationContextAwareUtils.when(ApplicationContextAwareUtils::getApplicationContext).thenReturn(applicationContext); - } - - @AfterAll - static void afterAll() { - mockedApplicationContextAwareUtils.close(); - } - - @BeforeEach - void setUp() { - MetadataContext.LOCAL_NAMESPACE = NAMESPACE_TEST; - MetadataContext.LOCAL_SERVICE = SERVICE_PROVIDER; - } - - @Test - public void testRun() throws IOException, URISyntaxException { - - ClientHttpResponse actualResult; - final byte[] inputBody = null; - - URI uri = new URI("http://0.0.0.0/"); - doReturn(uri).when(mockHttpRequest).getURI(); - doReturn(HttpMethod.GET).when(mockHttpRequest).getMethod(); - doReturn(mockHttpHeaders).when(mockHttpRequest).getHeaders(); - doReturn(mockClientHttpResponse).when(mockClientHttpRequestExecution).execute(mockHttpRequest, inputBody); - - EnhancedRestTemplateInterceptor reporter = new EnhancedRestTemplateInterceptor(new DefaultEnhancedPluginRunner(new ArrayList<>(), registration, null)); - actualResult = reporter.intercept(mockHttpRequest, inputBody, mockClientHttpRequestExecution); - assertThat(actualResult).isEqualTo(mockClientHttpResponse); - - actualResult = reporter.intercept(mockHttpRequest, inputBody, mockClientHttpRequestExecution); - assertThat(actualResult).isEqualTo(mockClientHttpResponse); - - doThrow(new SocketTimeoutException()).when(mockClientHttpRequestExecution).execute(mockHttpRequest, inputBody); - assertThatThrownBy(() -> reporter.intercept(mockHttpRequest, inputBody, mockClientHttpRequestExecution)).isInstanceOf(SocketTimeoutException.class); - } - -}