diff --git a/CHANGELOG.md b/CHANGELOG.md index 675453073..d466c6515 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,3 +8,4 @@ - [fix: fix nacos CircuitBreaker disable bug.](https://github.com/Tencent/spring-cloud-tencent/pull/948) - [feature: optimize polaris-ratelimit-example, add caller query params and headers, add callee custom label resolver.](https://github.com/Tencent/spring-cloud-tencent/pull/951) - [refactor:refactor reactor code in router module.](https://github.com/Tencent/spring-cloud-tencent/pull/955) +- [refactor: refactor circuitbreaker .](https://github.com/Tencent/spring-cloud-tencent/pull/963) 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 b73303f78..2c1f9decb 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 @@ -22,6 +22,8 @@ import java.util.List; import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreakerFactory; 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.circuitbreaker.resttemplate.PolarisCircuitBreakerRestTemplateBeanPostProcessor; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; @@ -61,6 +63,20 @@ public class PolarisCircuitBreakerAutoConfiguration { return CircuitBreakAPIFactory.createCircuitBreakAPIByContext(polarisContext); } + @Bean + @ConditionalOnMissingBean(SuccessCircuitBreakerReporter.class) + public SuccessCircuitBreakerReporter successCircuitBreakerReporter(RpcEnhancementReporterProperties properties, + SDKContext polarisContext, CircuitBreakAPI circuitBreakAPI) { + return new SuccessCircuitBreakerReporter(properties, polarisContext, circuitBreakAPI); + } + + @Bean + @ConditionalOnMissingBean(ExceptionCircuitBreakerReporter.class) + public ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter(RpcEnhancementReporterProperties properties, + SDKContext polarisContext, CircuitBreakAPI circuitBreakAPI) { + return new ExceptionCircuitBreakerReporter(properties, polarisContext, circuitBreakAPI); + } + @Bean @ConditionalOnMissingBean(CircuitBreakerFactory.class) public CircuitBreakerFactory polarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI) { 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 index fdb89b2c9..f2f509218 100644 --- 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 @@ -22,6 +22,8 @@ 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.rpc.enhancement.config.RpcEnhancementAutoConfiguration; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; import com.tencent.polaris.api.core.ConsumerAPI; @@ -59,6 +61,20 @@ public class ReactivePolarisCircuitBreakerAutoConfiguration { return CircuitBreakAPIFactory.createCircuitBreakAPIByContext(polarisContext); } + @Bean + @ConditionalOnMissingBean(SuccessCircuitBreakerReporter.class) + public SuccessCircuitBreakerReporter successCircuitBreakerReporter(RpcEnhancementReporterProperties properties, + SDKContext polarisContext, CircuitBreakAPI circuitBreakAPI) { + return new SuccessCircuitBreakerReporter(properties, polarisContext, circuitBreakAPI); + } + + @Bean + @ConditionalOnMissingBean(ExceptionCircuitBreakerReporter.class) + public ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter(RpcEnhancementReporterProperties properties, + SDKContext polarisContext, CircuitBreakAPI circuitBreakAPI) { + return new ExceptionCircuitBreakerReporter(properties, polarisContext, circuitBreakAPI); + } + @Bean @ConditionalOnMissingBean(ReactiveCircuitBreakerFactory.class) public ReactiveCircuitBreakerFactory polarisReactiveCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI) { 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 new file mode 100644 index 000000000..b47c1a4a2 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/ExceptionCircuitBreakerReporter.java @@ -0,0 +1,97 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.reporter; + +import java.util.Optional; + +import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +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.polaris.api.plugin.circuitbreaker.ResourceStat; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.client.api.SDKContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.core.Ordered; + +public class ExceptionCircuitBreakerReporter extends AbstractPolarisReporterAdapter implements EnhancedPlugin { + + private static final Logger LOG = LoggerFactory.getLogger(ExceptionCircuitBreakerReporter.class); + + private final CircuitBreakAPI circuitBreakAPI; + + public ExceptionCircuitBreakerReporter(RpcEnhancementReporterProperties reportProperties, + SDKContext context, + CircuitBreakAPI circuitBreakAPI) { + super(reportProperties, context); + this.circuitBreakAPI = circuitBreakAPI; + } + + @Override + public String getName() { + return ExceptionCircuitBreakerReporter.class.getName(); + } + + @Override + public EnhancedPluginType getType() { + return EnhancedPluginType.EXCEPTION; + } + + @Override + public void run(EnhancedPluginContext context) throws Throwable { + if (!super.reportProperties.isEnabled()) { + return; + } + + EnhancedRequestContext request = context.getRequest(); + ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance()).orElse(new DefaultServiceInstance()); + + ResourceStat resourceStat = createInstanceResourceStat( + serviceInstance.getServiceId(), + serviceInstance.getHost(), + serviceInstance.getPort(), + request.getUrl(), + null, + context.getDelay(), + context.getThrowable() + ); + + LOG.debug("Will report CircuitBreaker ResourceStat of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", + resourceStat.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl().getPath(), context.getThrowable().getMessage(), context.getDelay()); + + circuitBreakAPI.report(resourceStat); + + } + + @Override + public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) { + LOG.error("ExceptionCircuitBreakerReporter runs failed. context=[{}].", + context, throwable); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE + 2; + } +} 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 new file mode 100644 index 000000000..ce5fe2a81 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/SuccessCircuitBreakerReporter.java @@ -0,0 +1,99 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.reporter; + +import java.util.Optional; + +import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +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.api.plugin.circuitbreaker.ResourceStat; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.client.api.SDKContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.core.Ordered; + + +public class SuccessCircuitBreakerReporter extends AbstractPolarisReporterAdapter implements EnhancedPlugin { + + private static final Logger LOG = LoggerFactory.getLogger(SuccessPolarisReporter.class); + + private final CircuitBreakAPI circuitBreakAPI; + + public SuccessCircuitBreakerReporter(RpcEnhancementReporterProperties reportProperties, + SDKContext context, + CircuitBreakAPI circuitBreakAPI) { + super(reportProperties, context); + this.circuitBreakAPI = circuitBreakAPI; + } + + @Override + public String getName() { + return SuccessCircuitBreakerReporter.class.getName(); + } + + @Override + public EnhancedPluginType getType() { + return EnhancedPluginType.POST; + } + + @Override + public void run(EnhancedPluginContext context) throws Throwable { + if (!super.reportProperties.isEnabled()) { + return; + } + EnhancedRequestContext request = context.getRequest(); + EnhancedResponseContext response = context.getResponse(); + ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance()).orElse(new DefaultServiceInstance()); + + ResourceStat resourceStat = createInstanceResourceStat( + serviceInstance.getServiceId(), + serviceInstance.getHost(), + serviceInstance.getPort(), + request.getUrl(), + response.getHttpStatus(), + context.getDelay(), + null + ); + + LOG.debug("Will report CircuitBreaker ResourceStat of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", + resourceStat.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl().getPath(), response.getHttpStatus(), context.getDelay()); + + circuitBreakAPI.report(resourceStat); + } + + @Override + public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) { + LOG.error("SuccessCircuitBreakerReporter runs failed. context=[{}].", + context, throwable); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE + 2; + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerHttpResponse.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerHttpResponse.java index ce6f640a5..46a8e8584 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerHttpResponse.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisCircuitBreakerHttpResponse.java @@ -28,8 +28,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.client.AbstractClientHttpResponse; -import static com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter.POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER; - /** * PolarisCircuitBreakerHttpResponse. * @@ -57,7 +55,6 @@ public class PolarisCircuitBreakerHttpResponse extends AbstractClientHttpRespons PolarisCircuitBreakerHttpResponse(CircuitBreakerStatus.FallbackInfo fallbackInfo) { this.fallbackInfo = fallbackInfo; - headers.add(POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER, "true"); if (fallbackInfo.getHeaders() != null) { fallbackInfo.getHeaders().forEach(headers::add); } 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 index e8de9294c..49afb2210 100644 --- 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 @@ -20,7 +20,6 @@ package com.tencent.cloud.polaris.circuitbreaker.resttemplate; import java.io.IOException; import java.lang.reflect.Method; -import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter; import com.tencent.polaris.api.pojo.CircuitBreakerStatus; import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; @@ -35,8 +34,6 @@ import org.springframework.util.StringUtils; import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestTemplate; -import static com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter.HEADER_HAS_ERROR; - /** * PolarisCircuitBreakerRestTemplateInterceptor. * @@ -70,15 +67,8 @@ public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpR () -> { try { ClientHttpResponse response = execution.execute(request, body); - // pre handle response error - // EnhancedRestTemplateReporter always return true, - // so we need to check header set by EnhancedRestTemplateReporter ResponseErrorHandler errorHandler = restTemplate.getErrorHandler(); - boolean hasError = errorHandler.hasError(response); - if (errorHandler instanceof EnhancedRestTemplateReporter) { - hasError = Boolean.parseBoolean(response.getHeaders().getFirst(HEADER_HAS_ERROR)); - } - if (hasError) { + if (errorHandler.hasError(response)) { errorHandler.handleError(request.getURI(), request.getMethod(), response); } return response; diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerIntegrationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerIntegrationTest.java index f4bd9ede1..3e12daec8 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerIntegrationTest.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerIntegrationTest.java @@ -33,13 +33,9 @@ import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeig import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker; import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerFallback; import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerHttpResponse; -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter; -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.factory.CircuitBreakAPIFactory; -import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.client.util.Utils; import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto; import com.tencent.polaris.test.common.TestUtils; @@ -71,7 +67,6 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.DefaultUriBuilderFactory; -import static com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter.HEADER_HAS_ERROR; import static com.tencent.polaris.test.common.TestUtils.SERVER_ADDRESS_ENV; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -144,13 +139,11 @@ public class PolarisCircuitBreakerIntegrationTest { mockServer.verify(); mockServer.reset(); HttpHeaders headers = new HttpHeaders(); - headers.add(HEADER_HAS_ERROR, "true"); - // no delegateHandler in EnhancedRestTemplateReporter, so this will except err 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("BAD_GATEWAY"); + assertThat(defaultRestTemplate.getForObject("http://localhost:18001/example/service/b/info", String.class)).isEqualTo("fallback"); mockServer.verify(); mockServer.reset(); assertThat(restTemplateFallbackFromCode.getForObject("/example/service/b/info", String.class)).isEqualTo("\"this is a fallback class\""); @@ -176,11 +169,8 @@ public class PolarisCircuitBreakerIntegrationTest { @Bean @PolarisCircuitBreaker(fallback = "fallback") - public RestTemplate defaultRestTemplate(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { - RestTemplate defaultRestTemplate = new RestTemplate(); - EnhancedRestTemplateReporter enhancedRestTemplateReporter = new EnhancedRestTemplateReporter(properties, context, consumerAPI); - defaultRestTemplate.setErrorHandler(enhancedRestTemplateReporter); - return defaultRestTemplate; + public RestTemplate defaultRestTemplate() { + return new RestTemplate(); } @Bean 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 new file mode 100644 index 000000000..53e38c9d2 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/ExceptionCircuitBreakerReporterTest.java @@ -0,0 +1,145 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.reporter; + +import java.net.URI; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +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.circuitbreak.api.CircuitBreakAPI; +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.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.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.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * ExceptionCircuitBreakerReporterTest. + * + * @author sean yu + */ +@ExtendWith(MockitoExtension.class) +public class ExceptionCircuitBreakerReporterTest { + + private static MockedStatic mockedApplicationContextAwareUtils; + @Mock + private RpcEnhancementReporterProperties reporterProperties; + @Mock + private SDKContext sdkContext; + @InjectMocks + private ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter; + @Mock + private CircuitBreakAPI circuitBreakAPI; + + @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(exceptionCircuitBreakerReporter.getName()).isEqualTo(ExceptionCircuitBreakerReporter.class.getName()); + } + + @Test + public void testType() { + assertThat(exceptionCircuitBreakerReporter.getType()).isEqualTo(EnhancedPluginType.EXCEPTION); + } + + @Test + public void testRun() throws Throwable { + EnhancedPluginContext context = mock(EnhancedPluginContext.class); + // test not report + exceptionCircuitBreakerReporter.run(context); + verify(context, times(0)).getRequest(); + + doReturn(true).when(reporterProperties).isEnabled(); + + 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.setServiceInstance(serviceInstance); + pluginContext.setThrowable(new RuntimeException()); + + exceptionCircuitBreakerReporter.run(pluginContext); + exceptionCircuitBreakerReporter.getOrder(); + exceptionCircuitBreakerReporter.getName(); + exceptionCircuitBreakerReporter.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); + exceptionCircuitBreakerReporter.handlerThrowable(context, new RuntimeException("Mock exception.")); + } +} 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 new file mode 100644 index 000000000..4dcd3d19e --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/reporter/SuccessCircuitBreakerReporterTest.java @@ -0,0 +1,143 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.reporter; + +import java.net.URI; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +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.circuitbreak.api.CircuitBreakAPI; +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.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.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.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * SuccessCircuitBreakerReporterTest. + * + * @author sean yu + */ +@ExtendWith(MockitoExtension.class) +public class SuccessCircuitBreakerReporterTest { + + private static MockedStatic mockedApplicationContextAwareUtils; + @Mock + private SDKContext sdkContext; + @Mock + private RpcEnhancementReporterProperties reporterProperties; + @InjectMocks + private SuccessCircuitBreakerReporter successCircuitBreakerReporter; + @Mock + private CircuitBreakAPI circuitBreakAPI; + + @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(successCircuitBreakerReporter.getName()).isEqualTo(SuccessCircuitBreakerReporter.class.getName()); + } + + @Test + public void testType() { + assertThat(successCircuitBreakerReporter.getType()).isEqualTo(EnhancedPluginType.POST); + } + + @Test + public void testRun() throws Throwable { + + EnhancedPluginContext context = mock(EnhancedPluginContext.class); + // test not report + successCircuitBreakerReporter.run(context); + verify(context, times(0)).getRequest(); + + 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(200) + .build(); + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(SERVICE_PROVIDER); + + pluginContext.setRequest(request); + pluginContext.setResponse(response); + pluginContext.setServiceInstance(serviceInstance); + + successCircuitBreakerReporter.run(pluginContext); + successCircuitBreakerReporter.getOrder(); + successCircuitBreakerReporter.getName(); + successCircuitBreakerReporter.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); + successCircuitBreakerReporter.handlerThrowable(context, new RuntimeException("Mock exception.")); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java index da5377b63..3d9bf1a40 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java @@ -18,14 +18,9 @@ package com.tencent.cloud.polaris.loadbalancer; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; -import com.tencent.cloud.polaris.loadbalancer.reactive.PolarisLoadBalancerClientRequestTransformer; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.client.api.SDKContext; -import com.tencent.polaris.factory.api.DiscoveryAPIFactory; import com.tencent.polaris.router.api.core.RouterAPI; 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.ConditionalOnBlockingDiscoveryEnabled; @@ -34,7 +29,6 @@ import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; -import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerClientRequestTransformer; import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; @@ -71,14 +65,6 @@ public class PolarisLoadBalancerClientConfiguration { loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), routerAPI); } - @Bean - @ConditionalOnMissingBean - @ConditionalOnClass(name = "org.springframework.web.reactive.function.client.ClientRequest") - public LoadBalancerClientRequestTransformer polarisLoadBalancerClientRequestTransformer(SDKContext sdkContext) { - ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByContext(sdkContext); - return new PolarisLoadBalancerClientRequestTransformer(consumerAPI); - } - @Configuration(proxyBeanMethods = false) @ConditionalOnReactiveDiscoveryEnabled @Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER) diff --git a/spring-cloud-tencent-dependencies/pom.xml b/spring-cloud-tencent-dependencies/pom.xml index bffc5b3c7..87c1f3b5d 100644 --- a/spring-cloud-tencent-dependencies/pom.xml +++ b/spring-cloud-tencent-dependencies/pom.xml @@ -73,7 +73,7 @@ 1.11.0-2021.0.6-SNAPSHOT - 1.11.6 + 1.12.0-SNAPSHOT 31.0.1-jre 1.2.11 4.5.1 diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml index 650be3398..a7bdb3f4b 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml @@ -54,6 +54,6 @@ logging: level: root: info com.tencent.polaris.discovery.client.flow.RegisterFlow: off - com.tencent.polaris.plugins.registry.memory.CacheObject: off - com.tencent.cloud.polaris.circuitbreaker: debug + com.tencent.polaris.plugins.registry: off + com.tencent.cloud: debug diff --git a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/bootstrap.yml index 67168f141..649fd5a34 100644 --- a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/bootstrap.yml @@ -12,7 +12,9 @@ spring: enabled: true rejectRequestTipsFilePath: reject-tips.html maxQueuingTime: 500 - + stat: + enabled: true + port: 28083 management: endpoints: web: diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapter.java index ad47fde2b..d177aad8b 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapter.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapter.java @@ -17,6 +17,10 @@ package com.tencent.cloud.rpc.enhancement; +import java.io.UnsupportedEncodingException; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -24,9 +28,19 @@ import java.util.List; import java.util.Objects; 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.util.RequestLabelUtils; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat; +import com.tencent.polaris.api.plugin.circuitbreaker.entity.InstanceResource; +import com.tencent.polaris.api.plugin.circuitbreaker.entity.Resource; import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.ServiceCallResult; import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.client.api.SDKContext; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +48,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; +import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static org.springframework.http.HttpStatus.BAD_GATEWAY; import static org.springframework.http.HttpStatus.BANDWIDTH_LIMIT_EXCEEDED; import static org.springframework.http.HttpStatus.GATEWAY_TIMEOUT; @@ -53,22 +68,85 @@ import static org.springframework.http.HttpStatus.VARIANT_ALSO_NEGOTIATES; * @author Elve.Xu 2022-07-11 */ public abstract class AbstractPolarisReporterAdapter { - private static final Logger LOG = LoggerFactory.getLogger(AbstractPolarisReporterAdapter.class); private static final List HTTP_STATUSES = toList(NOT_IMPLEMENTED, BAD_GATEWAY, SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, HTTP_VERSION_NOT_SUPPORTED, VARIANT_ALSO_NEGOTIATES, INSUFFICIENT_STORAGE, LOOP_DETECTED, BANDWIDTH_LIMIT_EXCEEDED, NOT_EXTENDED, NETWORK_AUTHENTICATION_REQUIRED); + protected final RpcEnhancementReporterProperties reportProperties; + protected final SDKContext context; + /** * Constructor With {@link RpcEnhancementReporterProperties} . * * @param reportProperties instance of {@link RpcEnhancementReporterProperties}. */ - protected AbstractPolarisReporterAdapter(RpcEnhancementReporterProperties reportProperties) { + protected AbstractPolarisReporterAdapter(RpcEnhancementReporterProperties reportProperties, SDKContext context) { this.reportProperties = reportProperties; + this.context = context; + } + + /** + * createServiceCallResult. + * @param calleeServiceName will pick up url host when null + * @param calleeHost will pick up url host when null + * @param calleePort will pick up url port when null + * @param uri request url + * @param requestHeaders request header + * @param responseHeaders response header + * @param statusCode response status + * @param delay delay + * @param exception exception + * @return ServiceCallResult + */ + public ServiceCallResult createServiceCallResult( + @Nullable String calleeServiceName, @Nullable String calleeHost, @Nullable Integer calleePort, + URI uri, HttpHeaders requestHeaders, @Nullable HttpHeaders responseHeaders, + @Nullable Integer statusCode, long delay, @Nullable Throwable exception) { + + ServiceCallResult resultRequest = new ServiceCallResult(); + resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE); + resultRequest.setService(StringUtils.isBlank(calleeServiceName) ? uri.getHost() : calleeServiceName); + resultRequest.setMethod(uri.getPath()); + resultRequest.setRetCode(statusCode == null ? -1 : statusCode); + resultRequest.setDelay(delay); + resultRequest.setCallerService(new ServiceKey(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE)); + resultRequest.setCallerIp(this.context.getConfig().getGlobal().getAPI().getBindIP()); + resultRequest.setHost(StringUtils.isBlank(calleeHost) ? uri.getHost() : calleeHost); + resultRequest.setPort(calleePort == null ? getPort(uri) : calleePort); + resultRequest.setLabels(getLabels(requestHeaders)); + resultRequest.setRetStatus(getRetStatusFromRequest(responseHeaders, getDefaultRetStatus(statusCode, exception))); + resultRequest.setRuleName(getActiveRuleNameFromRequest(responseHeaders)); + return resultRequest; } + /** + * createInstanceResourceStat. + * @param calleeServiceName will pick up url host when null + * @param calleeHost will pick up url host when null + * @param calleePort will pick up url port when null + * @param uri request url + * @param statusCode response status + * @param delay delay + * @param exception exception + * @return ResourceStat + */ + public 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); + ServiceKey callerServiceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); + Resource resource = new InstanceResource( + calleeServiceKey, + StringUtils.isBlank(calleeHost) ? uri.getHost() : calleeHost, + calleePort == null ? getPort(uri) : calleePort, + callerServiceKey + ); + return new ResourceStat(resource, statusCode == null ? -1 : statusCode, delay, getDefaultRetStatus(statusCode, exception)); + } + + /** * Convert items to List. * @@ -123,7 +201,7 @@ public abstract class AbstractPolarisReporterAdapter { } protected RetStatus getRetStatusFromRequest(HttpHeaders headers, RetStatus defaultVal) { - if (headers.containsKey(HeaderConstant.INTERNAL_CALLEE_RET_STATUS)) { + if (headers != null && headers.containsKey(HeaderConstant.INTERNAL_CALLEE_RET_STATUS)) { List values = headers.get(HeaderConstant.INTERNAL_CALLEE_RET_STATUS); if (CollectionUtils.isNotEmpty(values)) { String retStatusVal = com.tencent.polaris.api.utils.StringUtils.defaultString(values.get(0)); @@ -139,13 +217,50 @@ public abstract class AbstractPolarisReporterAdapter { } protected String getActiveRuleNameFromRequest(HttpHeaders headers) { - if (headers.containsKey(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME)) { + if (headers != null && headers.containsKey(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME)) { Collection values = headers.get(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME); if (CollectionUtils.isNotEmpty(values)) { - String val = com.tencent.polaris.api.utils.StringUtils.defaultString(new ArrayList<>(values).get(0)); - return val; + return com.tencent.polaris.api.utils.StringUtils.defaultString(new ArrayList<>(values).get(0)); } } return ""; } + + private RetStatus getDefaultRetStatus(Integer statusCode, Throwable exception) { + RetStatus retStatus = RetStatus.RetSuccess; + if (exception != null) { + retStatus = RetStatus.RetFail; + if (exception instanceof SocketTimeoutException) { + retStatus = RetStatus.RetTimeout; + } + } + else if (statusCode == null || apply(HttpStatus.resolve(statusCode))) { + retStatus = RetStatus.RetFail; + } + return retStatus; + } + + private int getPort(URI uri) { + // -1 means access directly by url, and use http default port number 80 + return uri.getPort() == -1 ? 80 : uri.getPort(); + } + + private String getLabels(HttpHeaders headers) { + if (headers != null) { + Collection labels = headers.get(RouterConstant.ROUTER_LABEL_HEADER); + if (CollectionUtils.isNotEmpty(labels) && labels.iterator().hasNext()) { + String label = labels.iterator().next(); + try { + label = URLDecoder.decode(label, UTF_8); + } + catch (UnsupportedEncodingException e) { + LOG.error("unsupported charset exception " + UTF_8, e); + } + return RequestLabelUtils.convertLabel(label); + } + } + return 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 c26d1269b..d675886ce 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 @@ -22,17 +22,17 @@ import java.util.List; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; -import com.tencent.cloud.rpc.enhancement.feign.DefaultEnhancedFeignPluginRunner; import com.tencent.cloud.rpc.enhancement.feign.EnhancedFeignBeanPostProcessor; -import com.tencent.cloud.rpc.enhancement.feign.EnhancedFeignPluginRunner; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin; -import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.ExceptionPolarisReporter; -import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.SuccessPolarisReporter; +import com.tencent.cloud.rpc.enhancement.plugin.DefaultEnhancedPluginRunner; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin; +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.BlockingLoadBalancerClientAspect; -import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter; -import com.tencent.cloud.rpc.enhancement.scg.EnhancedPolarisHttpClientCustomizer; -import com.tencent.cloud.rpc.enhancement.scg.EnhancedPolarisHttpHeadersFilter; +import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateInterceptor; +import com.tencent.cloud.rpc.enhancement.scg.EnhancedGatewayGlobalFilter; import com.tencent.cloud.rpc.enhancement.webclient.EnhancedWebClientReporter; +import com.tencent.cloud.rpc.enhancement.webclient.PolarisLoadBalancerClientRequestTransformer; import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.polaris.client.api.SDKContext; @@ -46,14 +46,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.cloud.gateway.config.HttpClientCustomizer; -import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Role; import org.springframework.web.client.RestTemplate; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.WebClient; /** * Auto Configuration for Polaris {@link feign.Feign} OR {@link RestTemplate} which can automatically bring in the call @@ -68,6 +66,26 @@ import org.springframework.web.reactive.function.client.ExchangeFilterFunction; @AutoConfigureAfter(PolarisContextAutoConfiguration.class) public class RpcEnhancementAutoConfiguration { + @Bean + public EnhancedPluginRunner enhancedFeignPluginRunner( + @Autowired(required = false) List enhancedPlugins) { + return new DefaultEnhancedPluginRunner(enhancedPlugins); + } + + @Bean + public SuccessPolarisReporter successPolarisReporter(RpcEnhancementReporterProperties properties, + SDKContext context, + ConsumerAPI consumerAPI) { + return new SuccessPolarisReporter(properties, context, consumerAPI); + } + + @Bean + public ExceptionPolarisReporter exceptionPolarisReporter(RpcEnhancementReporterProperties properties, + SDKContext context, + ConsumerAPI consumerAPI) { + return new ExceptionPolarisReporter(properties, context, consumerAPI); + } + /** * Configuration for Polaris {@link feign.Feign} which can automatically bring in the call * results for reporting. @@ -81,33 +99,10 @@ public class RpcEnhancementAutoConfiguration { protected static class PolarisFeignClientAutoConfiguration { @Bean - public EnhancedFeignPluginRunner enhancedFeignPluginRunner( - @Autowired(required = false) List enhancedFeignPlugins) { - return new DefaultEnhancedFeignPluginRunner(enhancedFeignPlugins); - } - - @Bean - public EnhancedFeignBeanPostProcessor polarisFeignBeanPostProcessor(@Lazy EnhancedFeignPluginRunner pluginRunner) { + public EnhancedFeignBeanPostProcessor polarisFeignBeanPostProcessor(@Lazy EnhancedPluginRunner pluginRunner) { return new EnhancedFeignBeanPostProcessor(pluginRunner); } - @Configuration - static class PolarisReporterConfig { - - @Bean - public SuccessPolarisReporter successPolarisReporter(RpcEnhancementReporterProperties properties, - @Autowired(required = false) SDKContext context, - @Autowired(required = false) ConsumerAPI consumerAPI) { - return new SuccessPolarisReporter(properties, context, consumerAPI); - } - - @Bean - public ExceptionPolarisReporter exceptionPolarisReporter(RpcEnhancementReporterProperties properties, - @Autowired(required = false) SDKContext context, - @Autowired(required = false) ConsumerAPI consumerAPI) { - return new ExceptionPolarisReporter(properties, context, consumerAPI); - } - } } /** @@ -125,16 +120,15 @@ public class RpcEnhancementAutoConfiguration { private List restTemplates = Collections.emptyList(); @Bean - public EnhancedRestTemplateReporter enhancedRestTemplateReporter( - RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { - return new EnhancedRestTemplateReporter(properties, context, consumerAPI); + public EnhancedRestTemplateInterceptor enhancedPolarisRestTemplateReporter(@Lazy EnhancedPluginRunner pluginRunner) { + return new EnhancedRestTemplateInterceptor(pluginRunner); } @Bean - public SmartInitializingSingleton setErrorHandlerForRestTemplate(EnhancedRestTemplateReporter reporter) { + public SmartInitializingSingleton setPolarisReporterForRestTemplate(EnhancedRestTemplateInterceptor reporter) { return () -> { for (RestTemplate restTemplate : restTemplates) { - restTemplate.setErrorHandler(reporter); + restTemplate.getInterceptors().add(reporter); } }; } @@ -145,6 +139,7 @@ public class RpcEnhancementAutoConfiguration { public BlockingLoadBalancerClientAspect blockingLoadBalancerClientAspect() { return new BlockingLoadBalancerClientAspect(); } + } /** @@ -155,11 +150,28 @@ public class RpcEnhancementAutoConfiguration { @ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient") protected static class PolarisWebClientAutoConfiguration { + @Autowired(required = false) + private List webClientBuilder = Collections.emptyList(); + @Bean - public ExchangeFilterFunction exchangeFilterFunction( - RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { - return new EnhancedWebClientReporter(properties, context, consumerAPI); + public EnhancedWebClientReporter exchangeFilterFunction(@Lazy EnhancedPluginRunner pluginRunner) { + return new EnhancedWebClientReporter(pluginRunner); } + + @Bean + public SmartInitializingSingleton addEnhancedWebClientReporterForWebClient(EnhancedWebClientReporter reporter) { + return () -> webClientBuilder.forEach(webClient -> { + webClient.filter(reporter); + }); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnClass(name = "org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerClientRequestTransformer") + public PolarisLoadBalancerClientRequestTransformer polarisLoadBalancerClientRequestTransformer() { + return new PolarisLoadBalancerClientRequestTransformer(); + } + } /** @@ -172,17 +184,11 @@ public class RpcEnhancementAutoConfiguration { protected static class PolarisGatewayAutoConfiguration { @Bean - @ConditionalOnClass(name = {"org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter"}) - public HttpHeadersFilter enhancedPolarisHttpHeadersFilter() { - return new EnhancedPolarisHttpHeadersFilter(); - } - - @Bean - @ConditionalOnClass(name = {"org.springframework.cloud.gateway.config.HttpClientCustomizer"}) - public HttpClientCustomizer httpClientCustomizer( - RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { - return new EnhancedPolarisHttpClientCustomizer(properties, context, consumerAPI); + @ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.GlobalFilter") + public EnhancedGatewayGlobalFilter enhancedPolarisGatewayReporter(@Lazy EnhancedPluginRunner pluginRunner) { + return new EnhancedGatewayGlobalFilter(pluginRunner); } } + } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignBeanPostProcessor.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignBeanPostProcessor.java index 1ddbe8146..1aa0abe24 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignBeanPostProcessor.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignBeanPostProcessor.java @@ -17,6 +17,7 @@ package com.tencent.cloud.rpc.enhancement.feign; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; import feign.Client; import org.springframework.beans.BeansException; @@ -35,11 +36,11 @@ import org.springframework.cloud.openfeign.loadbalancer.RetryableFeignBlockingLo */ public class EnhancedFeignBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { - private final EnhancedFeignPluginRunner pluginRunner; + private final EnhancedPluginRunner pluginRunner; private BeanFactory factory; - public EnhancedFeignBeanPostProcessor(EnhancedFeignPluginRunner pluginRunner) { + public EnhancedFeignBeanPostProcessor(EnhancedPluginRunner pluginRunner) { this.pluginRunner = pluginRunner; } 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 b01850d4d..e191edfde 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 @@ -18,17 +18,26 @@ package com.tencent.cloud.rpc.enhancement.feign; import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; import feign.Client; import feign.Request; import feign.Request.Options; import feign.Response; -import static com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType.EXCEPTION; -import static com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType.FINALLY; -import static com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType.POST; -import static com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType.PRE; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; + +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.EXCEPTION; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.FINALLY; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.POST; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.PRE; import static feign.Util.checkNotNull; /** @@ -40,40 +49,64 @@ public class EnhancedFeignClient implements Client { private final Client delegate; - private final EnhancedFeignPluginRunner pluginRunner; + private final EnhancedPluginRunner pluginRunner; - public EnhancedFeignClient(Client target, EnhancedFeignPluginRunner pluginRunner) { + public EnhancedFeignClient(Client target, EnhancedPluginRunner pluginRunner) { this.delegate = checkNotNull(target, "target"); this.pluginRunner = pluginRunner; } @Override public Response execute(Request request, Options options) throws IOException { - EnhancedFeignContext enhancedFeignContext = new EnhancedFeignContext(); - enhancedFeignContext.setRequest(request); - enhancedFeignContext.setOptions(options); + EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext(); + + HttpHeaders requestHeaders = new HttpHeaders(); + request.headers().forEach((s, strings) -> requestHeaders.addAll(s, new ArrayList<>(strings))); + URI url = URI.create(request.url()); + + EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder() + .httpHeaders(requestHeaders) + .httpMethod(HttpMethod.resolve(request.httpMethod().name())) + .url(url) + .build(); + enhancedPluginContext.setRequest(enhancedRequestContext); - // Run pre enhanced feign plugins. - pluginRunner.run(PRE, enhancedFeignContext); + // Run pre enhanced plugins. + pluginRunner.run(PRE, enhancedPluginContext); + long startMillis = System.currentTimeMillis(); try { - long startMillis = System.currentTimeMillis(); Response response = delegate.execute(request, options); - enhancedFeignContext.setDelay(System.currentTimeMillis() - startMillis); - enhancedFeignContext.setResponse(response); + enhancedPluginContext.setDelay(System.currentTimeMillis() - startMillis); + + HttpHeaders responseHeaders = new HttpHeaders(); + response.headers().forEach((s, strings) -> responseHeaders.addAll(s, new ArrayList<>(strings))); + + EnhancedResponseContext enhancedResponseContext = EnhancedResponseContext.builder() + .httpStatus(response.status()) + .httpHeaders(responseHeaders) + .build(); + enhancedPluginContext.setResponse(enhancedResponseContext); + + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(request.requestTemplate().feignTarget().name()); + serviceInstance.setHost(url.getHost()); + serviceInstance.setPort(url.getPort()); + enhancedPluginContext.setServiceInstance(serviceInstance); - // Run post enhanced feign plugins. - pluginRunner.run(POST, enhancedFeignContext); + // Run post enhanced plugins. + pluginRunner.run(POST, enhancedPluginContext); return response; } catch (IOException origin) { - enhancedFeignContext.setException(origin); + enhancedPluginContext.setDelay(System.currentTimeMillis() - startMillis); + enhancedPluginContext.setThrowable(origin); // Run exception enhanced feign plugins. - pluginRunner.run(EXCEPTION, enhancedFeignContext); + pluginRunner.run(EXCEPTION, enhancedPluginContext); throw origin; } finally { - // Run finally enhanced feign plugins. - pluginRunner.run(FINALLY, enhancedFeignContext); + // Run finally enhanced plugins. + pluginRunner.run(FINALLY, enhancedPluginContext); } } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignContext.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignContext.java deleted file mode 100644 index d1d9aded3..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignContext.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.feign.plugin; - -import feign.Request; -import feign.Response; - -/** - * Context used by EnhancedFeignPlugin. - * - * @author Haotian Zhang - */ -public class EnhancedFeignContext { - - private Request request; - - private Request.Options options; - - private Response response; - - private Exception exception; - - private long delay; - - public Request getRequest() { - return request; - } - - public void setRequest(Request request) { - this.request = request; - } - - public Request.Options getOptions() { - return options; - } - - public void setOptions(Request.Options options) { - this.options = options; - } - - public Response getResponse() { - return response; - } - - public void setResponse(Response response) { - this.response = response; - } - - public Exception getException() { - return exception; - } - - public void setException(Exception exception) { - this.exception = exception; - } - - public long getDelay() { - return delay; - } - - public void setDelay(long delay) { - this.delay = delay; - } -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ExceptionPolarisReporter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ExceptionPolarisReporter.java deleted file mode 100644 index 487c64134..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ExceptionPolarisReporter.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; - -import java.net.SocketTimeoutException; -import java.util.ArrayList; - -import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.pojo.RetStatus; -import com.tencent.polaris.api.rpc.ServiceCallResult; -import com.tencent.polaris.client.api.SDKContext; -import feign.Request; -import feign.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.core.Ordered; -import org.springframework.http.HttpHeaders; - -/** - * Polaris reporter when feign call fails. - * - * @author Haotian Zhang - */ -public class ExceptionPolarisReporter extends AbstractPolarisReporterAdapter implements EnhancedFeignPlugin { - - private static final Logger LOG = LoggerFactory.getLogger(ExceptionPolarisReporter.class); - private final RpcEnhancementReporterProperties reporterProperties; - - private final ConsumerAPI consumerAPI; - - private final SDKContext context; - - - public ExceptionPolarisReporter(RpcEnhancementReporterProperties reporterProperties, - SDKContext context, - ConsumerAPI consumerAPI) { - super(reporterProperties); - this.reporterProperties = reporterProperties; - this.context = context; - this.consumerAPI = consumerAPI; - } - - @Override - public String getName() { - return ExceptionPolarisReporter.class.getName(); - } - - @Override - public EnhancedFeignPluginType getType() { - return EnhancedFeignPluginType.EXCEPTION; - } - - @Override - public void run(EnhancedFeignContext context) { - if (!reporterProperties.isEnabled()) { - return; - } - - if (consumerAPI != null) { - Request request = context.getRequest(); - Response response = context.getResponse(); - Exception exception = context.getException(); - RetStatus retStatus = RetStatus.RetFail; - long delay = context.getDelay(); - if (exception instanceof SocketTimeoutException) { - retStatus = RetStatus.RetTimeout; - } - LOG.debug("Will report result of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", retStatus.name(), request.httpMethod() - .name(), request.url(), response.status(), delay); - ServiceCallResult resultRequest = ReporterUtils.createServiceCallResult(this.context, request, response, - delay, retStatus, serviceCallResult -> { - HttpHeaders headers = new HttpHeaders(); - response.headers().forEach((s, strings) -> headers.addAll(s, new ArrayList<>(strings))); - serviceCallResult.setRetStatus(getRetStatusFromRequest(headers, serviceCallResult.getRetStatus())); - serviceCallResult.setRuleName(getActiveRuleNameFromRequest(headers)); - }); - consumerAPI.updateServiceCallResult(resultRequest); - } - } - - @Override - public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) { - Request request = context.getRequest(); - Response response = context.getResponse(); - LOG.error("ExceptionPolarisReporter runs failed. Request=[{}]. Response=[{}].", request, response, throwable); - } - - @Override - public int getOrder() { - return Ordered.HIGHEST_PRECEDENCE + 1; - } -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ReporterUtils.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ReporterUtils.java deleted file mode 100644 index 7d6a94b0e..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ReporterUtils.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLDecoder; -import java.util.Collection; -import java.util.Objects; -import java.util.function.Consumer; - -import com.tencent.cloud.common.constant.RouterConstant; -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.util.RequestLabelUtils; -import com.tencent.polaris.api.pojo.RetStatus; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.api.rpc.ServiceCallResult; -import com.tencent.polaris.api.utils.CollectionUtils; -import com.tencent.polaris.client.api.SDKContext; -import feign.Request; -import feign.RequestTemplate; -import feign.Response; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; - -/** - * Util for polaris reporter. - * - * @author Haotian Zhang - */ -public final class ReporterUtils { - - private static final Logger LOGGER = LoggerFactory.getLogger(ReporterUtils.class); - - private ReporterUtils() { - } - - public static ServiceCallResult createServiceCallResult(final SDKContext context, final Request request, - final Response response, long delay, RetStatus retStatus, final Consumer consumer) { - ServiceCallResult resultRequest = new ServiceCallResult(); - - resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE); - RequestTemplate requestTemplate = request.requestTemplate(); - String serviceName = requestTemplate.feignTarget().name(); - resultRequest.setService(serviceName); - Collection labels = requestTemplate.headers().get(RouterConstant.ROUTER_LABEL_HEADER); - if (CollectionUtils.isNotEmpty(labels) && labels.iterator().hasNext()) { - String label = labels.iterator().next(); - try { - label = URLDecoder.decode(label, UTF_8); - } - catch (UnsupportedEncodingException e) { - LOGGER.error("unsupported charset exception " + UTF_8, e); - } - resultRequest.setLabels(RequestLabelUtils.convertLabel(label)); - } - URI uri = URI.create(request.url()); - resultRequest.setMethod(uri.getPath()); - resultRequest.setRetCode(response.status()); - resultRequest.setRetStatus(retStatus); - resultRequest.setDelay(delay); - String scheme = uri.getScheme(); - if (StringUtils.isBlank(scheme)) { - scheme = "http"; - } - resultRequest.setProtocol(scheme); - String sourceNamespace = MetadataContext.LOCAL_NAMESPACE; - String sourceService = MetadataContext.LOCAL_SERVICE; - if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) { - resultRequest.setCallerService(new ServiceKey(sourceNamespace, sourceService)); - } - if (Objects.nonNull(context)) { - resultRequest.setCallerIp(context.getConfig().getGlobal().getAPI().getBindIP()); - } - resultRequest.setHost(uri.getHost()); - // -1 means access directly by url, and use http default port number 80 - resultRequest.setPort(uri.getPort() == -1 ? 80 : uri.getPort()); - consumer.accept(resultRequest); - return resultRequest; - } - -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/SuccessPolarisReporter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/SuccessPolarisReporter.java deleted file mode 100644 index eaa0ae916..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/SuccessPolarisReporter.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; - -import java.util.ArrayList; - -import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.pojo.RetStatus; -import com.tencent.polaris.api.rpc.ServiceCallResult; -import com.tencent.polaris.client.api.SDKContext; -import feign.Request; -import feign.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.core.Ordered; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; - -/** - * Polaris reporter when feign call is successful. - * - * @author Haotian Zhang - */ -public class SuccessPolarisReporter extends AbstractPolarisReporterAdapter implements EnhancedFeignPlugin { - - private static final Logger LOG = LoggerFactory.getLogger(SuccessPolarisReporter.class); - - private final ConsumerAPI consumerAPI; - - private final SDKContext context; - - public SuccessPolarisReporter(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { - super(properties); - this.context = context; - this.consumerAPI = consumerAPI; - } - - @Override - public String getName() { - return SuccessPolarisReporter.class.getName(); - } - - @Override - public EnhancedFeignPluginType getType() { - return EnhancedFeignPluginType.POST; - } - - @Override - public void run(EnhancedFeignContext context) { - if (!reportProperties.isEnabled()) { - return; - } - - if (consumerAPI != null) { - Request request = context.getRequest(); - Response response = context.getResponse(); - RetStatus retStatus = RetStatus.RetSuccess; - long delay = context.getDelay(); - if (apply(HttpStatus.resolve(response.status()))) { - retStatus = RetStatus.RetFail; - } - LOG.debug("Will report result of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", retStatus.name(), request.httpMethod() - .name(), request.url(), response.status(), delay); - ServiceCallResult resultRequest = ReporterUtils.createServiceCallResult(this.context, request, response, - delay, retStatus, serviceCallResult -> { - HttpHeaders headers = new HttpHeaders(); - response.headers().forEach((s, strings) -> headers.addAll(s, new ArrayList<>(strings))); - serviceCallResult.setRetStatus(getRetStatusFromRequest(headers, serviceCallResult.getRetStatus())); - serviceCallResult.setRuleName(getActiveRuleNameFromRequest(headers)); - }); - consumerAPI.updateServiceCallResult(resultRequest); - } - } - - @Override - public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) { - Request request = context.getRequest(); - Response response = context.getResponse(); - LOG.error("SuccessPolarisReporter runs failed. Request=[{}]. Response=[{}].", request, response, throwable); - } - - @Override - public int getOrder() { - return Ordered.HIGHEST_PRECEDENCE + 1; - } -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/DefaultEnhancedFeignPluginRunner.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/DefaultEnhancedPluginRunner.java similarity index 61% rename from spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/DefaultEnhancedFeignPluginRunner.java rename to spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/DefaultEnhancedPluginRunner.java index dbaf7d580..ca34b2af1 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/DefaultEnhancedFeignPluginRunner.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/DefaultEnhancedPluginRunner.java @@ -16,16 +16,13 @@ * */ -package com.tencent.cloud.rpc.enhancement.feign; +package com.tencent.cloud.rpc.enhancement.plugin; import java.util.Comparator; import java.util.List; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType; import org.springframework.util.CollectionUtils; @@ -34,14 +31,14 @@ import org.springframework.util.CollectionUtils; * * @author Derek Yi 2022-08-16 */ -public class DefaultEnhancedFeignPluginRunner implements EnhancedFeignPluginRunner { +public class DefaultEnhancedPluginRunner implements EnhancedPluginRunner { - private final Multimap pluginMap = ArrayListMultimap.create(); + private final Multimap pluginMap = ArrayListMultimap.create(); - public DefaultEnhancedFeignPluginRunner(List enhancedFeignPlugins) { - if (!CollectionUtils.isEmpty(enhancedFeignPlugins)) { - enhancedFeignPlugins.stream() - .sorted(Comparator.comparing(EnhancedFeignPlugin::getOrder)) + public DefaultEnhancedPluginRunner(List enhancedPlugins) { + if (!CollectionUtils.isEmpty(enhancedPlugins)) { + enhancedPlugins.stream() + .sorted(Comparator.comparing(EnhancedPlugin::getOrder)) .forEach(plugin -> pluginMap.put(plugin.getType().name(), plugin)); } } @@ -53,8 +50,8 @@ public class DefaultEnhancedFeignPluginRunner implements EnhancedFeignPluginRunn * @param context context in enhanced feign client. */ @Override - public void run(EnhancedFeignPluginType pluginType, EnhancedFeignContext context) { - for (EnhancedFeignPlugin plugin : pluginMap.get(pluginType.name())) { + public void run(EnhancedPluginType pluginType, EnhancedPluginContext context) { + for (EnhancedPlugin plugin : pluginMap.get(pluginType.name())) { try { plugin.run(context); } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignPlugin.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPlugin.java similarity index 75% rename from spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignPlugin.java rename to spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPlugin.java index 1ecc3d355..39bce26a3 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignPlugin.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPlugin.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.rpc.enhancement.feign.plugin; +package com.tencent.cloud.rpc.enhancement.plugin; import org.springframework.core.Ordered; @@ -24,7 +24,7 @@ import org.springframework.core.Ordered; * * @author Haotian Zhang */ -public interface EnhancedFeignPlugin extends Ordered { +public interface EnhancedPlugin extends Ordered { /** * Get name of plugin. @@ -38,9 +38,9 @@ public interface EnhancedFeignPlugin extends Ordered { /** * Get type of plugin. * - * @return {@link EnhancedFeignPluginType} + * @return {@link EnhancedPluginType} */ - EnhancedFeignPluginType getType(); + EnhancedPluginType getType(); /** * Run the plugin. @@ -48,15 +48,15 @@ public interface EnhancedFeignPlugin extends Ordered { * @param context context in enhanced feign client. * @throws Throwable throwable thrown from run method. */ - void run(EnhancedFeignContext context) throws Throwable; + void run(EnhancedPluginContext context) throws Throwable; /** - * Handler throwable from {@link EnhancedFeignPlugin#run(EnhancedFeignContext)}. + * Handler throwable from {@link EnhancedPlugin#run(EnhancedPluginContext)}. * * @param context context in enhanced feign client. * @param throwable throwable thrown from run method. */ - default void handlerThrowable(EnhancedFeignContext context, Throwable throwable) { + default void handlerThrowable(EnhancedPluginContext context, Throwable throwable) { } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java new file mode 100644 index 000000000..fa4e891d0 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java @@ -0,0 +1,90 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.plugin; + + +import org.springframework.cloud.client.ServiceInstance; + +/** + * Context used by EnhancedPlugin. + * + * @author Haotian Zhang + */ +public class EnhancedPluginContext { + + private EnhancedRequestContext request; + + private EnhancedResponseContext response; + + private Throwable throwable; + + private long delay; + + private ServiceInstance serviceInstance; + + public EnhancedRequestContext getRequest() { + return request; + } + + public void setRequest(EnhancedRequestContext request) { + this.request = request; + } + + public EnhancedResponseContext getResponse() { + return response; + } + + public void setResponse(EnhancedResponseContext response) { + this.response = response; + } + + public Throwable getThrowable() { + return throwable; + } + + public void setThrowable(Throwable throwable) { + this.throwable = throwable; + } + + public long getDelay() { + return delay; + } + + public void setDelay(long delay) { + this.delay = delay; + } + + public ServiceInstance getServiceInstance() { + return serviceInstance; + } + + public void setServiceInstance(ServiceInstance serviceInstance) { + this.serviceInstance = serviceInstance; + } + + @Override + public String toString() { + return "EnhancedPluginContext{" + + "request=" + request + + ", response=" + response + + ", throwable=" + throwable + + ", delay=" + delay + + ", serviceInstance=" + serviceInstance + + '}'; + } +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignPluginRunner.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginRunner.java similarity index 73% rename from spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignPluginRunner.java rename to spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginRunner.java index b821827b2..e15af39e5 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignPluginRunner.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginRunner.java @@ -16,17 +16,14 @@ * */ -package com.tencent.cloud.rpc.enhancement.feign; - -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType; +package com.tencent.cloud.rpc.enhancement.plugin; /** * Plugin runner. * * @author Derek Yi 2022-08-16 */ -public interface EnhancedFeignPluginRunner { +public interface EnhancedPluginRunner { /** * run the plugin. @@ -34,5 +31,5 @@ public interface EnhancedFeignPluginRunner { * @param pluginType type of plugin * @param context context in enhanced feign client. */ - void run(EnhancedFeignPluginType pluginType, EnhancedFeignContext context); + void run(EnhancedPluginType pluginType, EnhancedPluginContext context); } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignPluginType.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginType.java similarity index 88% rename from spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignPluginType.java rename to spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginType.java index fef181d83..64dc2ecb9 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignPluginType.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginType.java @@ -15,14 +15,14 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.rpc.enhancement.feign.plugin; +package com.tencent.cloud.rpc.enhancement.plugin; /** - * Type of EnhancedFeignPlugin. + * Type of EnhancedPlugin. * * @author Haotian Zhang */ -public enum EnhancedFeignPluginType { +public enum EnhancedPluginType { /** * Pre feign plugin. 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 new file mode 100644 index 000000000..74c9aff63 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedRequestContext.java @@ -0,0 +1,107 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.plugin; + +import java.net.URI; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; + +/** + * EnhancedRequestContext. + * + * @author sean yu + */ +public class EnhancedRequestContext { + + private HttpMethod httpMethod; + + private HttpHeaders httpHeaders; + + private URI url; + + public HttpMethod getHttpMethod() { + return httpMethod; + } + + public void setHttpMethod(HttpMethod httpMethod) { + this.httpMethod = httpMethod; + } + + public HttpHeaders getHttpHeaders() { + return httpHeaders; + } + + public void setHttpHeaders(HttpHeaders httpHeaders) { + this.httpHeaders = httpHeaders; + } + + public URI getUrl() { + return url; + } + + public void setUrl(URI url) { + this.url = url; + } + + public static EnhancedContextRequestBuilder builder() { + return new EnhancedContextRequestBuilder(); + } + + @Override + public String toString() { + return "EnhancedRequestContext{" + + "httpMethod=" + httpMethod + + ", httpHeaders=" + httpHeaders + + ", url=" + url + + '}'; + } + + public static final class EnhancedContextRequestBuilder { + private HttpMethod httpMethod; + private HttpHeaders httpHeaders; + private URI url; + + private EnhancedContextRequestBuilder() { + } + + public EnhancedContextRequestBuilder httpMethod(HttpMethod httpMethod) { + this.httpMethod = httpMethod; + return this; + } + + public EnhancedContextRequestBuilder httpHeaders(HttpHeaders httpHeaders) { + this.httpHeaders = httpHeaders; + return this; + } + + public EnhancedContextRequestBuilder url(URI url) { + this.url = url; + return this; + } + + public EnhancedRequestContext build() { + EnhancedRequestContext enhancedRequestContext = new EnhancedRequestContext(); + enhancedRequestContext.httpMethod = this.httpMethod; + enhancedRequestContext.url = this.url; + enhancedRequestContext.httpHeaders = this.httpHeaders; + return enhancedRequestContext; + } + } + +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedResponseContext.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedResponseContext.java new file mode 100644 index 000000000..c8773776a --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedResponseContext.java @@ -0,0 +1,86 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.plugin; + +import org.springframework.http.HttpHeaders; + +/** + * EnhancedResponseContext. + * + * @author sean yu + */ +public class EnhancedResponseContext { + + private Integer httpStatus; + + private HttpHeaders httpHeaders; + + public Integer getHttpStatus() { + return httpStatus; + } + + public void setHttpStatus(Integer httpStatus) { + this.httpStatus = httpStatus; + } + + public HttpHeaders getHttpHeaders() { + return httpHeaders; + } + + public void setHttpHeaders(HttpHeaders httpHeaders) { + this.httpHeaders = httpHeaders; + } + + public static EnhancedContextResponseBuilder builder() { + return new EnhancedContextResponseBuilder(); + } + + @Override + public String toString() { + return "EnhancedResponseContext{" + + "httpStatus=" + httpStatus + + ", httpHeaders=" + httpHeaders + + '}'; + } + + public static final class EnhancedContextResponseBuilder { + private Integer httpStatus; + private HttpHeaders httpHeaders; + + private EnhancedContextResponseBuilder() { + } + + public EnhancedContextResponseBuilder httpStatus(Integer httpStatus) { + this.httpStatus = httpStatus; + return this; + } + + public EnhancedContextResponseBuilder httpHeaders(HttpHeaders httpHeaders) { + this.httpHeaders = httpHeaders; + return this; + } + + public EnhancedResponseContext build() { + EnhancedResponseContext enhancedResponseContext = new EnhancedResponseContext(); + enhancedResponseContext.setHttpStatus(httpStatus); + enhancedResponseContext.setHttpHeaders(httpHeaders); + return enhancedResponseContext; + } + } + +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/reporter/ExceptionPolarisReporter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/reporter/ExceptionPolarisReporter.java new file mode 100644 index 000000000..76c4250b3 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/reporter/ExceptionPolarisReporter.java @@ -0,0 +1,106 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.plugin.reporter; + + +import java.util.Optional; + +import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +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.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.client.api.SDKContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.core.Ordered; + +/** + * Polaris reporter when feign call fails. + * + * @author Haotian Zhang + */ +public class ExceptionPolarisReporter extends AbstractPolarisReporterAdapter implements EnhancedPlugin { + + private static final Logger LOG = LoggerFactory.getLogger(ExceptionPolarisReporter.class); + + private final ConsumerAPI consumerAPI; + + public ExceptionPolarisReporter(RpcEnhancementReporterProperties reporterProperties, + SDKContext context, + ConsumerAPI consumerAPI) { + super(reporterProperties, context); + this.consumerAPI = consumerAPI; + } + + @Override + public String getName() { + return ExceptionPolarisReporter.class.getName(); + } + + @Override + public EnhancedPluginType getType() { + return EnhancedPluginType.EXCEPTION; + } + + @Override + public void run(EnhancedPluginContext context) { + if (!super.reportProperties.isEnabled()) { + return; + } + + EnhancedRequestContext request = context.getRequest(); + ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance()).orElse(new DefaultServiceInstance()); + + ServiceCallResult resultRequest = createServiceCallResult( + serviceInstance.getServiceId(), + serviceInstance.getHost(), + serviceInstance.getPort(), + request.getUrl(), + request.getHttpHeaders(), + null, + null, + context.getDelay(), + context.getThrowable() + ); + + LOG.debug("Will report ServiceCallResult of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", + resultRequest.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl().getPath(), context.getThrowable().getMessage(), context.getDelay()); + + consumerAPI.updateServiceCallResult(resultRequest); + + } + + @Override + public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) { + LOG.error("ExceptionPolarisReporter runs failed. context=[{}].", + context, throwable); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE + 1; + } + +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/reporter/SuccessPolarisReporter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/reporter/SuccessPolarisReporter.java new file mode 100644 index 000000000..4b35396da --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/reporter/SuccessPolarisReporter.java @@ -0,0 +1,106 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.plugin.reporter; + +import java.util.Optional; + +import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +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.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.client.api.SDKContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.core.Ordered; + +/** + * Polaris reporter when feign call is successful. + * + * @author Haotian Zhang + */ +public class SuccessPolarisReporter extends AbstractPolarisReporterAdapter implements EnhancedPlugin { + + private static final Logger LOG = LoggerFactory.getLogger(SuccessPolarisReporter.class); + + private final ConsumerAPI consumerAPI; + + public SuccessPolarisReporter(RpcEnhancementReporterProperties properties, + SDKContext context, + ConsumerAPI consumerAPI) { + super(properties, context); + this.consumerAPI = consumerAPI; + } + + @Override + public String getName() { + return SuccessPolarisReporter.class.getName(); + } + + @Override + public EnhancedPluginType getType() { + return EnhancedPluginType.POST; + } + + @Override + public void run(EnhancedPluginContext context) { + if (!super.reportProperties.isEnabled()) { + return; + } + + EnhancedRequestContext request = context.getRequest(); + EnhancedResponseContext response = context.getResponse(); + ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance()).orElse(new DefaultServiceInstance()); + + ServiceCallResult resultRequest = createServiceCallResult( + serviceInstance.getServiceId(), + serviceInstance.getHost(), + serviceInstance.getPort(), + request.getUrl(), + request.getHttpHeaders(), + response.getHttpHeaders(), + response.getHttpStatus(), + context.getDelay(), + null + ); + + LOG.debug("Will report ServiceCallResult of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", + resultRequest.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl().getPath(), response.getHttpStatus(), context.getDelay()); + + consumerAPI.updateServiceCallResult(resultRequest); + + } + + @Override + public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) { + LOG.error("SuccessPolarisReporter runs failed. context=[{}].", + context, throwable); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE + 1; + } +} 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/EnhancedRestTemplateInterceptor.java new file mode 100644 index 000000000..e67e241f2 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java @@ -0,0 +1,105 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.resttemplate; + +import java.io.IOException; +import java.util.Map; + +import com.tencent.cloud.common.constant.HeaderConstant; +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.EnhancedRequestContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; + +import org.springframework.cloud.client.DefaultServiceInstance; +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.plugin.EnhancedPluginType.EXCEPTION; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.FINALLY; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.POST; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.PRE; + +/** + * EnhancedRestTemplateInterceptor. + * + * @author sean yu + */ +public class EnhancedRestTemplateInterceptor implements ClientHttpRequestInterceptor { + + private final EnhancedPluginRunner pluginRunner; + + public EnhancedRestTemplateInterceptor(EnhancedPluginRunner pluginRunner) { + this.pluginRunner = pluginRunner; + } + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + + EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext(); + + EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder() + .httpHeaders(request.getHeaders()) + .httpMethod(request.getMethod()) + .url(request.getURI()) + .build(); + enhancedPluginContext.setRequest(enhancedRequestContext); + + // Run pre enhanced plugins. + pluginRunner.run(PRE, enhancedPluginContext); + long startMillis = System.currentTimeMillis(); + try { + ClientHttpResponse response = execution.execute(request, body); + enhancedPluginContext.setDelay(System.currentTimeMillis() - startMillis); + + EnhancedResponseContext enhancedResponseContext = EnhancedResponseContext.builder() + .httpStatus(response.getRawStatusCode()) + .httpHeaders(response.getHeaders()) + .build(); + enhancedPluginContext.setResponse(enhancedResponseContext); + + Map loadBalancerContext = MetadataContextHolder.get().getLoadbalancerMetadata(); + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(request.getURI().getHost()); + serviceInstance.setHost(loadBalancerContext.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST)); + if (loadBalancerContext.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT) != null) { + serviceInstance.setPort(Integer.parseInt(loadBalancerContext.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT))); + } + enhancedPluginContext.setServiceInstance(serviceInstance); + + // Run post enhanced plugins. + pluginRunner.run(POST, enhancedPluginContext); + return response; + } + catch (IOException e) { + enhancedPluginContext.setDelay(System.currentTimeMillis() - startMillis); + enhancedPluginContext.setThrowable(e); + // Run exception enhanced plugins. + pluginRunner.run(EXCEPTION, enhancedPluginContext); + throw e; + } + finally { + // Run finally enhanced plugins. + pluginRunner.run(FINALLY, enhancedPluginContext); + } + } + +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporter.java deleted file mode 100644 index 935cd3219..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporter.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.resttemplate; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLDecoder; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -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.RequestLabelUtils; -import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.pojo.RetStatus; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.api.rpc.ServiceCallResult; -import com.tencent.polaris.api.utils.CollectionUtils; -import com.tencent.polaris.client.api.SDKContext; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.http.HttpMethod; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.lang.NonNull; -import org.springframework.web.client.DefaultResponseErrorHandler; -import org.springframework.web.client.ResponseErrorHandler; - -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; - -/** - * Extend ResponseErrorHandler to get request information. - * - * @author wh 2022/6/21 - */ -public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter implements ResponseErrorHandler, ApplicationContextAware { - - /** - * Polaris-CircuitBreaker-Fallback header flag. - */ - public static final String POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER = "X-SCT-Polaris-CircuitBreaker-Fallback"; - /** - * response has error header flag, since EnhancedRestTemplateReporter#hasError always return true. - */ - public static final String HEADER_HAS_ERROR = "X-SCT-Has-Error"; - private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedRestTemplateReporter.class); - private final ConsumerAPI consumerAPI; - private final SDKContext context; - private ResponseErrorHandler delegateHandler; - - public EnhancedRestTemplateReporter(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { - super(properties); - this.context = context; - this.consumerAPI = consumerAPI; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - String[] handlerBeanNames = applicationContext.getBeanNamesForType(ResponseErrorHandler.class); - if (handlerBeanNames.length == 1) { - if (this.delegateHandler == null) { - this.delegateHandler = new DefaultResponseErrorHandler(); - } - return; - } - - // inject user custom ResponseErrorHandler - for (String beanName : handlerBeanNames) { - // ignore self - if (StringUtils.equalsIgnoreCase("enhancedRestTemplateReporter", beanName)) { - continue; - } - this.delegateHandler = (ResponseErrorHandler) applicationContext.getBean(beanName); - } - } - - @Override - public boolean hasError(@NonNull ClientHttpResponse response) throws IOException { - if (delegateHandler != null) { - // Preserve the delegated handler result - boolean hasError = delegateHandler.hasError(response); - response.getHeaders().add(HEADER_HAS_ERROR, String.valueOf(hasError)); - } - return true; - } - - @Override - public void handleError(@NonNull ClientHttpResponse response) throws IOException { - if (realHasError(response)) { - delegateHandler.handleError(response); - } - - clear(response); - } - - @Override - public void handleError(@NonNull URI url, @NonNull HttpMethod method, @NonNull ClientHttpResponse response) throws IOException { - // report result to polaris - if (reportProperties.isEnabled()) { - reportResult(url, response); - } - - // invoke delegate handler - invokeDelegateHandler(url, method, response); - } - - private void reportResult(URI url, ClientHttpResponse response) { - if (Boolean.parseBoolean(response.getHeaders().getFirst(POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER))) { - return; - } - try { - ServiceCallResult resultRequest = createServiceCallResult(url, response); - Map loadBalancerContext = MetadataContextHolder.get().getLoadbalancerMetadata(); - - String targetHost = loadBalancerContext.get("host"); - String targetPort = loadBalancerContext.get("port"); - String startMillis = loadBalancerContext.get("startMillis"); - long delay = System.currentTimeMillis() - Long.parseLong(startMillis); - - if (StringUtils.isBlank(targetHost) || StringUtils.isBlank(targetPort)) { - LOGGER.warn("Can not get target host or port from metadata context. host = {}, port = {}", targetHost, targetPort); - return; - } - - resultRequest.setHost(targetHost); - resultRequest.setPort(Integer.parseInt(targetPort)); - resultRequest.setDelay(delay); - - // checking response http status code - if (apply(response.getStatusCode())) { - resultRequest.setRetStatus(RetStatus.RetFail); - } - resultRequest.setRetStatus(getRetStatusFromRequest(response.getHeaders(), resultRequest.getRetStatus())); - resultRequest.setRuleName(getActiveRuleNameFromRequest(response.getHeaders())); - if (Objects.nonNull(context)) { - resultRequest.setCallerIp(context.getConfig().getGlobal().getAPI().getBindIP()); - } - - List labels = response.getHeaders().get(RouterConstant.ROUTER_LABEL_HEADER); - if (CollectionUtils.isNotEmpty(labels)) { - String label = labels.get(0); - try { - label = URLDecoder.decode(label, UTF_8); - } - catch (UnsupportedEncodingException e) { - LOGGER.error("unsupported charset exception " + UTF_8, e); - } - resultRequest.setLabels(RequestLabelUtils.convertLabel(label)); - } - - // processing report with consumerAPI . - LOGGER.debug("Will report result of {}. Request=[{}]. Response=[{}]. Delay=[{}]ms.", resultRequest.getRetStatus() - .name(), url, response.getStatusCode().value(), delay); - consumerAPI.updateServiceCallResult(resultRequest); - } - catch (Exception e) { - LOGGER.error("RestTemplate response reporter execute failed of {} url {}", response, url, e); - } - } - - private void invokeDelegateHandler(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { - if (realHasError(response)) { - delegateHandler.handleError(url, method, response); - } - - clear(response); - } - - private Boolean realHasError(ClientHttpResponse response) { - if (delegateHandler == null) { - return false; - } - - String hasErrorHeader = response.getHeaders().getFirst(HEADER_HAS_ERROR); - if (StringUtils.isBlank(hasErrorHeader)) { - return false; - } - - return Boolean.parseBoolean(hasErrorHeader); - } - - private void clear(ClientHttpResponse response) { - response.getHeaders().remove(HEADER_HAS_ERROR); - response.getHeaders().remove(POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER); - } - - private ServiceCallResult createServiceCallResult(URI uri, ClientHttpResponse response) throws IOException { - ServiceCallResult resultRequest = new ServiceCallResult(); - String serviceName = uri.getHost(); - resultRequest.setService(serviceName); - resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE); - resultRequest.setMethod(uri.getPath()); - resultRequest.setRetCode(response.getStatusCode().value()); - resultRequest.setRetStatus(RetStatus.RetSuccess); - String sourceNamespace = MetadataContext.LOCAL_NAMESPACE; - String sourceService = MetadataContext.LOCAL_SERVICE; - if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) { - resultRequest.setCallerService(new ServiceKey(sourceNamespace, sourceService)); - } - return resultRequest; - } - - protected ResponseErrorHandler getDelegateHandler() { - return this.delegateHandler; - } - - protected void setDelegateHandler(ResponseErrorHandler delegateHandler) { - this.delegateHandler = delegateHandler; - } - -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/LoadBalancerClientAspectUtils.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/LoadBalancerClientAspectUtils.java index ff44d659c..04ffac0cf 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/LoadBalancerClientAspectUtils.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/LoadBalancerClientAspectUtils.java @@ -17,6 +17,7 @@ package com.tencent.cloud.rpc.enhancement.resttemplate; +import com.tencent.cloud.common.constant.HeaderConstant; import com.tencent.cloud.common.metadata.MetadataContextHolder; import org.aspectj.lang.ProceedingJoinPoint; @@ -36,9 +37,8 @@ public final class LoadBalancerClientAspectUtils { Object server = joinPoint.getArgs()[0]; if (server instanceof ServiceInstance) { ServiceInstance instance = (ServiceInstance) server; - MetadataContextHolder.get().setLoadbalancer("host", instance.getHost()); - MetadataContextHolder.get().setLoadbalancer("port", String.valueOf(instance.getPort())); - MetadataContextHolder.get().setLoadbalancer("startMillis", String.valueOf(System.currentTimeMillis())); + MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST, instance.getHost()); + MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT, String.valueOf(instance.getPort())); } } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/PolarisResponseErrorHandler.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/PolarisResponseErrorHandler.java deleted file mode 100644 index fa49521eb..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/PolarisResponseErrorHandler.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.resttemplate; - -import org.springframework.web.client.ResponseErrorHandler; - -/** - * Polaris Response Error Handler Definition Of {@link ResponseErrorHandler}. - * - * @author wh 2022/6/21 - */ -public interface PolarisResponseErrorHandler extends ResponseErrorHandler { - -} 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 new file mode 100644 index 000000000..bfe816f32 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java @@ -0,0 +1,104 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.scg; + +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; +import reactor.core.publisher.Mono; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.web.server.ServerWebExchange; + +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.EXCEPTION; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.FINALLY; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.POST; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.PRE; +import static org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR; + +/** + * EnhancedGatewayGlobalFilter. + * + * @author sean yu + */ +public class EnhancedGatewayGlobalFilter implements GlobalFilter, Ordered { + + private final EnhancedPluginRunner pluginRunner; + + public EnhancedGatewayGlobalFilter(EnhancedPluginRunner pluginRunner) { + this.pluginRunner = pluginRunner; + } + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext(); + + EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder() + .httpHeaders(exchange.getRequest().getHeaders()) + .httpMethod(exchange.getRequest().getMethod()) + .url(exchange.getRequest().getURI()) + .build(); + enhancedPluginContext.setRequest(enhancedRequestContext); + + // Run pre enhanced plugins. + pluginRunner.run(PRE, enhancedPluginContext); + + long startTime = System.currentTimeMillis(); + return chain.filter(exchange) + .doOnSubscribe(v -> { + Response serviceInstanceResponse = exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR); + if (serviceInstanceResponse != null && serviceInstanceResponse.hasServer()) { + ServiceInstance instance = serviceInstanceResponse.getServer(); + enhancedPluginContext.setServiceInstance(instance); + } + }) + .doOnSuccess(v -> { + enhancedPluginContext.setDelay(System.currentTimeMillis() - startTime); + EnhancedResponseContext enhancedResponseContext = EnhancedResponseContext.builder() + .httpStatus(exchange.getResponse().getRawStatusCode()) + .httpHeaders(exchange.getResponse().getHeaders()) + .build(); + enhancedPluginContext.setResponse(enhancedResponseContext); + + // Run post enhanced plugins. + pluginRunner.run(POST, enhancedPluginContext); + }) + .doOnError(t -> { + enhancedPluginContext.setDelay(System.currentTimeMillis() - startTime); + enhancedPluginContext.setThrowable(t); + + // Run exception enhanced plugins. + pluginRunner.run(EXCEPTION, enhancedPluginContext); + }) + .doFinally(v -> { + // Run finally enhanced plugins. + pluginRunner.run(FINALLY, enhancedPluginContext); + }); + } + + @Override + public int getOrder() { + return LOAD_BALANCER_CLIENT_FILTER_ORDER + 1; + } +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpClient.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpClient.java deleted file mode 100644 index 33b2b556c..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpClient.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.scg; - -import java.net.SocketTimeoutException; -import java.util.Map; -import java.util.Objects; -import java.util.function.BiConsumer; - -import com.tencent.cloud.common.constant.HeaderConstant; -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.metadata.MetadataContextHolder; -import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.pojo.RetStatus; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.api.rpc.ServiceCallResult; -import com.tencent.polaris.client.api.SDKContext; -import io.netty.handler.codec.http.HttpHeaders; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import reactor.netty.http.client.HttpClient; -import reactor.netty.http.client.HttpClientConfig; -import reactor.netty.http.client.HttpClientResponse; - -import org.springframework.http.HttpStatus; - -public class EnhancedPolarisHttpClient extends HttpClient { - - private static final Logger LOG = LoggerFactory.getLogger(EnhancedPolarisHttpClient.class); - - private final RpcEnhancementReporterProperties properties; - private final SDKContext context; - private final ConsumerAPI consumerAPI; - private final Reporter adapter; - private final BiConsumer handler = new BiConsumer() { - @Override - public void accept(HttpClientResponse httpClientResponse, Throwable throwable) { - if (Objects.isNull(consumerAPI)) { - return; - } - HttpHeaders responseHeaders = httpClientResponse.responseHeaders(); - - ServiceCallResult result = new ServiceCallResult(); - result.setCallerService(new ServiceKey(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE)); - result.setNamespace(MetadataContext.LOCAL_NAMESPACE); - - Map metadata = MetadataContextHolder.get().getLoadbalancerMetadata(); - result.setDelay(System.currentTimeMillis() - Long.parseLong(metadata.get("startTime"))); - result.setService(metadata.get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID)); - result.setHost(metadata.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST)); - result.setPort(Integer.parseInt(metadata.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT))); - RetStatus status = RetStatus.RetSuccess; - if (Objects.isNull(throwable)) { - if (EnhancedPolarisHttpClient.this.adapter.apply(HttpStatus.valueOf(httpClientResponse.status() - .code()))) { - status = RetStatus.RetFail; - } - org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders(); - responseHeaders.forEach(entry -> headers.add(entry.getKey(), entry.getValue())); - status = adapter.getRetStatusFromRequest(headers, status); - result.setRuleName(adapter.getActiveRuleNameFromRequest(headers)); - } - else { - if (throwable instanceof SocketTimeoutException) { - status = RetStatus.RetTimeout; - } - } - result.setMethod(httpClientResponse.uri()); - result.setRetCode(httpClientResponse.status().code()); - result.setRetStatus(status); - if (Objects.nonNull(context)) { - result.setCallerIp(context.getConfig().getGlobal().getAPI().getBindIP()); - } - try { - consumerAPI.updateServiceCallResult(result); - } - catch (Throwable ex) { - LOG.error("update service call result fail", ex); - } - } - }; - private HttpClient target; - - public EnhancedPolarisHttpClient( - HttpClient client, - RpcEnhancementReporterProperties properties, - SDKContext context, - ConsumerAPI consumerAPI) { - this.properties = properties; - this.context = context; - this.consumerAPI = consumerAPI; - this.target = client; - this.adapter = new Reporter(properties); - this.registerReportHandler(); - } - - @Override - public HttpClientConfig configuration() { - return target.configuration(); - } - - @Override - protected HttpClient duplicate() { - return new EnhancedPolarisHttpClient(target, properties, context, consumerAPI); - } - - private void registerReportHandler() { - target = target.doOnRequest((request, connection) -> { - String serviceId = request.requestHeaders().get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID); - String host = request.requestHeaders().get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST); - String port = request.requestHeaders().get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT); - if (StringUtils.isNotBlank(serviceId)) { - MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID, serviceId); - MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST, host); - MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT, port); - MetadataContextHolder.get().setLoadbalancer("startTime", System.currentTimeMillis() + ""); - } - - request.requestHeaders().remove(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID); - request.requestHeaders().remove(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST); - request.requestHeaders().remove(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT); - }); - target = target.doOnResponse((httpClientResponse, connection) -> handler.accept(httpClientResponse, null)); - target = target.doOnResponseError(handler); - } - - - private static class Reporter extends AbstractPolarisReporterAdapter { - - /** - * Constructor With {@link RpcEnhancementReporterProperties} . - * - * @param reportProperties instance of {@link RpcEnhancementReporterProperties}. - */ - protected Reporter(RpcEnhancementReporterProperties reportProperties) { - super(reportProperties); - } - - @Override - public boolean apply(HttpStatus httpStatus) { - return super.apply(httpStatus); - } - - @Override - public RetStatus getRetStatusFromRequest(org.springframework.http.HttpHeaders headers, RetStatus defaultVal) { - return super.getRetStatusFromRequest(headers, defaultVal); - } - - @Override - public String getActiveRuleNameFromRequest(org.springframework.http.HttpHeaders headers) { - return super.getActiveRuleNameFromRequest(headers); - } - } - -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpClientCustomizer.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpClientCustomizer.java deleted file mode 100644 index 630feb349..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpClientCustomizer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.scg; - -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.client.api.SDKContext; -import reactor.netty.http.client.HttpClient; - -import org.springframework.cloud.gateway.config.HttpClientCustomizer; - -public class EnhancedPolarisHttpClientCustomizer implements HttpClientCustomizer { - - private final RpcEnhancementReporterProperties properties; - private final SDKContext context; - private final ConsumerAPI consumerAPI; - - public EnhancedPolarisHttpClientCustomizer(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { - this.properties = properties; - this.context = context; - this.consumerAPI = consumerAPI; - } - - @Override - public HttpClient customize(HttpClient httpClient) { - return new EnhancedPolarisHttpClient(httpClient, properties, context, consumerAPI); - } -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpHeadersFilter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpHeadersFilter.java deleted file mode 100644 index a251b67a1..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpHeadersFilter.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.scg; - -import java.util.List; - -import com.tencent.cloud.common.constant.HeaderConstant; - -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.loadbalancer.Response; -import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; -import org.springframework.http.HttpHeaders; -import org.springframework.util.StringUtils; -import org.springframework.web.server.ServerWebExchange; - -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR; - -public class EnhancedPolarisHttpHeadersFilter implements HttpHeadersFilter { - - public EnhancedPolarisHttpHeadersFilter() { - } - - @Override - public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) { - Response serviceInstanceResponse = exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR); - if (serviceInstanceResponse == null || !serviceInstanceResponse.hasServer()) { - return input; - } - ServiceInstance instance = serviceInstanceResponse.getServer(); - write(input, HeaderConstant.INTERNAL_CALLEE_SERVICE_ID, instance.getServiceId(), true); - write(input, HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST, instance.getHost(), true); - write(input, HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT, instance.getPort() + "", true); - return input; - } - - @Override - public boolean supports(Type type) { - return Type.REQUEST.equals(type); - } - - private void write(HttpHeaders headers, String name, String value, boolean append) { - if (value == null) { - return; - } - if (append) { - headers.add(name, value); - // these headers should be treated as a single comma separated header - List values = headers.get(name); - String delimitedValue = StringUtils.collectionToCommaDelimitedString(values); - headers.set(name, delimitedValue); - } - else { - headers.set(name, value); - } - } -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientReporter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientReporter.java index 33737cdd7..2b62fc5d0 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientReporter.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientReporter.java @@ -17,128 +17,81 @@ package com.tencent.cloud.rpc.enhancement.webclient; -import java.io.UnsupportedEncodingException; -import java.net.SocketTimeoutException; -import java.net.URI; -import java.net.URLDecoder; -import java.util.Collection; -import java.util.Objects; +import java.util.Map; 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.util.RequestLabelUtils; -import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.pojo.RetStatus; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.api.rpc.ServiceCallResult; -import com.tencent.polaris.api.utils.CollectionUtils; -import com.tencent.polaris.client.api.SDKContext; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +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.EnhancedRequestContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; import reactor.core.publisher.Mono; -import reactor.util.context.Context; -import reactor.util.context.ContextView; -import org.springframework.http.HttpHeaders; +import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.ExchangeFunction; -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.EXCEPTION; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.FINALLY; +import static com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType.POST; -public class EnhancedWebClientReporter extends AbstractPolarisReporterAdapter implements ExchangeFilterFunction { - - protected static final String METRICS_WEBCLIENT_START_TIME = EnhancedWebClientReporter.class.getName() - + ".START_TIME"; - private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedWebClientReporter.class); - private final ConsumerAPI consumerAPI; - - private final SDKContext context; +/** + * EnhancedWebClientReporter. + * + * @author sean yu + */ +public class EnhancedWebClientReporter implements ExchangeFilterFunction { + private final EnhancedPluginRunner pluginRunner; - public EnhancedWebClientReporter(RpcEnhancementReporterProperties reportProperties, SDKContext context, ConsumerAPI consumerAPI) { - super(reportProperties); - this.context = context; - this.consumerAPI = consumerAPI; + public EnhancedWebClientReporter(EnhancedPluginRunner pluginRunner) { + this.pluginRunner = pluginRunner; } @Override public Mono filter(ClientRequest request, ExchangeFunction next) { - return next.exchange(request).as((responseMono) -> instrumentResponse(request, responseMono)) - .contextWrite(this::putStartTime); - } - - Mono instrumentResponse(ClientRequest request, Mono responseMono) { - return Mono.deferContextual((ctx) -> responseMono.doOnEach((signal) -> { - // report result to polaris - if (!reportProperties.isEnabled()) { - return; - } - ServiceCallResult callResult = new ServiceCallResult(); - Long startTime = getStartTime(ctx); - callResult.setDelay(System.currentTimeMillis() - startTime); - - callResult.setNamespace(MetadataContext.LOCAL_NAMESPACE); - callResult.setService(request.headers().getFirst(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID)); - String sourceNamespace = MetadataContext.LOCAL_NAMESPACE; - String sourceService = MetadataContext.LOCAL_SERVICE; - if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) { - callResult.setCallerService(new ServiceKey(sourceNamespace, sourceService)); - } - - Collection labels = request.headers().get(RouterConstant.ROUTER_LABEL_HEADER); - if (CollectionUtils.isNotEmpty(labels) && labels.iterator().hasNext()) { - String label = labels.iterator().next(); - try { - label = URLDecoder.decode(label, UTF_8); - } - catch (UnsupportedEncodingException e) { - LOGGER.error("unsupported charset exception " + UTF_8, e); - } - callResult.setLabels(RequestLabelUtils.convertLabel(label)); - } - - URI uri = request.url(); - callResult.setMethod(uri.getPath()); - callResult.setHost(uri.getHost()); - // -1 means access directly by url, and use http default port number 80 - callResult.setPort(uri.getPort() == -1 ? 80 : uri.getPort()); - if (Objects.nonNull(context)) { - callResult.setCallerIp(context.getConfig().getGlobal().getAPI().getBindIP()); - } - - RetStatus retStatus = RetStatus.RetSuccess; - ClientResponse response = signal.get(); - if (Objects.nonNull(response)) { - HttpHeaders headers = response.headers().asHttpHeaders(); - - callResult.setRuleName(getActiveRuleNameFromRequest(headers)); - if (apply(response.statusCode())) { - retStatus = RetStatus.RetFail; - } - retStatus = getRetStatusFromRequest(headers, retStatus); - } - if (signal.isOnError()) { - Throwable throwable = signal.getThrowable(); - if (throwable instanceof SocketTimeoutException) { - retStatus = RetStatus.RetTimeout; - } - } - callResult.setRetStatus(retStatus); - - consumerAPI.updateServiceCallResult(callResult); - })); - } - - private Long getStartTime(ContextView context) { - return context.get(METRICS_WEBCLIENT_START_TIME); - } - - private Context putStartTime(Context context) { - return context.put(METRICS_WEBCLIENT_START_TIME, System.currentTimeMillis()); + EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext(); + + EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder() + .httpHeaders(request.headers()) + .httpMethod(request.method()) + .url(request.url()) + .build(); + enhancedPluginContext.setRequest(enhancedRequestContext); + + long startTime = System.currentTimeMillis(); + return next.exchange(request) + .doOnSubscribe(subscription -> { + Map loadBalancerContext = MetadataContextHolder.get().getLoadbalancerMetadata(); + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(loadBalancerContext.get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID)); + serviceInstance.setHost(request.url().getHost()); + serviceInstance.setPort(request.url().getPort()); + enhancedPluginContext.setServiceInstance(serviceInstance); + }) + .doOnSuccess(response -> { + enhancedPluginContext.setDelay(System.currentTimeMillis() - startTime); + + EnhancedResponseContext enhancedResponseContext = EnhancedResponseContext.builder() + .httpStatus(response.rawStatusCode()) + .httpHeaders(response.headers().asHttpHeaders()) + .build(); + enhancedPluginContext.setResponse(enhancedResponseContext); + + // Run post enhanced plugins. + pluginRunner.run(POST, enhancedPluginContext); + }) + .doOnError(t -> { + enhancedPluginContext.setDelay(System.currentTimeMillis() - startTime); + enhancedPluginContext.setThrowable(t); + + // Run exception enhanced plugins. + pluginRunner.run(EXCEPTION, enhancedPluginContext); + }) + .doFinally(v -> { + // Run finally enhanced plugins. + pluginRunner.run(FINALLY, enhancedPluginContext); + }); } } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/reactive/PolarisLoadBalancerClientRequestTransformer.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/PolarisLoadBalancerClientRequestTransformer.java similarity index 74% rename from spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/reactive/PolarisLoadBalancerClientRequestTransformer.java rename to spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/PolarisLoadBalancerClientRequestTransformer.java index cee59298b..4e6913164 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/reactive/PolarisLoadBalancerClientRequestTransformer.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/PolarisLoadBalancerClientRequestTransformer.java @@ -15,30 +15,28 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.loadbalancer.reactive; +package com.tencent.cloud.rpc.enhancement.webclient; import com.tencent.cloud.common.constant.HeaderConstant; -import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.cloud.common.metadata.MetadataContextHolder; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerClientRequestTransformer; -import org.springframework.http.HttpHeaders; import org.springframework.web.reactive.function.client.ClientRequest; +/** + * PolarisLoadBalancerClientRequestTransformer. + * + * @author sean yu + */ public class PolarisLoadBalancerClientRequestTransformer implements LoadBalancerClientRequestTransformer { - private final ConsumerAPI consumerAPI; - - public PolarisLoadBalancerClientRequestTransformer(ConsumerAPI consumerAPI) { - this.consumerAPI = consumerAPI; - } - @Override public ClientRequest transformRequest(ClientRequest request, ServiceInstance instance) { if (instance != null) { - HttpHeaders headers = request.headers(); - headers.add(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID, instance.getServiceId()); + MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID, instance.getServiceId()); } return request; } + } diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapterTest.java index 351c278ce..825da246f 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapterTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapterTest.java @@ -17,32 +17,196 @@ package com.tencent.cloud.rpc.enhancement; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.net.URISyntaxException; + 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.util.ApplicationContextAwareUtils; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.global.APIConfig; +import com.tencent.polaris.api.config.global.GlobalConfig; +import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat; import com.tencent.polaris.api.pojo.RetStatus; -import org.assertj.core.api.Assertions; +import com.tencent.polaris.api.rpc.ServiceCallResult; +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.http.HttpHeaders; import org.springframework.http.HttpStatus; +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.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + /** * Test For {@link AbstractPolarisReporterAdapter}. * * @author Elve.Xu 2022/7/11 */ +@ExtendWith(MockitoExtension.class) public class AbstractPolarisReporterAdapterTest { + private static MockedStatic mockedApplicationContextAwareUtils; + private final RpcEnhancementReporterProperties reporterProperties = new RpcEnhancementReporterProperties(); + @Mock + private SDKContext sdkContext; + + @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 testServiceCallResult() throws URISyntaxException { + APIConfig apiConfig = mock(APIConfig.class); + doReturn("0.0.0.0").when(apiConfig).getBindIP(); + + GlobalConfig globalConfig = mock(GlobalConfig.class); + doReturn(apiConfig).when(globalConfig).getAPI(); + + Configuration configuration = mock(Configuration.class); + doReturn(globalConfig).when(configuration).getGlobal(); + + doReturn(configuration).when(sdkContext).getConfig(); + + SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(reporterProperties, sdkContext); + + ServiceCallResult serviceCallResult; + + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.add(RouterConstant.ROUTER_LABEL_HEADER, "{\"k1\":\"v1\"}"); + + serviceCallResult = adapter.createServiceCallResult( + "test", + null, + null, + new URI("http://0.0.0.0/"), + requestHeaders, + new HttpHeaders(), + 200, + 0, + null + ); + assertThat(serviceCallResult.getRetStatus()).isEqualTo(RetStatus.RetSuccess); + + serviceCallResult = adapter.createServiceCallResult( + "test", + null, + null, + new URI("http://0.0.0.0/"), + requestHeaders, + new HttpHeaders(), + 502, + 0, + null + ); + assertThat(serviceCallResult.getRetStatus()).isEqualTo(RetStatus.RetFail); + + serviceCallResult = adapter.createServiceCallResult( + "test", + null, + null, + new URI("http://0.0.0.0/"), + requestHeaders, + null, + null, + 0, + new SocketTimeoutException() + ); + assertThat(serviceCallResult.getRetStatus()).isEqualTo(RetStatus.RetTimeout); + + serviceCallResult = adapter.createServiceCallResult( + "test", + "0.0.0.0", + 8080, + new URI("/"), + requestHeaders, + new HttpHeaders(), + 200, + 0, + null + ); + assertThat(serviceCallResult.getRetStatus()).isEqualTo(RetStatus.RetSuccess); + assertThat(serviceCallResult.getHost()).isEqualTo("0.0.0.0"); + assertThat(serviceCallResult.getPort()).isEqualTo(8080); + } + + @Test + public void testResourceStat() throws URISyntaxException { + + SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(reporterProperties, sdkContext); + + ResourceStat resourceStat; + + resourceStat = adapter.createInstanceResourceStat("test", + null, + null, + new URI("http://0.0.0.0/"), + 200, + 0, + null + ); + assertThat(resourceStat.getRetStatus()).isEqualTo(RetStatus.RetSuccess); + + resourceStat = adapter.createInstanceResourceStat("test", + null, + null, + new URI("http://0.0.0.0/"), + null, + 0, + new SocketTimeoutException() + ); + assertThat(resourceStat.getRetStatus()).isEqualTo(RetStatus.RetTimeout); + + resourceStat = adapter.createInstanceResourceStat("test", + null, + null, + new URI("http://0.0.0.0/"), + 200, + 0, + null + ); + assertThat(resourceStat.getRetStatus()).isEqualTo(RetStatus.RetSuccess); + } + @Test public void testApplyWithDefaultConfig() { RpcEnhancementReporterProperties properties = new RpcEnhancementReporterProperties(); // Mock Condition - SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties); + SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties, sdkContext); // Assert - Assertions.assertThat(adapter.apply(HttpStatus.OK)).isEqualTo(false); - Assertions.assertThat(adapter.apply(HttpStatus.INTERNAL_SERVER_ERROR)).isEqualTo(false); - Assertions.assertThat(adapter.apply(HttpStatus.BAD_GATEWAY)).isEqualTo(true); + assertThat(adapter.apply(HttpStatus.OK)).isEqualTo(false); + assertThat(adapter.apply(HttpStatus.INTERNAL_SERVER_ERROR)).isEqualTo(false); + assertThat(adapter.apply(HttpStatus.BAD_GATEWAY)).isEqualTo(true); } @Test @@ -52,12 +216,12 @@ public class AbstractPolarisReporterAdapterTest { properties.getStatuses().clear(); properties.setIgnoreInternalServerError(false); - SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties); + SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties, sdkContext); // Assert - Assertions.assertThat(adapter.apply(HttpStatus.OK)).isEqualTo(false); - Assertions.assertThat(adapter.apply(HttpStatus.INTERNAL_SERVER_ERROR)).isEqualTo(true); - Assertions.assertThat(adapter.apply(HttpStatus.BAD_GATEWAY)).isEqualTo(true); + assertThat(adapter.apply(HttpStatus.OK)).isEqualTo(false); + assertThat(adapter.apply(HttpStatus.INTERNAL_SERVER_ERROR)).isEqualTo(true); + assertThat(adapter.apply(HttpStatus.BAD_GATEWAY)).isEqualTo(true); } @Test @@ -67,12 +231,12 @@ public class AbstractPolarisReporterAdapterTest { properties.getStatuses().clear(); properties.setIgnoreInternalServerError(true); - SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties); + SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties, sdkContext); // Assert - Assertions.assertThat(adapter.apply(HttpStatus.OK)).isEqualTo(false); - Assertions.assertThat(adapter.apply(HttpStatus.INTERNAL_SERVER_ERROR)).isEqualTo(false); - Assertions.assertThat(adapter.apply(HttpStatus.BAD_GATEWAY)).isEqualTo(true); + assertThat(adapter.apply(HttpStatus.OK)).isEqualTo(false); + assertThat(adapter.apply(HttpStatus.INTERNAL_SERVER_ERROR)).isEqualTo(false); + assertThat(adapter.apply(HttpStatus.BAD_GATEWAY)).isEqualTo(true); } @Test @@ -82,12 +246,12 @@ public class AbstractPolarisReporterAdapterTest { properties.getStatuses().clear(); properties.getSeries().clear(); - SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties); + SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties, sdkContext); // Assert - Assertions.assertThat(adapter.apply(HttpStatus.OK)).isEqualTo(false); - Assertions.assertThat(adapter.apply(HttpStatus.INTERNAL_SERVER_ERROR)).isEqualTo(false); - Assertions.assertThat(adapter.apply(HttpStatus.BAD_GATEWAY)).isEqualTo(true); + assertThat(adapter.apply(HttpStatus.OK)).isEqualTo(false); + assertThat(adapter.apply(HttpStatus.INTERNAL_SERVER_ERROR)).isEqualTo(false); + assertThat(adapter.apply(HttpStatus.BAD_GATEWAY)).isEqualTo(true); } @Test @@ -98,15 +262,16 @@ public class AbstractPolarisReporterAdapterTest { properties.getSeries().clear(); properties.getSeries().add(HttpStatus.Series.CLIENT_ERROR); - SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties); + SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties, sdkContext); // Assert - Assertions.assertThat(adapter.apply(HttpStatus.OK)).isEqualTo(false); - Assertions.assertThat(adapter.apply(HttpStatus.INTERNAL_SERVER_ERROR)).isEqualTo(false); - Assertions.assertThat(adapter.apply(HttpStatus.BAD_GATEWAY)).isEqualTo(false); - Assertions.assertThat(adapter.apply(HttpStatus.FORBIDDEN)).isEqualTo(true); + assertThat(adapter.apply(HttpStatus.OK)).isEqualTo(false); + assertThat(adapter.apply(HttpStatus.INTERNAL_SERVER_ERROR)).isEqualTo(false); + assertThat(adapter.apply(HttpStatus.BAD_GATEWAY)).isEqualTo(false); + assertThat(adapter.apply(HttpStatus.FORBIDDEN)).isEqualTo(true); } + @Test public void testGetRetStatusFromRequest() { RpcEnhancementReporterProperties properties = new RpcEnhancementReporterProperties(); @@ -115,19 +280,19 @@ public class AbstractPolarisReporterAdapterTest { properties.getSeries().clear(); properties.getSeries().add(HttpStatus.Series.CLIENT_ERROR); - SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties); + SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties, sdkContext); HttpHeaders headers = new HttpHeaders(); RetStatus ret = adapter.getRetStatusFromRequest(headers, RetStatus.RetFail); - Assertions.assertThat(ret).isEqualTo(RetStatus.RetFail); + assertThat(ret).isEqualTo(RetStatus.RetFail); headers.set(HeaderConstant.INTERNAL_CALLEE_RET_STATUS, RetStatus.RetFlowControl.getDesc()); ret = adapter.getRetStatusFromRequest(headers, RetStatus.RetFail); - Assertions.assertThat(ret).isEqualTo(RetStatus.RetFlowControl); + assertThat(ret).isEqualTo(RetStatus.RetFlowControl); headers.set(HeaderConstant.INTERNAL_CALLEE_RET_STATUS, RetStatus.RetReject.getDesc()); ret = adapter.getRetStatusFromRequest(headers, RetStatus.RetFail); - Assertions.assertThat(ret).isEqualTo(RetStatus.RetReject); + assertThat(ret).isEqualTo(RetStatus.RetReject); } @Test @@ -138,15 +303,15 @@ public class AbstractPolarisReporterAdapterTest { properties.getSeries().clear(); properties.getSeries().add(HttpStatus.Series.CLIENT_ERROR); - SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties); + SimplePolarisReporterAdapter adapter = new SimplePolarisReporterAdapter(properties, sdkContext); HttpHeaders headers = new HttpHeaders(); String ruleName = adapter.getActiveRuleNameFromRequest(headers); - Assertions.assertThat(ruleName).isEqualTo(""); + assertThat(ruleName).isEqualTo(""); headers.set(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME, "mock_rule"); ruleName = adapter.getActiveRuleNameFromRequest(headers); - Assertions.assertThat(ruleName).isEqualTo("mock_rule"); + assertThat(ruleName).isEqualTo("mock_rule"); } /** @@ -154,8 +319,8 @@ public class AbstractPolarisReporterAdapterTest { */ public static class SimplePolarisReporterAdapter extends AbstractPolarisReporterAdapter { - public SimplePolarisReporterAdapter(RpcEnhancementReporterProperties properties) { - super(properties); + protected SimplePolarisReporterAdapter(RpcEnhancementReporterProperties reportProperties, SDKContext context) { + super(reportProperties, context); } } } 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 e768dd914..fe02ab397 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 @@ -19,10 +19,10 @@ package com.tencent.cloud.rpc.enhancement.config; import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.cloud.rpc.enhancement.feign.EnhancedFeignBeanPostProcessor; -import com.tencent.cloud.rpc.enhancement.feign.EnhancedFeignPluginRunner; -import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.ExceptionPolarisReporter; -import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.SuccessPolarisReporter; -import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter; +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.polaris.api.core.ConsumerAPI; import org.junit.jupiter.api.Test; @@ -56,14 +56,12 @@ public class RpcEnhancementAutoConfigurationTest { public void testDefaultInitialization() { this.contextRunner.run(context -> { assertThat(context).hasSingleBean(ConsumerAPI.class); - assertThat(context).hasSingleBean(EnhancedFeignPluginRunner.class); + assertThat(context).hasSingleBean(EnhancedPluginRunner.class); assertThat(context).hasSingleBean(EnhancedFeignBeanPostProcessor.class); assertThat(context).hasSingleBean(SuccessPolarisReporter.class); assertThat(context).hasSingleBean(ExceptionPolarisReporter.class); - assertThat(context).hasSingleBean(EnhancedRestTemplateReporter.class); + assertThat(context).hasSingleBean(EnhancedRestTemplateInterceptor.class); assertThat(context).hasSingleBean(RestTemplate.class); - RestTemplate restTemplate = context.getBean(RestTemplate.class); - assertThat(restTemplate.getErrorHandler() instanceof EnhancedRestTemplateReporter).isTrue(); }); } diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementReporterPropertiesTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementReporterPropertiesTest.java index e3483decc..6ed9d8857 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementReporterPropertiesTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementReporterPropertiesTest.java @@ -40,7 +40,8 @@ import static org.springframework.http.HttpStatus.Series.SERVER_ERROR; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = RpcEnhancementReporterPropertiesTest.TestApplication.class, properties = { "spring.application.name=test", - "spring.cloud.gateway.enabled=false" + "spring.cloud.gateway.enabled=false", + "spring.cloud.tencent.rpc-enhancement.reporter=true" }) @ActiveProfiles("test") public class RpcEnhancementReporterPropertiesTest { @@ -58,6 +59,7 @@ public class RpcEnhancementReporterPropertiesTest { assertThat(rpcEnhancementReporterProperties.getStatuses()).isNotEmpty(); assertThat(rpcEnhancementReporterProperties.getStatuses().get(0)).isEqualTo(MULTIPLE_CHOICES); assertThat(rpcEnhancementReporterProperties.getStatuses().get(1)).isEqualTo(MOVED_PERMANENTLY); + assertThat(rpcEnhancementReporterProperties.isEnabled()).isEqualTo(true); } @SpringBootApplication 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 e1c8eacf6..2ff9ff909 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 @@ -22,9 +22,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType; +import com.tencent.cloud.rpc.enhancement.plugin.DefaultEnhancedPluginRunner; +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 feign.Client; import feign.Request; import feign.RequestTemplate; @@ -72,9 +73,9 @@ public class EnhancedFeignClientTest { fail("Exception encountered.", e); } - List enhancedFeignPlugins = getMockEnhancedFeignPlugins(); + List enhancedPlugins = getMockEnhancedFeignPlugins(); try { - new EnhancedFeignClient(mock(Client.class), new DefaultEnhancedFeignPluginRunner(enhancedFeignPlugins)); + new EnhancedFeignClient(mock(Client.class), new DefaultEnhancedPluginRunner(enhancedPlugins)); } catch (Throwable e) { fail("Exception encountered.", e); @@ -103,7 +104,7 @@ public class EnhancedFeignClientTest { RequestTemplate requestTemplate = new RequestTemplate(); requestTemplate.feignTarget(target); - EnhancedFeignClient polarisFeignClient = new EnhancedFeignClient(delegate, new DefaultEnhancedFeignPluginRunner(getMockEnhancedFeignPlugins())); + EnhancedFeignClient polarisFeignClient = new EnhancedFeignClient(delegate, new DefaultEnhancedPluginRunner(getMockEnhancedFeignPlugins())); // 200 Response response = polarisFeignClient.execute(Request.create(Request.HttpMethod.GET, "http://localhost:8080/test", @@ -127,22 +128,22 @@ public class EnhancedFeignClientTest { } } - private List getMockEnhancedFeignPlugins() { - List enhancedFeignPlugins = new ArrayList<>(); + private List getMockEnhancedFeignPlugins() { + List enhancedPlugins = new ArrayList<>(); - enhancedFeignPlugins.add(new EnhancedFeignPlugin() { + enhancedPlugins.add(new EnhancedPlugin() { @Override - public EnhancedFeignPluginType getType() { - return EnhancedFeignPluginType.PRE; + public EnhancedPluginType getType() { + return EnhancedPluginType.PRE; } @Override - public void run(EnhancedFeignContext context) { + public void run(EnhancedPluginContext context) { } @Override - public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) { + public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) { } @@ -152,19 +153,19 @@ public class EnhancedFeignClientTest { } }); - enhancedFeignPlugins.add(new EnhancedFeignPlugin() { + enhancedPlugins.add(new EnhancedPlugin() { @Override - public EnhancedFeignPluginType getType() { - return EnhancedFeignPluginType.POST; + public EnhancedPluginType getType() { + return EnhancedPluginType.POST; } @Override - public void run(EnhancedFeignContext context) { + public void run(EnhancedPluginContext context) { } @Override - public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) { + public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) { } @@ -174,19 +175,19 @@ public class EnhancedFeignClientTest { } }); - enhancedFeignPlugins.add(new EnhancedFeignPlugin() { + enhancedPlugins.add(new EnhancedPlugin() { @Override - public EnhancedFeignPluginType getType() { - return EnhancedFeignPluginType.EXCEPTION; + public EnhancedPluginType getType() { + return EnhancedPluginType.EXCEPTION; } @Override - public void run(EnhancedFeignContext context) { + public void run(EnhancedPluginContext context) { } @Override - public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) { + public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) { } @@ -196,19 +197,19 @@ public class EnhancedFeignClientTest { } }); - enhancedFeignPlugins.add(new EnhancedFeignPlugin() { + enhancedPlugins.add(new EnhancedPlugin() { @Override - public EnhancedFeignPluginType getType() { - return EnhancedFeignPluginType.FINALLY; + public EnhancedPluginType getType() { + return EnhancedPluginType.FINALLY; } @Override - public void run(EnhancedFeignContext context) { + public void run(EnhancedPluginContext context) { } @Override - public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) { + public void handlerThrowable(EnhancedPluginContext context, Throwable throwable) { } @@ -218,7 +219,7 @@ public class EnhancedFeignClientTest { } }); - return enhancedFeignPlugins; + return enhancedPlugins; } diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignContextTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignContextTest.java deleted file mode 100644 index 0143fc844..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/EnhancedFeignContextTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.feign.plugin; - -import feign.Request; -import feign.Response; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -/** - * Test for {@link EnhancedFeignContext}. - * - * @author Haotian Zhang - */ -public class EnhancedFeignContextTest { - - @Test - public void testGetAndSet() { - EnhancedFeignContext enhancedFeignContext = new EnhancedFeignContext(); - enhancedFeignContext.setRequest(mock(Request.class)); - enhancedFeignContext.setOptions(mock(Request.Options.class)); - enhancedFeignContext.setResponse(mock(Response.class)); - enhancedFeignContext.setException(mock(Exception.class)); - assertThat(enhancedFeignContext.getRequest()).isNotNull(); - assertThat(enhancedFeignContext.getOptions()).isNotNull(); - assertThat(enhancedFeignContext.getResponse()).isNotNull(); - assertThat(enhancedFeignContext.getException()).isNotNull(); - } -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ReporterUtilsTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ReporterUtilsTest.java deleted file mode 100644 index 0ba60f5fe..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ReporterUtilsTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - -import com.tencent.cloud.common.constant.RouterConstant; -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.util.ApplicationContextAwareUtils; -import com.tencent.polaris.api.config.Configuration; -import com.tencent.polaris.api.config.global.APIConfig; -import com.tencent.polaris.api.config.global.GlobalConfig; -import com.tencent.polaris.api.pojo.RetStatus; -import com.tencent.polaris.api.rpc.ServiceCallResult; -import com.tencent.polaris.client.api.SDKContext; -import feign.Request; -import feign.RequestTemplate; -import feign.Response; -import feign.Target; -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.MockedStatic; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; -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.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; - -/** - * Test for {@link ReporterUtils}. - * - * @author Haotian Zhang - */ -@ExtendWith(MockitoExtension.class) -public class ReporterUtilsTest { - - private static MockedStatic mockedApplicationContextAwareUtils; - - @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 testCreateServiceCallResult() { - // mock target - Target target = mock(Target.class); - doReturn(SERVICE_PROVIDER).when(target).name(); - - // mock RequestTemplate.class - RequestTemplate requestTemplate = new RequestTemplate(); - requestTemplate.feignTarget(target); - try { - requestTemplate.header(RouterConstant.ROUTER_LABEL_HEADER, URLEncoder.encode("{\"k1\":\"v1\",\"k2\":\"v2\"}", UTF_8)); - } - catch (UnsupportedEncodingException e) { - throw new RuntimeException("unsupported charset exception " + UTF_8); - } - - // mock request - Request request = mock(Request.class); - doReturn(requestTemplate).when(request).requestTemplate(); - doReturn("http://1.1.1.1:2345/path").when(request).url(); - - // mock request - Response response = mock(Response.class); - doReturn(502).when(response).status(); - - ServiceCallResult serviceCallResult = ReporterUtils.createServiceCallResult(mockSDKContext(), request, response, 10L, RetStatus.RetSuccess, result -> { - - }); - assertThat(serviceCallResult.getNamespace()).isEqualTo(NAMESPACE_TEST); - assertThat(serviceCallResult.getService()).isEqualTo(SERVICE_PROVIDER); - assertThat(serviceCallResult.getHost()).isEqualTo("1.1.1.1"); - assertThat(serviceCallResult.getPort()).isEqualTo(2345); - assertThat(serviceCallResult.getRetStatus()).isEqualTo(RetStatus.RetSuccess); - assertThat(serviceCallResult.getMethod()).isEqualTo("/path"); - assertThat(serviceCallResult.getCallerService().getNamespace()).isEqualTo(NAMESPACE_TEST); - assertThat(serviceCallResult.getCallerService().getService()).isEqualTo(SERVICE_PROVIDER); - assertThat(serviceCallResult.getLabels()).isEqualTo("k1:v1|k2:v2"); - assertThat(serviceCallResult.getRetCode()).isEqualTo(502); - assertThat(serviceCallResult.getDelay()).isEqualTo(10L); - } - - public static SDKContext mockSDKContext() { - APIConfig apiConfig = mock(APIConfig.class); - doReturn("127.0.0.1").when(apiConfig).getBindIP(); - GlobalConfig globalConfig = mock(GlobalConfig.class); - doReturn(apiConfig).when(globalConfig).getAPI(); - Configuration configuration = mock(Configuration.class); - doReturn(globalConfig).when(configuration).getGlobal(); - SDKContext context = mock(SDKContext.class); - doReturn(configuration).when(context).getConfig(); - - return context; - } -} 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 new file mode 100644 index 000000000..a516483f1 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContextTest.java @@ -0,0 +1,134 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.plugin; + +import java.net.URI; +import java.util.Arrays; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; +import com.tencent.cloud.rpc.enhancement.plugin.reporter.ExceptionPolarisReporter; +import com.tencent.cloud.rpc.enhancement.plugin.reporter.SuccessPolarisReporter; +import com.tencent.polaris.api.core.ConsumerAPI; +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.DefaultServiceInstance; +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.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link EnhancedPluginContext}. + * + * @author Haotian Zhang + */ +@ExtendWith(MockitoExtension.class) +public class EnhancedPluginContextTest { + + private static MockedStatic mockedApplicationContextAwareUtils; + @Mock + private RpcEnhancementReporterProperties reporterProperties; + @Mock + private SDKContext sdkContext; + @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 testGetAndSet() throws Throwable { + EnhancedRequestContext requestContext = new EnhancedRequestContext(); + requestContext.setHttpHeaders(new HttpHeaders()); + requestContext.setUrl(new URI("/")); + requestContext.setHttpMethod(HttpMethod.GET); + + EnhancedRequestContext requestContext1 = EnhancedRequestContext.builder() + .httpHeaders(requestContext.getHttpHeaders()) + .url(requestContext.getUrl()) + .httpMethod(requestContext.getHttpMethod()) + .build(); + assertThat(requestContext1.getUrl()).isEqualTo(requestContext.getUrl()); + + EnhancedResponseContext responseContext = new EnhancedResponseContext(); + responseContext.setHttpStatus(200); + responseContext.setHttpHeaders(new HttpHeaders()); + + EnhancedResponseContext responseContext1 = EnhancedResponseContext.builder() + .httpStatus(responseContext.getHttpStatus()) + .httpHeaders(responseContext.getHttpHeaders()) + .build(); + assertThat(responseContext1.getHttpStatus()).isEqualTo(responseContext.getHttpStatus()); + + EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext(); + enhancedPluginContext.setRequest(requestContext); + enhancedPluginContext.setResponse(responseContext); + enhancedPluginContext.setServiceInstance(new DefaultServiceInstance()); + enhancedPluginContext.setThrowable(mock(Exception.class)); + enhancedPluginContext.setDelay(0); + assertThat(enhancedPluginContext.getRequest()).isNotNull(); + assertThat(enhancedPluginContext.getResponse()).isNotNull(); + assertThat(enhancedPluginContext.getServiceInstance()).isNotNull(); + assertThat(enhancedPluginContext.getThrowable()).isNotNull(); + assertThat(enhancedPluginContext.getDelay()).isNotNull(); + + EnhancedPlugin enhancedPlugin = new SuccessPolarisReporter(reporterProperties, sdkContext, consumerAPI); + EnhancedPlugin enhancedPlugin1 = new ExceptionPolarisReporter(reporterProperties, sdkContext, consumerAPI); + EnhancedPluginRunner enhancedPluginRunner = new DefaultEnhancedPluginRunner(Arrays.asList(enhancedPlugin, enhancedPlugin1)); + enhancedPluginRunner.run(EnhancedPluginType.POST, enhancedPluginContext); + + EnhancedPlugin enhancedPlugin2 = mock(EnhancedPlugin.class); + doThrow(new RuntimeException()).when(enhancedPlugin2).run(any()); + doReturn(EnhancedPluginType.POST).when(enhancedPlugin2).getType(); + enhancedPluginRunner = new DefaultEnhancedPluginRunner(Arrays.asList(enhancedPlugin2)); + enhancedPluginRunner.run(EnhancedPluginType.POST, enhancedPluginContext); + } +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ExceptionPolarisReporterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/ExceptionPolarisReporterTest.java similarity index 57% rename from spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ExceptionPolarisReporterTest.java rename to spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/ExceptionPolarisReporterTest.java index 617732d73..21c61ef92 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/ExceptionPolarisReporterTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/ExceptionPolarisReporterTest.java @@ -15,24 +15,19 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; +package com.tencent.cloud.rpc.enhancement.plugin; -import java.util.HashMap; -import java.util.function.Consumer; +import java.net.URI; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType; +import com.tencent.cloud.rpc.enhancement.plugin.reporter.ExceptionPolarisReporter; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.global.APIConfig; +import com.tencent.polaris.api.config.global.GlobalConfig; import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.pojo.RetStatus; -import com.tencent.polaris.api.rpc.ServiceCallResult; import com.tencent.polaris.client.api.SDKContext; -import feign.Request; -import feign.RequestTemplate; -import feign.Response; -import feign.Target; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -44,11 +39,13 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.cloud.client.DefaultServiceInstance; +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.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -63,30 +60,26 @@ import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) public class ExceptionPolarisReporterTest { - private static MockedStatic mockedReporterUtils; private static MockedStatic mockedApplicationContextAwareUtils; @Mock - private ConsumerAPI consumerAPI; - @Mock private RpcEnhancementReporterProperties reporterProperties; + @Mock + private SDKContext sdkContext; @InjectMocks private ExceptionPolarisReporter exceptionPolarisReporter; + @Mock + private ConsumerAPI consumerAPI; @BeforeAll static void beforeAll() { mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) .thenReturn("unit-test"); - mockedReporterUtils = Mockito.mockStatic(ReporterUtils.class); - mockedReporterUtils.when(() -> ReporterUtils.createServiceCallResult(any(SDKContext.class), any(Request.class), - any(Response.class), anyLong(), any(RetStatus.class), any(Consumer.class))) - .thenReturn(new ServiceCallResult()); } @AfterAll static void afterAll() { mockedApplicationContextAwareUtils.close(); - mockedReporterUtils.close(); } @BeforeEach @@ -102,63 +95,60 @@ public class ExceptionPolarisReporterTest { @Test public void testType() { - assertThat(exceptionPolarisReporter.getType()).isEqualTo(EnhancedFeignPluginType.EXCEPTION); + assertThat(exceptionPolarisReporter.getType()).isEqualTo(EnhancedPluginType.EXCEPTION); } @Test public void testRun() { - // mock request - Request request = Request.create(Request.HttpMethod.GET, "/", new HashMap<>(), null, null, null); - // mock response - Response response = mock(Response.class); - - EnhancedFeignContext context = mock(EnhancedFeignContext.class); - doReturn(request).when(context).getRequest(); - doReturn(response).when(context).getResponse(); + EnhancedPluginContext context = mock(EnhancedPluginContext.class); // test not report exceptionPolarisReporter.run(context); verify(context, times(0)).getRequest(); - // test do report + doReturn(true).when(reporterProperties).isEnabled(); - exceptionPolarisReporter.run(context); - verify(context, times(1)).getRequest(); - - - try { - mockedReporterUtils.close(); - // mock target - Target target = mock(Target.class); - doReturn(SERVICE_PROVIDER).when(target).name(); - - // mock RequestTemplate.class - RequestTemplate requestTemplate = new RequestTemplate(); - requestTemplate.feignTarget(target); - - EnhancedFeignContext feignContext = new EnhancedFeignContext(); - request = Request.create(Request.HttpMethod.GET, "/", new HashMap<>(), null, null, requestTemplate); - response = Response.builder() - .request(request) - .build(); - feignContext.setRequest(request); - feignContext.setResponse(response); - exceptionPolarisReporter.run(feignContext); - } - finally { - mockedReporterUtils = Mockito.mockStatic(ReporterUtils.class); - mockedReporterUtils.when(() -> ReporterUtils.createServiceCallResult(any(SDKContext.class), any(Request.class), - any(Response.class), anyLong(), any(RetStatus.class), any(Consumer.class))) - .thenReturn(new ServiceCallResult()); - } + + APIConfig apiConfig = mock(APIConfig.class); + doReturn("0.0.0.0").when(apiConfig).getBindIP(); + + GlobalConfig globalConfig = mock(GlobalConfig.class); + doReturn(apiConfig).when(globalConfig).getAPI(); + + Configuration configuration = mock(Configuration.class); + doReturn(globalConfig).when(configuration).getGlobal(); + + doReturn(configuration).when(sdkContext).getConfig(); + + 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.setServiceInstance(serviceInstance); + pluginContext.setThrowable(new RuntimeException()); + + exceptionPolarisReporter.run(pluginContext); + exceptionPolarisReporter.getOrder(); + exceptionPolarisReporter.getName(); + exceptionPolarisReporter.getType(); } @Test public void testHandlerThrowable() { // mock request - Request request = mock(Request.class); + EnhancedRequestContext request = mock(EnhancedRequestContext.class); // mock response - Response response = mock(Response.class); + EnhancedResponseContext response = mock(EnhancedResponseContext.class); - EnhancedFeignContext context = new EnhancedFeignContext(); + EnhancedPluginContext context = new EnhancedPluginContext(); context.setRequest(request); context.setResponse(response); exceptionPolarisReporter.handlerThrowable(context, new RuntimeException("Mock exception.")); diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/SuccessPolarisReporterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/SuccessPolarisReporterTest.java similarity index 57% rename from spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/SuccessPolarisReporterTest.java rename to spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/SuccessPolarisReporterTest.java index 44b667b79..9af4ca019 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/SuccessPolarisReporterTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/plugin/SuccessPolarisReporterTest.java @@ -15,24 +15,19 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; +package com.tencent.cloud.rpc.enhancement.plugin; -import java.util.HashMap; -import java.util.function.Consumer; +import java.net.URI; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext; -import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType; +import com.tencent.cloud.rpc.enhancement.plugin.reporter.SuccessPolarisReporter; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.global.APIConfig; +import com.tencent.polaris.api.config.global.GlobalConfig; import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.pojo.RetStatus; -import com.tencent.polaris.api.rpc.ServiceCallResult; import com.tencent.polaris.client.api.SDKContext; -import feign.Request; -import feign.RequestTemplate; -import feign.Response; -import feign.Target; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -44,11 +39,12 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.cloud.client.DefaultServiceInstance; +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.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -62,31 +58,26 @@ import static org.mockito.Mockito.verify; */ @ExtendWith(MockitoExtension.class) public class SuccessPolarisReporterTest { - - private static MockedStatic mockedReporterUtils; private static MockedStatic mockedApplicationContextAwareUtils; @Mock - private ConsumerAPI consumerAPI; + private SDKContext sdkContext; @Mock private RpcEnhancementReporterProperties reporterProperties; @InjectMocks private SuccessPolarisReporter successPolarisReporter; + @Mock + private ConsumerAPI consumerAPI; @BeforeAll static void beforeAll() { mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) .thenReturn("unit-test"); - mockedReporterUtils = Mockito.mockStatic(ReporterUtils.class); - mockedReporterUtils.when(() -> ReporterUtils.createServiceCallResult(any(SDKContext.class), any(Request.class), - any(Response.class), anyLong(), any(RetStatus.class), any(Consumer.class))) - .thenReturn(new ServiceCallResult()); } @AfterAll static void afterAll() { mockedApplicationContextAwareUtils.close(); - mockedReporterUtils.close(); } @BeforeEach @@ -102,63 +93,58 @@ public class SuccessPolarisReporterTest { @Test public void testType() { - assertThat(successPolarisReporter.getType()).isEqualTo(EnhancedFeignPluginType.POST); + assertThat(successPolarisReporter.getType()).isEqualTo(EnhancedPluginType.POST); } @Test public void testRun() { - // mock request - Request request = Request.create(Request.HttpMethod.GET, "/", new HashMap<>(), null, null, null); - // mock response - Response response = mock(Response.class); - doReturn(502).when(response).status(); - EnhancedFeignContext context = mock(EnhancedFeignContext.class); - doReturn(request).when(context).getRequest(); - doReturn(response).when(context).getResponse(); + EnhancedPluginContext context = mock(EnhancedPluginContext.class); // test not report successPolarisReporter.run(context); verify(context, times(0)).getRequest(); - // test do report + doReturn(true).when(reporterProperties).isEnabled(); - successPolarisReporter.run(context); - verify(context, times(1)).getRequest(); - - try { - mockedReporterUtils.close(); - // mock target - Target target = mock(Target.class); - doReturn(SERVICE_PROVIDER).when(target).name(); - - // mock RequestTemplate.class - RequestTemplate requestTemplate = new RequestTemplate(); - requestTemplate.feignTarget(target); - - EnhancedFeignContext feignContext = new EnhancedFeignContext(); - request = Request.create(Request.HttpMethod.GET, "/", new HashMap<>(), null, null, requestTemplate); - response = Response.builder() - .request(request) - .build(); - feignContext.setRequest(request); - feignContext.setResponse(response); - successPolarisReporter.run(feignContext); - } - finally { - mockedReporterUtils = Mockito.mockStatic(ReporterUtils.class); - mockedReporterUtils.when(() -> ReporterUtils.createServiceCallResult(any(SDKContext.class), any(Request.class), - any(Response.class), anyLong(), any(RetStatus.class), any(Consumer.class))) - .thenReturn(new ServiceCallResult()); - } + APIConfig apiConfig = mock(APIConfig.class); + doReturn("0.0.0.0").when(apiConfig).getBindIP(); + + GlobalConfig globalConfig = mock(GlobalConfig.class); + doReturn(apiConfig).when(globalConfig).getAPI(); + + Configuration configuration = mock(Configuration.class); + doReturn(globalConfig).when(configuration).getGlobal(); + + doReturn(configuration).when(sdkContext).getConfig(); + + 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(200) + .build(); + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(SERVICE_PROVIDER); + + pluginContext.setRequest(request); + pluginContext.setResponse(response); + pluginContext.setServiceInstance(serviceInstance); + + successPolarisReporter.run(pluginContext); + successPolarisReporter.getOrder(); + successPolarisReporter.getName(); + successPolarisReporter.getType(); } @Test public void testHandlerThrowable() { // mock request - Request request = mock(Request.class); + EnhancedRequestContext request = mock(EnhancedRequestContext.class); // mock response - Response response = mock(Response.class); + EnhancedResponseContext response = mock(EnhancedResponseContext.class); - EnhancedFeignContext context = new EnhancedFeignContext(); + EnhancedPluginContext context = new EnhancedPluginContext(); context.setRequest(request); context.setResponse(response); successPolarisReporter.handlerThrowable(context, new RuntimeException("Mock exception.")); diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/BlockingLoadBalancerClientAspectTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/BlockingLoadBalancerClientAspectTest.java new file mode 100644 index 000000000..82bf173ba --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/BlockingLoadBalancerClientAspectTest.java @@ -0,0 +1,92 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.resttemplate; + +import com.tencent.cloud.common.constant.HeaderConstant; +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 org.aspectj.lang.ProceedingJoinPoint; +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.ServiceInstance; +import org.springframework.context.ApplicationContext; + +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.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +public class BlockingLoadBalancerClientAspectTest { + + private static MockedStatic mockedApplicationContextAwareUtils; + @Mock + private ProceedingJoinPoint proceedingJoinPoint; + + private BlockingLoadBalancerClientAspect aspect = new BlockingLoadBalancerClientAspect(); + + @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 test() throws Throwable { + ServiceInstance serviceInstance = mock(ServiceInstance.class); + doReturn("0.0.0.0").when(serviceInstance).getHost(); + doReturn(80).when(serviceInstance).getPort(); + doReturn(new Object[]{ serviceInstance }).when(proceedingJoinPoint).getArgs(); + aspect.invoke(proceedingJoinPoint); + aspect.pointcut(); + assertThat(MetadataContextHolder.get().getLoadbalancerMetadata().get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST)).isEqualTo("0.0.0.0"); + assertThat(MetadataContextHolder.get().getLoadbalancerMetadata().get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT)).isEqualTo("80"); + } + +} 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 new file mode 100644 index 000000000..6b256f014 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptorTest.java @@ -0,0 +1,123 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.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.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 + 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<>())); + 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); + } + +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporterTest.java deleted file mode 100644 index c28f3c6ed..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporterTest.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.resttemplate; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -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.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.polaris.api.core.ConsumerAPI; -import org.checkerframework.checker.nullness.qual.NonNull; -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.context.ApplicationContext; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.AbstractClientHttpResponse; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.web.client.DefaultResponseErrorHandler; -import org.springframework.web.client.ResponseErrorHandler; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Test for {@link EnhancedRestTemplateReporter}. - * @author lepdou 2022-09-06 - */ -@ExtendWith(MockitoExtension.class) -public class EnhancedRestTemplateReporterTest { - - private static MockedStatic mockedMetadataContextHolder; - private static MockedStatic mockedApplicationContextAwareUtils; - @Mock - private ConsumerAPI consumerAPI; - @Mock - private RpcEnhancementReporterProperties reporterProperties; - @Mock - private ResponseErrorHandler delegate; - @InjectMocks - private EnhancedRestTemplateReporter enhancedRestTemplateReporter; - - @InjectMocks - private EnhancedRestTemplateReporter enhancedRestTemplateReporter2; - - @BeforeAll - static void beforeAll() { - mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); - mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) - .thenReturn("caller"); - MetadataContext metadataContext = Mockito.mock(MetadataContext.class); - - // mock transitive metadata - Map loadBalancerContext = new HashMap<>(); - loadBalancerContext.put("host", "1.1.1.1"); - loadBalancerContext.put("port", "8080"); - loadBalancerContext.put("startMillis", String.valueOf(System.currentTimeMillis())); - when(metadataContext.getLoadbalancerMetadata()).thenReturn(loadBalancerContext); - - mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class); - mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); - } - - @AfterAll - static void afterAll() { - mockedApplicationContextAwareUtils.close(); - mockedMetadataContextHolder.close(); - } - - @BeforeEach - void setUp() { - enhancedRestTemplateReporter.setDelegateHandler(delegate); - } - - @Test - public void testSetApplicationContext() { - ApplicationContext applicationContext = mock(ApplicationContext.class); - - // test no ResponseErrorHandler - when(applicationContext.getBeanNamesForType(any(Class.class))) - .thenReturn(new String[] {"enhancedRestTemplateReporter"}); - enhancedRestTemplateReporter2.setApplicationContext(applicationContext); - assertThat(enhancedRestTemplateReporter2.getDelegateHandler()).isInstanceOf(DefaultResponseErrorHandler.class); - - // test one other ResponseErrorHandler - when(applicationContext.getBeanNamesForType(any(Class.class))) - .thenReturn(new String[] {"enhancedRestTemplateReporter", "mockedResponseErrorHandler"}); - when(applicationContext.getBean(anyString())).thenReturn(mock(MockedResponseErrorHandler.class)); - enhancedRestTemplateReporter2.setApplicationContext(applicationContext); - assertThat(enhancedRestTemplateReporter2.getDelegateHandler()).isInstanceOf(MockedResponseErrorHandler.class); - } - - @Test - public void testHasError() throws IOException { - when(delegate.hasError(any())).thenReturn(true); - - MockedClientHttpResponse response = new MockedClientHttpResponse(); - assertThat(enhancedRestTemplateReporter.hasError(response)).isTrue(); - - String realHasError = response.getHeaders().getFirst(EnhancedRestTemplateReporter.HEADER_HAS_ERROR); - assertThat(realHasError).isEqualTo("true"); - } - - @Test - public void testHandleHasError() throws IOException { - when(reporterProperties.isEnabled()).thenReturn(true); - when(delegate.hasError(any())).thenReturn(true); - - MockedClientHttpResponse response = new MockedClientHttpResponse(); - enhancedRestTemplateReporter.hasError(response); - - URI uri = mock(URI.class); - enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, response); - - verify(consumerAPI, times(1)).updateServiceCallResult(any()); - verify(delegate).handleError(uri, HttpMethod.GET, response); - } - - @Test - public void testHandleHasNotError() throws IOException { - when(reporterProperties.isEnabled()).thenReturn(true); - when(delegate.hasError(any())).thenReturn(false); - - MockedClientHttpResponse response = new MockedClientHttpResponse(); - enhancedRestTemplateReporter.hasError(response); - - URI uri = mock(URI.class); - enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, response); - - verify(consumerAPI, times(1)).updateServiceCallResult(any()); - verify(delegate, times(0)).handleError(uri, HttpMethod.GET, response); - } - - @Test - public void testReportSwitchOff() throws IOException { - when(reporterProperties.isEnabled()).thenReturn(false); - when(delegate.hasError(any())).thenReturn(true); - - MockedClientHttpResponse response = new MockedClientHttpResponse(); - enhancedRestTemplateReporter.hasError(response); - - URI uri = mock(URI.class); - enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, response); - - verify(consumerAPI, times(0)).updateServiceCallResult(any()); - verify(delegate).handleError(uri, HttpMethod.GET, response); - } - - static class MockedClientHttpResponse extends AbstractClientHttpResponse { - - private final HttpHeaders headers; - - MockedClientHttpResponse() { - this.headers = new HttpHeaders(); - } - - @Override - public int getRawStatusCode() { - return 0; - } - - @Override - public String getStatusText() { - return null; - } - - @Override - public void close() { - - } - - @Override - public InputStream getBody() throws IOException { - return null; - } - - @Override - public HttpHeaders getHeaders() { - return headers; - } - - @Override - public HttpStatus getStatusCode() throws IOException { - return HttpStatus.OK; - } - } - - private static class MockedResponseErrorHandler extends DefaultResponseErrorHandler { - - @Override - public void handleError(@NonNull ClientHttpResponse response) { - } - - } -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java new file mode 100644 index 000000000..6d5e8816d --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java @@ -0,0 +1,136 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.scg; + +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 reactor.core.publisher.Mono; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.context.ApplicationContext; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ServerWebExchange; + +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.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR; + +@ExtendWith(MockitoExtension.class) +public class EnhancedGatewayGlobalFilterTest { + + private static MockedStatic mockedApplicationContextAwareUtils; + @Mock + private RpcEnhancementReporterProperties reporterProperties; + @Mock + private SDKContext sdkContext; + @Mock + ServerWebExchange exchange; + @Mock + GatewayFilterChain chain; + @Mock + ServerHttpResponse response; + @Mock + ServerHttpRequest request; + + @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 URISyntaxException { + + doReturn(new URI("http://0.0.0.0/")).when(request).getURI(); + doReturn(new HttpHeaders()).when(request).getHeaders(); + doReturn(HttpMethod.GET).when(request).getMethod(); + doReturn(new HttpHeaders()).when(response).getHeaders(); + doReturn(Mono.empty()).when(chain).filter(exchange); + + ServiceInstance serviceInstance = mock(ServiceInstance.class); + Response serviceInstanceResponse = new Response() { + @Override + public boolean hasServer() { + return true; + } + + @Override + public ServiceInstance getServer() { + return serviceInstance; + } + }; + doReturn(serviceInstanceResponse).when(exchange).getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR); + doReturn(request).when(exchange).getRequest(); + doReturn(response).when(exchange).getResponse(); + + EnhancedGatewayGlobalFilter reporter = new EnhancedGatewayGlobalFilter(new DefaultEnhancedPluginRunner(new ArrayList<>())); + reporter.getOrder(); + + reporter.filter(exchange, chain).block(); + + doReturn(Mono.error(new RuntimeException())).when(chain).filter(exchange); + + assertThatThrownBy(() -> reporter.filter(exchange, chain).block()).isInstanceOf(RuntimeException.class); + + } +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpClientCustomizerTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpClientCustomizerTest.java deleted file mode 100644 index 84056bf69..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpClientCustomizerTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.scg; - -import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.client.api.SDKContext; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import reactor.netty.http.client.HttpClient; - -public class EnhancedPolarisHttpClientCustomizerTest { - - @Test - public void testCustomize() { - RpcEnhancementReporterProperties properties = new RpcEnhancementReporterProperties(); - properties.setEnabled(true); - properties.getStatuses().clear(); - properties.getSeries().clear(); - - SDKContext context = Mockito.mock(SDKContext.class); - ConsumerAPI consumerAPI = Mockito.mock(ConsumerAPI.class); - - EnhancedPolarisHttpClientCustomizer clientCustomizer = new EnhancedPolarisHttpClientCustomizer(properties, context, consumerAPI); - - HttpClient client = HttpClient.create(); - HttpClient proxyClient = clientCustomizer.customize(client); - - Assertions.assertNotNull(proxyClient); - Assertions.assertEquals(EnhancedPolarisHttpClient.class.getName(), proxyClient.getClass().getName()); - } - -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpHeadersFilterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpHeadersFilterTest.java deleted file mode 100644 index f8c8b7710..000000000 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpHeadersFilterTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.rpc.enhancement.scg; - -import java.util.Collections; - -import com.tencent.cloud.common.constant.HeaderConstant; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.loadbalancer.DefaultResponse; -import org.springframework.http.HttpHeaders; -import org.springframework.web.server.ServerWebExchange; - -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR; - -public class EnhancedPolarisHttpHeadersFilterTest { - - @Test - public void testFilter() { - EnhancedPolarisHttpHeadersFilter filter = new EnhancedPolarisHttpHeadersFilter(); - - ServiceInstance instance = Mockito.mock(ServiceInstance.class); - Mockito.doReturn("mock_service").when(instance).getServiceId(); - Mockito.doReturn("127.0.0.1").when(instance).getHost(); - Mockito.doReturn(8080).when(instance).getPort(); - DefaultResponse response = new DefaultResponse(instance); - - ServerWebExchange exchange = Mockito.mock(ServerWebExchange.class); - Mockito.doReturn(response).when(exchange).getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR); - - HttpHeaders input = new HttpHeaders(); - HttpHeaders headers = filter.filter(input, exchange); - - Assertions.assertEquals(Collections.singletonList("mock_service"), headers.get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID)); - Assertions.assertEquals(Collections.singletonList("127.0.0.1"), headers.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST)); - Assertions.assertEquals(Collections.singletonList("8080"), headers.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT)); - } - -} diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientReporterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientReporterTest.java index 0393e6c1c..f8a4a673a 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientReporterTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientReporterTest.java @@ -18,42 +18,69 @@ package com.tencent.cloud.rpc.enhancement.webclient; 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.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.rpc.ServiceCallResult; +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.Assertions; 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 reactor.core.publisher.Mono; +import org.springframework.context.ApplicationContext; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.ExchangeFunction; -import static com.tencent.cloud.rpc.enhancement.webclient.EnhancedWebClientReporter.METRICS_WEBCLIENT_START_TIME; 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.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +@ExtendWith(MockitoExtension.class) public class EnhancedWebClientReporterTest { - private static final String URI_TEMPLATE_ATTRIBUTE = EnhancedWebClientReporterTest.class.getName() + ".uriTemplate"; - private static MockedStatic mockedApplicationContextAwareUtils; + @Mock + private RpcEnhancementReporterProperties reporterProperties; + @Mock + private SDKContext sdkContext; + @Mock + private ClientRequest clientRequest; + @Mock + private ExchangeFunction exchangeFunction; + @Mock + private ClientResponse clientResponse; @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 @@ -66,35 +93,28 @@ public class EnhancedWebClientReporterTest { MetadataContext.LOCAL_NAMESPACE = NAMESPACE_TEST; MetadataContext.LOCAL_SERVICE = SERVICE_PROVIDER; } - @Test - public void testInstrumentResponse() { - ClientResponse response = Mockito.mock(ClientResponse.class); - ClientResponse.Headers headers = Mockito.mock(ClientResponse.Headers.class); - Mockito.doReturn(headers).when(response).headers(); - Mockito.doReturn(new HttpHeaders()).when(headers).asHttpHeaders(); - Mono responseMono = Mono.just(response); - ClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create("https://example.org/projects/spring-boot")) - .attribute(URI_TEMPLATE_ATTRIBUTE, "https://example.org/projects/{project}") - .build(); - - ConsumerAPI consumerAPI = Mockito.mock(ConsumerAPI.class); - Mockito.doAnswer(invocationOnMock -> { - ServiceCallResult result = invocationOnMock.getArgument(0, ServiceCallResult.class); - Assertions.assertTrue(result.getDelay() > 0); - return null; - }).when(consumerAPI) - .updateServiceCallResult(Mockito.any(ServiceCallResult.class)); - - RpcEnhancementReporterProperties properties = new RpcEnhancementReporterProperties(); - properties.setEnabled(true); - properties.getStatuses().clear(); - properties.getSeries().clear(); - EnhancedWebClientReporter reporter = new EnhancedWebClientReporter(properties, null, consumerAPI); - - reporter.instrumentResponse(request, responseMono) - .contextWrite(context -> context.put(METRICS_WEBCLIENT_START_TIME, System.currentTimeMillis())) - .subscribe(); + public void testRun() throws URISyntaxException { + + doReturn(new URI("http://0.0.0.0/")).when(clientRequest).url(); + doReturn(new HttpHeaders()).when(clientRequest).headers(); + doReturn(HttpMethod.GET).when(clientRequest).method(); + ClientResponse.Headers headers = mock(ClientResponse.Headers.class); + doReturn(headers).when(clientResponse).headers(); + doReturn(Mono.just(clientResponse)).when(exchangeFunction).exchange(any()); + + EnhancedWebClientReporter reporter = new EnhancedWebClientReporter(new DefaultEnhancedPluginRunner(new ArrayList<>())); + ClientResponse clientResponse1 = reporter.filter(clientRequest, exchangeFunction).block(); + assertThat(clientResponse1).isEqualTo(clientResponse); + + ClientResponse clientResponse2 = reporter.filter(clientRequest, exchangeFunction).block(); + assertThat(clientResponse2).isEqualTo(clientResponse); + + doReturn(Mono.error(new RuntimeException())).when(exchangeFunction).exchange(any()); + + assertThatThrownBy(() -> reporter.filter(clientRequest, exchangeFunction).block()).isInstanceOf(RuntimeException.class); + + } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/webclient/PolarisLoadBalancerClientRequestTransformerTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/webclient/PolarisLoadBalancerClientRequestTransformerTest.java new file mode 100644 index 000000000..811b7c5eb --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/webclient/PolarisLoadBalancerClientRequestTransformerTest.java @@ -0,0 +1,90 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.rpc.enhancement.webclient; + +import com.tencent.cloud.common.constant.HeaderConstant; +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 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.ServiceInstance; +import org.springframework.context.ApplicationContext; +import org.springframework.web.reactive.function.client.ClientRequest; + +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.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +public class PolarisLoadBalancerClientRequestTransformerTest { + + private static MockedStatic mockedApplicationContextAwareUtils; + + private PolarisLoadBalancerClientRequestTransformer transformer = new PolarisLoadBalancerClientRequestTransformer(); + + @Mock + private ClientRequest clientRequest; + + @Mock + private ServiceInstance serviceInstance; + + @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 test() throws Throwable { + doReturn("test").when(serviceInstance).getServiceId(); + transformer.transformRequest(clientRequest, serviceInstance); + assertThat(MetadataContextHolder.get().getLoadbalancerMetadata().get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID)).isEqualTo("test"); + } +}