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..00de2e779 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,7 @@ public class PolarisCircuitBreakerHttpResponse extends AbstractClientHttpRespons PolarisCircuitBreakerHttpResponse(CircuitBreakerStatus.FallbackInfo fallbackInfo) { this.fallbackInfo = fallbackInfo; - headers.add(POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER, "true"); +// 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..a69702596 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 @@ -34,7 +34,6 @@ import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreak 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; @@ -71,7 +70,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,7 +142,6 @@ 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"))) @@ -176,11 +173,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-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..c417a22e2 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 @@ -25,7 +25,6 @@ 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; @@ -71,13 +70,12 @@ 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); - } +// @Bean +// @ConditionalOnMissingBean +// public LoadBalancerClientRequestTransformer polarisLoadBalancerClientRequestTransformer(SDKContext sdkContext) { +// ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByContext(sdkContext); +// return new PolarisLoadBalancerClientRequestTransformer(consumerAPI); +// } @Configuration(proxyBeanMethods = false) @ConditionalOnReactiveDiscoveryEnabled diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/HeaderConstant.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/HeaderConstant.java index feffd0f49..0f8b96e03 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/HeaderConstant.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/HeaderConstant.java @@ -47,6 +47,8 @@ public final class HeaderConstant { */ public static final String INTERNAL_CALLEE_INSTANCE_PORT = "internal-callee-instance-port"; + public static final String INTERNAL_CALL_START_TIME = "internal-call-start-time"; + private HeaderConstant() { } } diff --git a/spring-cloud-tencent-rpc-enhancement/pom.xml b/spring-cloud-tencent-rpc-enhancement/pom.xml index 5d7cf2d24..1a412a45b 100644 --- a/spring-cloud-tencent-rpc-enhancement/pom.xml +++ b/spring-cloud-tencent-rpc-enhancement/pom.xml @@ -32,6 +32,41 @@ + + + com.tencent.polaris + polaris-circuitbreaker-factory + + + com.tencent.polaris + router-rule + + + com.tencent.polaris + router-nearby + + + com.tencent.polaris + router-metadata + + + com.tencent.polaris + router-canary + + + com.tencent.polaris + router-set + + + com.tencent.polaris + router-isolated + + + com.tencent.polaris + router-healthy + + + 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..acf2e14b6 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,16 +17,34 @@ 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; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; 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 feign.Request; +import feign.RequestTemplate; +import feign.Response; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +52,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; @@ -58,17 +77,83 @@ public abstract class AbstractPolarisReporterAdapter { 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.setProtocol(getProtocol(uri)); + 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, + getProtocol(uri) + ); + return new ResourceStat(resource, statusCode == null ? -1 : statusCode, delay, getDefaultRetStatus(statusCode, exception)); + } + + /** * Convert items to List. * @@ -87,7 +172,7 @@ public abstract class AbstractPolarisReporterAdapter { * @param httpStatus request http status code * @return true , otherwise return false . */ - protected boolean apply(@Nullable HttpStatus httpStatus) { + private boolean apply(@Nullable HttpStatus httpStatus) { if (Objects.isNull(httpStatus)) { return false; } @@ -122,8 +207,8 @@ public abstract class AbstractPolarisReporterAdapter { return false; } - protected RetStatus getRetStatusFromRequest(HttpHeaders headers, RetStatus defaultVal) { - if (headers.containsKey(HeaderConstant.INTERNAL_CALLEE_RET_STATUS)) { + private RetStatus getRetStatusFromRequest(HttpHeaders headers, RetStatus defaultVal) { + 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)); @@ -138,14 +223,56 @@ public abstract class AbstractPolarisReporterAdapter { return defaultVal; } - protected String getActiveRuleNameFromRequest(HttpHeaders headers) { - if (headers.containsKey(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME)) { + private String getActiveRuleNameFromRequest(HttpHeaders headers) { + 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 String getProtocol(URI uri) { + String scheme = uri.getScheme(); + if (StringUtils.isBlank(scheme)) { + scheme = "http"; + } + return scheme; + } + + 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) { + 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 e8196a642..2d65b4377 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 @@ -29,11 +29,13 @@ 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.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.PolarisGatewayReporter; +import com.tencent.cloud.rpc.enhancement.resttemplate.PolarisRestTemplateReporter; import com.tencent.cloud.rpc.enhancement.scg.EnhancedPolarisHttpHeadersFilter; import com.tencent.cloud.rpc.enhancement.webclient.EnhancedWebClientReporter; import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory; import com.tencent.polaris.client.api.SDKContext; import org.springframework.beans.factory.SmartInitializingSingleton; @@ -46,7 +48,6 @@ 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; @@ -54,6 +55,7 @@ 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 +70,12 @@ import org.springframework.web.reactive.function.client.ExchangeFilterFunction; @AutoConfigureAfter(PolarisContextAutoConfiguration.class) public class RpcEnhancementAutoConfiguration { + @Bean + @ConditionalOnMissingBean(CircuitBreakAPI.class) + public CircuitBreakAPI circuitBreakAPI(SDKContext polarisContext) { + return CircuitBreakAPIFactory.createCircuitBreakAPIByContext(polarisContext); + } + /** * Configuration for Polaris {@link feign.Feign} which can automatically bring in the call * results for reporting. @@ -96,16 +104,18 @@ public class RpcEnhancementAutoConfiguration { @Bean public SuccessPolarisReporter successPolarisReporter(RpcEnhancementReporterProperties properties, - @Autowired(required = false) SDKContext context, - @Autowired(required = false) ConsumerAPI consumerAPI) { - return new SuccessPolarisReporter(properties, context, consumerAPI); + SDKContext context, + ConsumerAPI consumerAPI, + CircuitBreakAPI circuitBreakAPI) { + return new SuccessPolarisReporter(properties, context, consumerAPI, circuitBreakAPI); } @Bean public ExceptionPolarisReporter exceptionPolarisReporter(RpcEnhancementReporterProperties properties, - @Autowired(required = false) SDKContext context, - @Autowired(required = false) ConsumerAPI consumerAPI) { - return new ExceptionPolarisReporter(properties, context, consumerAPI); + SDKContext context, + ConsumerAPI consumerAPI, + CircuitBreakAPI circuitBreakAPI) { + return new ExceptionPolarisReporter(properties, context, consumerAPI, circuitBreakAPI); } } } @@ -125,16 +135,18 @@ 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 PolarisRestTemplateReporter polarisRestTemplateReporter(RpcEnhancementReporterProperties properties, + SDKContext context, + ConsumerAPI consumerAPI, + CircuitBreakAPI circuitBreakAPI) { + return new PolarisRestTemplateReporter(properties, context, consumerAPI, circuitBreakAPI); } @Bean - public SmartInitializingSingleton setErrorHandlerForRestTemplate(EnhancedRestTemplateReporter reporter) { + public SmartInitializingSingleton setPolarisReporterForRestTemplate(PolarisRestTemplateReporter reporter) { return () -> { for (RestTemplate restTemplate : restTemplates) { - restTemplate.setErrorHandler(reporter); + restTemplate.getInterceptors().add(reporter); } }; } @@ -156,10 +168,22 @@ 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 EnhancedWebClientReporter exchangeFilterFunction(RpcEnhancementReporterProperties properties, + SDKContext context, + ConsumerAPI consumerAPI, + CircuitBreakAPI circuitBreakAPI) { + return new EnhancedWebClientReporter(properties, context, consumerAPI, circuitBreakAPI); + } + @Bean - public ExchangeFilterFunction exchangeFilterFunction( - RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { - return new EnhancedWebClientReporter(properties, context, consumerAPI); + public SmartInitializingSingleton addEncodeTransferMetadataFilterForWebClient(EnhancedWebClientReporter reporter) { + return () -> webClientBuilder.forEach(webClient -> { + webClient.filter(reporter); + }); } } @@ -179,10 +203,12 @@ public class RpcEnhancementAutoConfiguration { } @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 PolarisGatewayReporter polarisGatewayReporter(RpcEnhancementReporterProperties properties, + SDKContext context, + ConsumerAPI consumerAPI, + CircuitBreakAPI circuitBreakAPI) { + return new PolarisGatewayReporter(properties, context, consumerAPI, circuitBreakAPI); } } 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 index 487c64134..3948ffbfb 100644 --- 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 @@ -18,6 +18,7 @@ package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; import java.net.SocketTimeoutException; +import java.net.URI; import java.util.ArrayList; import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; @@ -26,8 +27,10 @@ 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.plugin.circuitbreaker.ResourceStat; import com.tencent.polaris.api.pojo.RetStatus; import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; import com.tencent.polaris.client.api.SDKContext; import feign.Request; import feign.Response; @@ -49,16 +52,16 @@ public class ExceptionPolarisReporter extends AbstractPolarisReporterAdapter imp private final ConsumerAPI consumerAPI; - private final SDKContext context; - + private final CircuitBreakAPI circuitBreakAPI; public ExceptionPolarisReporter(RpcEnhancementReporterProperties reporterProperties, SDKContext context, - ConsumerAPI consumerAPI) { - super(reporterProperties); + ConsumerAPI consumerAPI, + CircuitBreakAPI circuitBreakAPI) { + super(reporterProperties, context); this.reporterProperties = reporterProperties; - this.context = context; this.consumerAPI = consumerAPI; + this.circuitBreakAPI = circuitBreakAPI; } @Override @@ -77,26 +80,46 @@ public class ExceptionPolarisReporter extends AbstractPolarisReporterAdapter imp 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); + Request request = context.getRequest(); + Response response = context.getResponse(); + Exception exception = context.getException(); + long delay = context.getDelay(); + + HttpHeaders requestHeaders = new HttpHeaders(); + request.headers().forEach((s, strings) -> requestHeaders.addAll(s, new ArrayList<>(strings))); + HttpHeaders responseHeaders = new HttpHeaders(); + Integer status = null; + if (response != null) { + response.headers().forEach((s, strings) -> responseHeaders.addAll(s, new ArrayList<>(strings))); + status = response.status(); } + + ServiceCallResult resultRequest = createServiceCallResult( + request.requestTemplate().feignTarget().name(), + null, + null, + URI.create(request.url()), + requestHeaders, + responseHeaders, + status, + delay, + exception + ); + LOG.debug("Will report result of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", + resultRequest.getRetStatus().name(), request.httpMethod().name(), request.url(), status, delay); + consumerAPI.updateServiceCallResult(resultRequest); + + ResourceStat resourceStat = createInstanceResourceStat( + request.requestTemplate().feignTarget().name(), + null, + null, + URI.create(request.url()), + status, + delay, + exception + ); + circuitBreakAPI.report(resourceStat); + } @Override 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 index 7d6a94b0e..43fda9978 100644 --- 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 @@ -1,100 +1,140 @@ -/* - * 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; - } - -} +///* +// * 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.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 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 ResourceStat createInstanceResourceStat(final Request request, +// final Response response, long delay, RetStatus retStatus) { +// return createInstanceResourceStat(request.requestTemplate().feignTarget().name(), URI.create(request.url()), response.status(), delay, retStatus); +// } +// +// public static ResourceStat createInstanceResourceStat(final URI uri, +// final int status, long delay, RetStatus retStatus) { +// return createInstanceResourceStat(null, uri, status, delay, retStatus); +// } +// +// private static ResourceStat createInstanceResourceStat(final String serviceName, +// final URI uri, final int status, long delay, RetStatus retStatus) { +// ServiceKey calleeServiceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, serviceName); +// ServiceKey callerServiceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); +// Resource resource = new InstanceResource(calleeServiceKey, uri.getHost(), getRequestPort(uri), callerServiceKey, getRequestProtocol(uri)); +// return new ResourceStat(resource, status, delay, retStatus); +// } +// +// public static ServiceCallResult createServiceCallResult(final SDKContext context, final URI uri, +// final int status, long delay, RetStatus retStatus, final Consumer consumer) { +// +// } +// +// +// 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); +// resultRequest.setProtocol(getRequestProtocol(uri)); +// 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()); +// resultRequest.setPort(getRequestPort(uri)); +// consumer.accept(resultRequest); +// return resultRequest; +// } +// +// private static String getRequestProtocol(URI uri) { +// String scheme = uri.getScheme(); +// if (StringUtils.isBlank(scheme)) { +// scheme = "http"; +// } +// return scheme; +// } +// +// private static int getRequestPort(URI uri) { +// // -1 means access directly by url, and use http default port number 80 +// return uri.getPort() == -1 ? 80 : uri.getPort(); +// } +// +// private static String convertLabel(String label) { +// label = label.replaceAll("\"|\\{|\\}", "") +// .replaceAll(",", "|"); +// return label; +// } +//} 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 index eaa0ae916..d4ca83dfd 100644 --- 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 @@ -17,6 +17,7 @@ package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; +import java.net.URI; import java.util.ArrayList; import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; @@ -25,8 +26,11 @@ 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.plugin.circuitbreaker.ResourceStat; +import com.tencent.polaris.api.plugin.circuitbreaker.entity.InstanceResource; import com.tencent.polaris.api.pojo.RetStatus; import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; import com.tencent.polaris.client.api.SDKContext; import feign.Request; import feign.Response; @@ -48,12 +52,15 @@ public class SuccessPolarisReporter extends AbstractPolarisReporterAdapter imple private final ConsumerAPI consumerAPI; - private final SDKContext context; + private final CircuitBreakAPI circuitBreakAPI; - public SuccessPolarisReporter(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { - super(properties); - this.context = context; + public SuccessPolarisReporter(RpcEnhancementReporterProperties properties, + SDKContext context, + ConsumerAPI consumerAPI, + CircuitBreakAPI circuitBreakAPI) { + super(properties, context); this.consumerAPI = consumerAPI; + this.circuitBreakAPI = circuitBreakAPI; } @Override @@ -72,25 +79,44 @@ public class SuccessPolarisReporter extends AbstractPolarisReporterAdapter imple 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); + Request request = context.getRequest(); + Response response = context.getResponse(); + long delay = context.getDelay(); + HttpHeaders requestHeaders = new HttpHeaders(); + request.headers().forEach((s, strings) -> requestHeaders.addAll(s, new ArrayList<>(strings))); + HttpHeaders responseHeaders = new HttpHeaders(); + Integer status = null; + if (response != null) { + response.headers().forEach((s, strings) -> responseHeaders.addAll(s, new ArrayList<>(strings))); + status = response.status(); } + + ServiceCallResult resultRequest = createServiceCallResult( + request.requestTemplate().feignTarget().name(), + null, + null, + URI.create(request.url()), + requestHeaders, + responseHeaders, + status, + delay, + null + ); + LOG.debug("Will report result of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", + resultRequest.getRetStatus().name(), request.httpMethod().name(), request.url(), status, delay); + consumerAPI.updateServiceCallResult(resultRequest); + + ResourceStat resourceStat = createInstanceResourceStat( + request.requestTemplate().feignTarget().name(), + null, + null, + URI.create(request.url()), + status, + delay, + null + ); + circuitBreakAPI.report(resourceStat); + } @Override 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 index 03ff2c460..b5acc1e8d 100644 --- 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 @@ -1,237 +1,240 @@ -/* - * 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.HttpStatus; -import org.springframework.http.HttpStatusCode; -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 - HttpStatusCode httpStatusCode = response.getStatusCode(); - HttpStatus httpStatus = HttpStatus.valueOf(httpStatusCode.value()); - if (apply(httpStatus)) { - 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, httpStatusCode.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; - } -} +///* +// * 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.cloud.rpc.enhancement.feign.plugin.reporter.ReporterUtils; +//import com.tencent.polaris.api.core.ConsumerAPI; +//import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat; +//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.circuitbreak.api.CircuitBreakAPI; +//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 final CircuitBreakAPI circuitBreakAPI; +// private ResponseErrorHandler delegateHandler; +// +// public EnhancedRestTemplateReporter(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI, CircuitBreakAPI circuitBreakAPI) { +// super(properties); +// this.context = context; +// this.consumerAPI = consumerAPI; +// this.circuitBreakAPI = circuitBreakAPI; +// } +// +// @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 { +// 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; +// } +// +// ServiceCallResult resultRequest = createServiceCallResult(url, response); +// +// 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); +// ResourceStat resourceStat = ReporterUtils.createInstanceResourceStat(url, response.getRawStatusCode(), delay, resultRequest.getRetStatus()); +// circuitBreakAPI.report(resourceStat); +// } +// 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..4d46d00fc 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,9 @@ 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())); + MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALL_START_TIME, String.valueOf(System.currentTimeMillis())); } } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/PolarisRestTemplateReporter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/PolarisRestTemplateReporter.java new file mode 100644 index 000000000..87f309528 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/PolarisRestTemplateReporter.java @@ -0,0 +1,124 @@ +/* + * 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.util.Map; + +import com.tencent.cloud.common.constant.HeaderConstant; +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.plugin.circuitbreaker.ResourceStat; +import com.tencent.polaris.api.rpc.ServiceCallResult; +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.http.HttpHeaders; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; + +public class PolarisRestTemplateReporter extends AbstractPolarisReporterAdapter implements ClientHttpRequestInterceptor { + + private static final Logger LOG = LoggerFactory.getLogger(PolarisRestTemplateReporter.class); + + private final ConsumerAPI consumerAPI; + + private final CircuitBreakAPI circuitBreakAPI; + + + /** + * Constructor With {@link RpcEnhancementReporterProperties} . + * + * @param reportProperties instance of {@link RpcEnhancementReporterProperties}. + */ + public PolarisRestTemplateReporter(RpcEnhancementReporterProperties reportProperties, + SDKContext context, + ConsumerAPI consumerAPI, + CircuitBreakAPI circuitBreakAPI) { + super(reportProperties, context); + this.consumerAPI = consumerAPI; + this.circuitBreakAPI = circuitBreakAPI; + } + + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + if (!reportProperties.isEnabled()) { + return execution.execute(request, body); + } + + ClientHttpResponse response = null; + IOException ex = null; + try { + response = execution.execute(request, body); + } catch (SocketTimeoutException e) { + ex = e; + } + HttpHeaders requestHeaders = request.getHeaders(); + HttpHeaders responseHeaders = null; + Integer status = null; + if (response != null) { + responseHeaders = response.getHeaders(); + status = response.getRawStatusCode(); + } + + Map loadBalancerContext = MetadataContextHolder.get().getLoadbalancerMetadata(); + String targetHost = loadBalancerContext.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST); + Integer targetPort = Integer.valueOf(loadBalancerContext.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT)); + long delay = System.currentTimeMillis() - Long.parseLong(loadBalancerContext.get(HeaderConstant.INTERNAL_CALL_START_TIME)); + + ServiceCallResult resultRequest = createServiceCallResult( + request.getURI().getHost(), + targetHost, + targetPort, + request.getURI(), + requestHeaders, + responseHeaders, + status, + delay, + ex + ); + LOG.debug("Will report result of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", + resultRequest.getRetStatus().name(), request.getMethod(), request.getURI().getPath(), status, delay); + consumerAPI.updateServiceCallResult(resultRequest); + + ResourceStat resourceStat = createInstanceResourceStat( + request.getURI().getHost(), + targetHost, + targetPort, + request.getURI(), + status, + delay, + ex + ); + circuitBreakAPI.report(resourceStat); + + if (ex != null) { + throw ex; + } + return response; + } + +} 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 index 33b2b556c..b7f80546e 100644 --- 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 @@ -1,173 +1,173 @@ -/* - * 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); - } - } - -} +///* +// * 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 index 630feb349..c17cbc499 100644 --- 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 @@ -1,43 +1,43 @@ -/* - * 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); - } -} +///* +// * 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/PolarisGatewayReporter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/PolarisGatewayReporter.java new file mode 100644 index 000000000..59298c714 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/PolarisGatewayReporter.java @@ -0,0 +1,109 @@ +package com.tencent.cloud.rpc.enhancement.scg; + +import java.util.Map; + +import com.tencent.cloud.common.constant.HeaderConstant; +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.plugin.circuitbreaker.ResourceStat; +import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.client.api.SDKContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ServerWebExchange; + +public class PolarisGatewayReporter extends AbstractPolarisReporterAdapter implements GlobalFilter { + + private static final Logger LOG = LoggerFactory.getLogger(PolarisGatewayReporter.class); + + private final ConsumerAPI consumerAPI; + + private final CircuitBreakAPI circuitBreakAPI; + + /** + * Constructor With {@link RpcEnhancementReporterProperties} . + * + * @param reportProperties instance of {@link RpcEnhancementReporterProperties}. + */ + public PolarisGatewayReporter(RpcEnhancementReporterProperties reportProperties, + SDKContext context, + ConsumerAPI consumerAPI, + CircuitBreakAPI circuitBreakAPI) { + super(reportProperties, context); + this.consumerAPI = consumerAPI; + this.circuitBreakAPI = circuitBreakAPI; + } + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + if (!reportProperties.isEnabled()) { + return chain.filter(exchange); + } + MetadataContextHolder.get().setLoadbalancer( + HeaderConstant.INTERNAL_CALLEE_SERVICE_ID, + exchange.getRequest().getHeaders().getFirst(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID) + ); + MetadataContextHolder.get().setLoadbalancer( + HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST, + exchange.getRequest().getHeaders().getFirst(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST) + ); + MetadataContextHolder.get().setLoadbalancer( + HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT, + exchange.getRequest().getHeaders().getFirst(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT) + ); + MetadataContextHolder.get().setLoadbalancer( + HeaderConstant.INTERNAL_CALL_START_TIME, + String.valueOf(System.currentTimeMillis()) + ); + return chain.filter(exchange) + .doOnSuccess(v -> instrumentResponse(exchange, null)) + .doOnError(t -> instrumentResponse(exchange, t)); + } + + private void instrumentResponse(ServerWebExchange exchange, Throwable t) { + Map loadBalancerContext = MetadataContextHolder.get().getLoadbalancerMetadata(); + String serviceId = loadBalancerContext.get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID); + String targetHost = loadBalancerContext.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST); + Integer targetPort = Integer.valueOf(loadBalancerContext.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT)); + long delay = System.currentTimeMillis() - Long.parseLong(loadBalancerContext.get(HeaderConstant.INTERNAL_CALL_START_TIME)); + + ServerHttpResponse response = exchange.getResponse(); + ServerHttpRequest request = exchange.getRequest(); + + ServiceCallResult resultRequest = createServiceCallResult( + serviceId, + targetHost, + targetPort, + request.getURI(), + request.getHeaders(), + response.getHeaders(), + response.getRawStatusCode(), + delay, + t + ); + LOG.debug("Will report result of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", + resultRequest.getRetStatus().name(), request.getMethod(), request.getURI().getPath(), response.getRawStatusCode(), delay); + consumerAPI.updateServiceCallResult(resultRequest); + + ResourceStat resourceStat = createInstanceResourceStat( + serviceId, + targetHost, + targetPort, + request.getURI(), + response.getRawStatusCode(), + delay, + t + ); + circuitBreakAPI.report(resourceStat); + } + +} 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 e3309a98d..876b87e99 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 @@ -22,19 +22,23 @@ import java.net.SocketTimeoutException; import java.net.URI; import java.net.URLDecoder; import java.util.Collection; +import java.util.Map; 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.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.plugin.circuitbreaker.ResourceStat; 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.circuitbreak.api.CircuitBreakAPI; import com.tencent.polaris.client.api.SDKContext; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; @@ -44,7 +48,6 @@ import reactor.util.context.Context; import reactor.util.context.ContextView; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; @@ -53,93 +56,76 @@ import org.springframework.web.reactive.function.client.ExchangeFunction; import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; 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 static final Logger LOG = LoggerFactory.getLogger(EnhancedWebClientReporter.class); private final ConsumerAPI consumerAPI; - private final SDKContext context; + private final CircuitBreakAPI circuitBreakAPI; - public EnhancedWebClientReporter(RpcEnhancementReporterProperties reportProperties, SDKContext context, ConsumerAPI consumerAPI) { - super(reportProperties); - this.context = context; + public EnhancedWebClientReporter(RpcEnhancementReporterProperties reportProperties, + SDKContext context, + ConsumerAPI consumerAPI, + CircuitBreakAPI circuitBreakAPI) { + super(reportProperties, context); this.consumerAPI = consumerAPI; + this.circuitBreakAPI = circuitBreakAPI; } @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(HttpStatus.valueOf(response.statusCode().value()))) { - 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); + if (!reportProperties.isEnabled()) { + return next.exchange(request); + } + + MetadataContextHolder.get().setLoadbalancer( + HeaderConstant.INTERNAL_CALLEE_SERVICE_ID, + request.headers().getFirst(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID) + ); + MetadataContextHolder.get().setLoadbalancer( + HeaderConstant.INTERNAL_CALL_START_TIME, + String.valueOf(System.currentTimeMillis()) + ); + return next.exchange(request) + .doOnSuccess(response -> instrumentResponse(request, response, null)) + .doOnError(t -> instrumentResponse(request, null, t)); } - private Context putStartTime(Context context) { - return context.put(METRICS_WEBCLIENT_START_TIME, System.currentTimeMillis()); + private void instrumentResponse(ClientRequest request, ClientResponse response, Throwable t) { + Map loadBalancerContext = MetadataContextHolder.get().getLoadbalancerMetadata(); + String serviceId = loadBalancerContext.get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID); + long delay = System.currentTimeMillis() - Long.parseLong(loadBalancerContext.get(HeaderConstant.INTERNAL_CALL_START_TIME)); + + HttpHeaders requestHeaders = request.headers(); + HttpHeaders responseHeaders = null; + Integer status = null; + if (response != null) { + responseHeaders = response.headers().asHttpHeaders(); + status = response.rawStatusCode(); + } + + ServiceCallResult resultRequest = createServiceCallResult( + serviceId, + null, + null, + request.url(), + requestHeaders, + responseHeaders, + status, + delay, + t + ); + LOG.debug("Will report result of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", + resultRequest.getRetStatus().name(), request.method().name(), request.url().getPath(), status, delay); + consumerAPI.updateServiceCallResult(resultRequest); + + ResourceStat resourceStat = createInstanceResourceStat( + serviceId, + null, + null, + request.url(), + status, + delay, + t + ); + circuitBreakAPI.report(resourceStat); } } 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..8b7042882 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfigurationTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfigurationTest.java @@ -22,7 +22,7 @@ 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.resttemplate.PolarisRestTemplateReporter; import com.tencent.polaris.api.core.ConsumerAPI; import org.junit.jupiter.api.Test; @@ -60,10 +60,8 @@ public class RpcEnhancementAutoConfigurationTest { assertThat(context).hasSingleBean(EnhancedFeignBeanPostProcessor.class); assertThat(context).hasSingleBean(SuccessPolarisReporter.class); assertThat(context).hasSingleBean(ExceptionPolarisReporter.class); - assertThat(context).hasSingleBean(EnhancedRestTemplateReporter.class); + assertThat(context).hasSingleBean(PolarisRestTemplateReporter.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/resttemplate/EnhancedRestTemplateReporterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporterTest.java index c28f3c6ed..92db4f0ac 100644 --- 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 @@ -1,230 +1,231 @@ -/* - * 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) { - } - - } -} +///* +// * 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/EnhancedPolarisHttpClientCustomizerTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedPolarisHttpClientCustomizerTest.java index 84056bf69..b6befe635 100644 --- 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 @@ -1,49 +1,49 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.cloud.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()); - } - -} +///* +// * 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()); +// } +// +//}