temp commit

pull/964/head
seanyu 2 years ago
parent 8869e901b9
commit 629c7441a4

@ -28,8 +28,6 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.client.AbstractClientHttpResponse; import org.springframework.http.client.AbstractClientHttpResponse;
import static com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter.POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER;
/** /**
* PolarisCircuitBreakerHttpResponse. * PolarisCircuitBreakerHttpResponse.
* *
@ -57,7 +55,7 @@ public class PolarisCircuitBreakerHttpResponse extends AbstractClientHttpRespons
PolarisCircuitBreakerHttpResponse(CircuitBreakerStatus.FallbackInfo fallbackInfo) { PolarisCircuitBreakerHttpResponse(CircuitBreakerStatus.FallbackInfo fallbackInfo) {
this.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) { if (fallbackInfo.getHeaders() != null) {
fallbackInfo.getHeaders().forEach(headers::add); fallbackInfo.getHeaders().forEach(headers::add);
} }

@ -20,7 +20,6 @@ package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter;
import com.tencent.polaris.api.pojo.CircuitBreakerStatus; import com.tencent.polaris.api.pojo.CircuitBreakerStatus;
import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException; 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.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import static com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter.HEADER_HAS_ERROR;
/** /**
* PolarisCircuitBreakerRestTemplateInterceptor. * PolarisCircuitBreakerRestTemplateInterceptor.
* *
@ -70,15 +67,8 @@ public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpR
() -> { () -> {
try { try {
ClientHttpResponse response = execution.execute(request, body); 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(); ResponseErrorHandler errorHandler = restTemplate.getErrorHandler();
boolean hasError = errorHandler.hasError(response); if (errorHandler.hasError(response)) {
if (errorHandler instanceof EnhancedRestTemplateReporter) {
hasError = Boolean.parseBoolean(response.getHeaders().getFirst(HEADER_HAS_ERROR));
}
if (hasError) {
errorHandler.handleError(request.getURI(), request.getMethod(), response); errorHandler.handleError(request.getURI(), request.getMethod(), response);
} }
return response; return response;

@ -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.PolarisCircuitBreakerFallback;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerHttpResponse; import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerHttpResponse;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter;
import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
@ -71,7 +70,6 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory; 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 com.tencent.polaris.test.common.TestUtils.SERVER_ADDRESS_ENV;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@ -144,7 +142,6 @@ public class PolarisCircuitBreakerIntegrationTest {
mockServer.verify(); mockServer.verify();
mockServer.reset(); mockServer.reset();
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.add(HEADER_HAS_ERROR, "true");
// no delegateHandler in EnhancedRestTemplateReporter, so this will except err // no delegateHandler in EnhancedRestTemplateReporter, so this will except err
mockServer mockServer
.expect(ExpectedCount.once(), requestTo(new URI("http://localhost:18001/example/service/b/info"))) .expect(ExpectedCount.once(), requestTo(new URI("http://localhost:18001/example/service/b/info")))
@ -176,11 +173,8 @@ public class PolarisCircuitBreakerIntegrationTest {
@Bean @Bean
@PolarisCircuitBreaker(fallback = "fallback") @PolarisCircuitBreaker(fallback = "fallback")
public RestTemplate defaultRestTemplate(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { public RestTemplate defaultRestTemplate() {
RestTemplate defaultRestTemplate = new RestTemplate(); return new RestTemplate();
EnhancedRestTemplateReporter enhancedRestTemplateReporter = new EnhancedRestTemplateReporter(properties, context, consumerAPI);
defaultRestTemplate.setErrorHandler(enhancedRestTemplateReporter);
return defaultRestTemplate;
} }
@Bean @Bean

@ -25,7 +25,6 @@ import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import com.tencent.polaris.router.api.core.RouterAPI; import com.tencent.polaris.router.api.core.RouterAPI;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled; import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
@ -71,13 +70,12 @@ public class PolarisLoadBalancerClientConfiguration {
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), routerAPI); loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), routerAPI);
} }
@Bean // @Bean
@ConditionalOnMissingBean // @ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.reactive.function.client.ClientRequest") // public LoadBalancerClientRequestTransformer polarisLoadBalancerClientRequestTransformer(SDKContext sdkContext) {
public LoadBalancerClientRequestTransformer polarisLoadBalancerClientRequestTransformer(SDKContext sdkContext) { // ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByContext(sdkContext);
ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByContext(sdkContext); // return new PolarisLoadBalancerClientRequestTransformer(consumerAPI);
return new PolarisLoadBalancerClientRequestTransformer(consumerAPI); // }
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnReactiveDiscoveryEnabled @ConditionalOnReactiveDiscoveryEnabled

@ -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_CALLEE_INSTANCE_PORT = "internal-callee-instance-port";
public static final String INTERNAL_CALL_START_TIME = "internal-call-start-time";
private HeaderConstant() { private HeaderConstant() {
} }
} }

@ -32,6 +32,41 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-circuitbreaker-factory</artifactId>
<exclusions>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-rule</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-nearby</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-metadata</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-canary</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-set</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-isolated</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-healthy</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Polaris dependencies end --> <!-- Polaris dependencies end -->
<dependency> <dependency>

@ -17,16 +17,34 @@
package com.tencent.cloud.rpc.enhancement; 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.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer;
import com.tencent.cloud.common.constant.HeaderConstant; 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.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.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.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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -34,6 +52,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable; 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.BAD_GATEWAY;
import static org.springframework.http.HttpStatus.BANDWIDTH_LIMIT_EXCEEDED; import static org.springframework.http.HttpStatus.BANDWIDTH_LIMIT_EXCEEDED;
import static org.springframework.http.HttpStatus.GATEWAY_TIMEOUT; import static org.springframework.http.HttpStatus.GATEWAY_TIMEOUT;
@ -58,17 +77,83 @@ public abstract class AbstractPolarisReporterAdapter {
private static final List<HttpStatus> HTTP_STATUSES = toList(NOT_IMPLEMENTED, BAD_GATEWAY, private static final List<HttpStatus> HTTP_STATUSES = toList(NOT_IMPLEMENTED, BAD_GATEWAY,
SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, HTTP_VERSION_NOT_SUPPORTED, VARIANT_ALSO_NEGOTIATES, SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, HTTP_VERSION_NOT_SUPPORTED, VARIANT_ALSO_NEGOTIATES,
INSUFFICIENT_STORAGE, LOOP_DETECTED, BANDWIDTH_LIMIT_EXCEEDED, NOT_EXTENDED, NETWORK_AUTHENTICATION_REQUIRED); INSUFFICIENT_STORAGE, LOOP_DETECTED, BANDWIDTH_LIMIT_EXCEEDED, NOT_EXTENDED, NETWORK_AUTHENTICATION_REQUIRED);
protected final RpcEnhancementReporterProperties reportProperties; protected final RpcEnhancementReporterProperties reportProperties;
protected final SDKContext context;
/** /**
* Constructor With {@link RpcEnhancementReporterProperties} . * Constructor With {@link RpcEnhancementReporterProperties} .
* *
* @param reportProperties instance of {@link RpcEnhancementReporterProperties}. * @param reportProperties instance of {@link RpcEnhancementReporterProperties}.
*/ */
protected AbstractPolarisReporterAdapter(RpcEnhancementReporterProperties reportProperties) { protected AbstractPolarisReporterAdapter(RpcEnhancementReporterProperties reportProperties, SDKContext context) {
this.reportProperties = reportProperties; 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. * Convert items to List.
* *
@ -87,7 +172,7 @@ public abstract class AbstractPolarisReporterAdapter {
* @param httpStatus request http status code * @param httpStatus request http status code
* @return true , otherwise return false . * @return true , otherwise return false .
*/ */
protected boolean apply(@Nullable HttpStatus httpStatus) { private boolean apply(@Nullable HttpStatus httpStatus) {
if (Objects.isNull(httpStatus)) { if (Objects.isNull(httpStatus)) {
return false; return false;
} }
@ -122,8 +207,8 @@ public abstract class AbstractPolarisReporterAdapter {
return false; return false;
} }
protected RetStatus getRetStatusFromRequest(HttpHeaders headers, RetStatus defaultVal) { private RetStatus getRetStatusFromRequest(HttpHeaders headers, RetStatus defaultVal) {
if (headers.containsKey(HeaderConstant.INTERNAL_CALLEE_RET_STATUS)) { if (headers != null && headers.containsKey(HeaderConstant.INTERNAL_CALLEE_RET_STATUS)) {
List<String> values = headers.get(HeaderConstant.INTERNAL_CALLEE_RET_STATUS); List<String> values = headers.get(HeaderConstant.INTERNAL_CALLEE_RET_STATUS);
if (CollectionUtils.isNotEmpty(values)) { if (CollectionUtils.isNotEmpty(values)) {
String retStatusVal = com.tencent.polaris.api.utils.StringUtils.defaultString(values.get(0)); String retStatusVal = com.tencent.polaris.api.utils.StringUtils.defaultString(values.get(0));
@ -138,14 +223,56 @@ public abstract class AbstractPolarisReporterAdapter {
return defaultVal; return defaultVal;
} }
protected String getActiveRuleNameFromRequest(HttpHeaders headers) { private String getActiveRuleNameFromRequest(HttpHeaders headers) {
if (headers.containsKey(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME)) { if (headers != null && headers.containsKey(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME)) {
Collection<String> values = headers.get(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME); Collection<String> values = headers.get(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME);
if (CollectionUtils.isNotEmpty(values)) { if (CollectionUtils.isNotEmpty(values)) {
String val = com.tencent.polaris.api.utils.StringUtils.defaultString(new ArrayList<>(values).get(0)); return com.tencent.polaris.api.utils.StringUtils.defaultString(new ArrayList<>(values).get(0));
return val;
} }
} }
return ""; 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<String> 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;
}
} }

@ -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.ExceptionPolarisReporter;
import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.SuccessPolarisReporter; 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.BlockingLoadBalancerClientAspect;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter; import com.tencent.cloud.rpc.enhancement.scg.PolarisGatewayReporter;
import com.tencent.cloud.rpc.enhancement.scg.EnhancedPolarisHttpClientCustomizer; import com.tencent.cloud.rpc.enhancement.resttemplate.PolarisRestTemplateReporter;
import com.tencent.cloud.rpc.enhancement.scg.EnhancedPolarisHttpHeadersFilter; import com.tencent.cloud.rpc.enhancement.scg.EnhancedPolarisHttpHeadersFilter;
import com.tencent.cloud.rpc.enhancement.webclient.EnhancedWebClientReporter; import com.tencent.cloud.rpc.enhancement.webclient.EnhancedWebClientReporter;
import com.tencent.polaris.api.core.ConsumerAPI; 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 com.tencent.polaris.client.api.SDKContext;
import org.springframework.beans.factory.SmartInitializingSingleton; 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.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalanced; 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.cloud.gateway.filter.headers.HttpHeadersFilter;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; 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.context.annotation.Role;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction; 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 * 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) @AutoConfigureAfter(PolarisContextAutoConfiguration.class)
public class RpcEnhancementAutoConfiguration { 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 * Configuration for Polaris {@link feign.Feign} which can automatically bring in the call
* results for reporting. * results for reporting.
@ -96,16 +104,18 @@ public class RpcEnhancementAutoConfiguration {
@Bean @Bean
public SuccessPolarisReporter successPolarisReporter(RpcEnhancementReporterProperties properties, public SuccessPolarisReporter successPolarisReporter(RpcEnhancementReporterProperties properties,
@Autowired(required = false) SDKContext context, SDKContext context,
@Autowired(required = false) ConsumerAPI consumerAPI) { ConsumerAPI consumerAPI,
return new SuccessPolarisReporter(properties, context, consumerAPI); CircuitBreakAPI circuitBreakAPI) {
return new SuccessPolarisReporter(properties, context, consumerAPI, circuitBreakAPI);
} }
@Bean @Bean
public ExceptionPolarisReporter exceptionPolarisReporter(RpcEnhancementReporterProperties properties, public ExceptionPolarisReporter exceptionPolarisReporter(RpcEnhancementReporterProperties properties,
@Autowired(required = false) SDKContext context, SDKContext context,
@Autowired(required = false) ConsumerAPI consumerAPI) { ConsumerAPI consumerAPI,
return new ExceptionPolarisReporter(properties, context, consumerAPI); CircuitBreakAPI circuitBreakAPI) {
return new ExceptionPolarisReporter(properties, context, consumerAPI, circuitBreakAPI);
} }
} }
} }
@ -125,16 +135,18 @@ public class RpcEnhancementAutoConfiguration {
private List<RestTemplate> restTemplates = Collections.emptyList(); private List<RestTemplate> restTemplates = Collections.emptyList();
@Bean @Bean
public EnhancedRestTemplateReporter enhancedRestTemplateReporter( public PolarisRestTemplateReporter polarisRestTemplateReporter(RpcEnhancementReporterProperties properties,
RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { SDKContext context,
return new EnhancedRestTemplateReporter(properties, context, consumerAPI); ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
return new PolarisRestTemplateReporter(properties, context, consumerAPI, circuitBreakAPI);
} }
@Bean @Bean
public SmartInitializingSingleton setErrorHandlerForRestTemplate(EnhancedRestTemplateReporter reporter) { public SmartInitializingSingleton setPolarisReporterForRestTemplate(PolarisRestTemplateReporter reporter) {
return () -> { return () -> {
for (RestTemplate restTemplate : restTemplates) { 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") @ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient")
protected static class PolarisWebClientAutoConfiguration { protected static class PolarisWebClientAutoConfiguration {
@Autowired(required = false)
private List<WebClient.Builder> webClientBuilder = Collections.emptyList();
@Bean
public EnhancedWebClientReporter exchangeFilterFunction(RpcEnhancementReporterProperties properties,
SDKContext context,
ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
return new EnhancedWebClientReporter(properties, context, consumerAPI, circuitBreakAPI);
}
@Bean @Bean
public ExchangeFilterFunction exchangeFilterFunction( public SmartInitializingSingleton addEncodeTransferMetadataFilterForWebClient(EnhancedWebClientReporter reporter) {
RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { return () -> webClientBuilder.forEach(webClient -> {
return new EnhancedWebClientReporter(properties, context, consumerAPI); webClient.filter(reporter);
});
} }
} }
@ -179,10 +203,12 @@ public class RpcEnhancementAutoConfiguration {
} }
@Bean @Bean
@ConditionalOnClass(name = {"org.springframework.cloud.gateway.config.HttpClientCustomizer"}) @ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.GlobalFilter")
public HttpClientCustomizer httpClientCustomizer( public PolarisGatewayReporter polarisGatewayReporter(RpcEnhancementReporterProperties properties,
RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { SDKContext context,
return new EnhancedPolarisHttpClientCustomizer(properties, context, consumerAPI); ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
return new PolarisGatewayReporter(properties, context, consumerAPI, circuitBreakAPI);
} }
} }

@ -18,6 +18,7 @@
package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; 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.EnhancedFeignPlugin;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType; import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType;
import com.tencent.polaris.api.core.ConsumerAPI; 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.RetStatus;
import com.tencent.polaris.api.rpc.ServiceCallResult; import com.tencent.polaris.api.rpc.ServiceCallResult;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.client.api.SDKContext;
import feign.Request; import feign.Request;
import feign.Response; import feign.Response;
@ -49,16 +52,16 @@ public class ExceptionPolarisReporter extends AbstractPolarisReporterAdapter imp
private final ConsumerAPI consumerAPI; private final ConsumerAPI consumerAPI;
private final SDKContext context; private final CircuitBreakAPI circuitBreakAPI;
public ExceptionPolarisReporter(RpcEnhancementReporterProperties reporterProperties, public ExceptionPolarisReporter(RpcEnhancementReporterProperties reporterProperties,
SDKContext context, SDKContext context,
ConsumerAPI consumerAPI) { ConsumerAPI consumerAPI,
super(reporterProperties); CircuitBreakAPI circuitBreakAPI) {
super(reporterProperties, context);
this.reporterProperties = reporterProperties; this.reporterProperties = reporterProperties;
this.context = context;
this.consumerAPI = consumerAPI; this.consumerAPI = consumerAPI;
this.circuitBreakAPI = circuitBreakAPI;
} }
@Override @Override
@ -77,26 +80,46 @@ public class ExceptionPolarisReporter extends AbstractPolarisReporterAdapter imp
return; return;
} }
if (consumerAPI != null) { Request request = context.getRequest();
Request request = context.getRequest(); Response response = context.getResponse();
Response response = context.getResponse(); Exception exception = context.getException();
Exception exception = context.getException(); long delay = context.getDelay();
RetStatus retStatus = RetStatus.RetFail;
long delay = context.getDelay(); HttpHeaders requestHeaders = new HttpHeaders();
if (exception instanceof SocketTimeoutException) { request.headers().forEach((s, strings) -> requestHeaders.addAll(s, new ArrayList<>(strings)));
retStatus = RetStatus.RetTimeout; HttpHeaders responseHeaders = new HttpHeaders();
} Integer status = null;
LOG.debug("Will report result of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", retStatus.name(), request.httpMethod() if (response != null) {
.name(), request.url(), response.status(), delay); response.headers().forEach((s, strings) -> responseHeaders.addAll(s, new ArrayList<>(strings)));
ServiceCallResult resultRequest = ReporterUtils.createServiceCallResult(this.context, request, response, status = response.status();
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);
} }
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 @Override

@ -1,100 +1,140 @@
/* ///*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available. // * 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. // * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* // *
* Licensed under the BSD 3-Clause License (the "License"); // * Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* https://opensource.org/licenses/BSD-3-Clause // * https://opensource.org/licenses/BSD-3-Clause
* // *
* Unless required by applicable law or agreed to in writing, software distributed // * 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 // * 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 // * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. // * specific language governing permissions and limitations under the License.
*/ // */
//
package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; //package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
//
import java.io.UnsupportedEncodingException; //import java.io.UnsupportedEncodingException;
import java.net.URI; //import java.net.URI;
import java.net.URLDecoder; //import java.net.URLDecoder;
import java.util.Collection; //import java.util.Collection;
import java.util.Objects; //import java.util.Objects;
import java.util.function.Consumer; //import java.util.function.Consumer;
//
import com.tencent.cloud.common.constant.RouterConstant; //import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext; //import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.util.RequestLabelUtils; //import com.tencent.cloud.common.util.RequestLabelUtils;
import com.tencent.polaris.api.pojo.RetStatus; //import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat;
import com.tencent.polaris.api.pojo.ServiceKey; //import com.tencent.polaris.api.plugin.circuitbreaker.entity.InstanceResource;
import com.tencent.polaris.api.rpc.ServiceCallResult; //import com.tencent.polaris.api.plugin.circuitbreaker.entity.Resource;
import com.tencent.polaris.api.utils.CollectionUtils; //import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.client.api.SDKContext; //import com.tencent.polaris.api.pojo.ServiceKey;
import feign.Request; //import com.tencent.polaris.api.rpc.ServiceCallResult;
import feign.RequestTemplate; //import com.tencent.polaris.api.utils.CollectionUtils;
import feign.Response; //import com.tencent.polaris.client.api.SDKContext;
import org.apache.commons.lang.StringUtils; //import feign.Request;
import org.slf4j.Logger; //import feign.RequestTemplate;
import org.slf4j.LoggerFactory; //import feign.Response;
//import org.apache.commons.lang.StringUtils;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; //import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
/** //
* Util for polaris reporter. //import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
* //
* @author Haotian Zhang ///**
*/ // * Util for polaris reporter.
public final class ReporterUtils { // *
// * @author Haotian Zhang
private static final Logger LOGGER = LoggerFactory.getLogger(ReporterUtils.class); // */
//public final class ReporterUtils {
private ReporterUtils() { //
} // private static final Logger LOGGER = LoggerFactory.getLogger(ReporterUtils.class);
//
public static ServiceCallResult createServiceCallResult(final SDKContext context, final Request request, // private ReporterUtils() {
final Response response, long delay, RetStatus retStatus, final Consumer<ServiceCallResult> consumer) { // }
ServiceCallResult resultRequest = new ServiceCallResult(); //
// public static ResourceStat createInstanceResourceStat(final Request request,
resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE); // final Response response, long delay, RetStatus retStatus) {
RequestTemplate requestTemplate = request.requestTemplate(); // return createInstanceResourceStat(request.requestTemplate().feignTarget().name(), URI.create(request.url()), response.status(), delay, retStatus);
String serviceName = requestTemplate.feignTarget().name(); // }
resultRequest.setService(serviceName); //
Collection<String> labels = requestTemplate.headers().get(RouterConstant.ROUTER_LABEL_HEADER); // public static ResourceStat createInstanceResourceStat(final URI uri,
if (CollectionUtils.isNotEmpty(labels) && labels.iterator().hasNext()) { // final int status, long delay, RetStatus retStatus) {
String label = labels.iterator().next(); // return createInstanceResourceStat(null, uri, status, delay, retStatus);
try { // }
label = URLDecoder.decode(label, UTF_8); //
} // private static ResourceStat createInstanceResourceStat(final String serviceName,
catch (UnsupportedEncodingException e) { // final URI uri, final int status, long delay, RetStatus retStatus) {
LOGGER.error("unsupported charset exception " + UTF_8, e); // ServiceKey calleeServiceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, serviceName);
} // ServiceKey callerServiceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE);
resultRequest.setLabels(RequestLabelUtils.convertLabel(label)); // Resource resource = new InstanceResource(calleeServiceKey, uri.getHost(), getRequestPort(uri), callerServiceKey, getRequestProtocol(uri));
} // return new ResourceStat(resource, status, delay, retStatus);
URI uri = URI.create(request.url()); // }
resultRequest.setMethod(uri.getPath()); //
resultRequest.setRetCode(response.status()); // public static ServiceCallResult createServiceCallResult(final SDKContext context, final URI uri,
resultRequest.setRetStatus(retStatus); // final int status, long delay, RetStatus retStatus, final Consumer<ServiceCallResult> consumer) {
resultRequest.setDelay(delay); //
String scheme = uri.getScheme(); // }
if (StringUtils.isBlank(scheme)) { //
scheme = "http"; //
} // public static ServiceCallResult createServiceCallResult(final SDKContext context, final Request request,
resultRequest.setProtocol(scheme); // final Response response, long delay, RetStatus retStatus, final Consumer<ServiceCallResult> consumer) {
String sourceNamespace = MetadataContext.LOCAL_NAMESPACE; // ServiceCallResult resultRequest = new ServiceCallResult();
String sourceService = MetadataContext.LOCAL_SERVICE; //
if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) { // resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE);
resultRequest.setCallerService(new ServiceKey(sourceNamespace, sourceService)); // RequestTemplate requestTemplate = request.requestTemplate();
} // String serviceName = requestTemplate.feignTarget().name();
if (Objects.nonNull(context)) { // resultRequest.setService(serviceName);
resultRequest.setCallerIp(context.getConfig().getGlobal().getAPI().getBindIP()); // Collection<String> labels = requestTemplate.headers().get(RouterConstant.ROUTER_LABEL_HEADER);
} // if (CollectionUtils.isNotEmpty(labels) && labels.iterator().hasNext()) {
resultRequest.setHost(uri.getHost()); // String label = labels.iterator().next();
// -1 means access directly by url, and use http default port number 80 // try {
resultRequest.setPort(uri.getPort() == -1 ? 80 : uri.getPort()); // label = URLDecoder.decode(label, UTF_8);
consumer.accept(resultRequest); // }
return resultRequest; // 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;
// }
//}

@ -17,6 +17,7 @@
package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter; package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; 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.EnhancedFeignPlugin;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType; import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType;
import com.tencent.polaris.api.core.ConsumerAPI; 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.pojo.RetStatus;
import com.tencent.polaris.api.rpc.ServiceCallResult; import com.tencent.polaris.api.rpc.ServiceCallResult;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.client.api.SDKContext;
import feign.Request; import feign.Request;
import feign.Response; import feign.Response;
@ -48,12 +52,15 @@ public class SuccessPolarisReporter extends AbstractPolarisReporterAdapter imple
private final ConsumerAPI consumerAPI; private final ConsumerAPI consumerAPI;
private final SDKContext context; private final CircuitBreakAPI circuitBreakAPI;
public SuccessPolarisReporter(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { public SuccessPolarisReporter(RpcEnhancementReporterProperties properties,
super(properties); SDKContext context,
this.context = context; ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
super(properties, context);
this.consumerAPI = consumerAPI; this.consumerAPI = consumerAPI;
this.circuitBreakAPI = circuitBreakAPI;
} }
@Override @Override
@ -72,25 +79,44 @@ public class SuccessPolarisReporter extends AbstractPolarisReporterAdapter imple
return; return;
} }
if (consumerAPI != null) { Request request = context.getRequest();
Request request = context.getRequest(); Response response = context.getResponse();
Response response = context.getResponse(); long delay = context.getDelay();
RetStatus retStatus = RetStatus.RetSuccess; HttpHeaders requestHeaders = new HttpHeaders();
long delay = context.getDelay(); request.headers().forEach((s, strings) -> requestHeaders.addAll(s, new ArrayList<>(strings)));
if (apply(HttpStatus.resolve(response.status()))) { HttpHeaders responseHeaders = new HttpHeaders();
retStatus = RetStatus.RetFail; Integer status = null;
} if (response != null) {
LOG.debug("Will report result of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.", retStatus.name(), request.httpMethod() response.headers().forEach((s, strings) -> responseHeaders.addAll(s, new ArrayList<>(strings)));
.name(), request.url(), response.status(), delay); status = response.status();
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);
} }
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 @Override

@ -1,237 +1,240 @@
/* ///*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available. // * 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. // * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* // *
* Licensed under the BSD 3-Clause License (the "License"); // * Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* https://opensource.org/licenses/BSD-3-Clause // * https://opensource.org/licenses/BSD-3-Clause
* // *
* Unless required by applicable law or agreed to in writing, software distributed // * 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 // * 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 // * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. // * specific language governing permissions and limitations under the License.
*/ // */
//
package com.tencent.cloud.rpc.enhancement.resttemplate; //package com.tencent.cloud.rpc.enhancement.resttemplate;
//
import java.io.IOException; //import java.io.IOException;
import java.io.UnsupportedEncodingException; //import java.io.UnsupportedEncodingException;
import java.net.URI; //import java.net.URI;
import java.net.URLDecoder; //import java.net.URLDecoder;
import java.util.List; //import java.util.List;
import java.util.Map; //import java.util.Map;
import java.util.Objects; //import java.util.Objects;
//
import com.tencent.cloud.common.constant.RouterConstant; //import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext; //import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder; //import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.RequestLabelUtils; //import com.tencent.cloud.common.util.RequestLabelUtils;
import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; //import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; //import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI; //import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.ReporterUtils;
import com.tencent.polaris.api.pojo.RetStatus; //import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.ServiceKey; //import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat;
import com.tencent.polaris.api.rpc.ServiceCallResult; //import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.utils.CollectionUtils; //import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.client.api.SDKContext; //import com.tencent.polaris.api.rpc.ServiceCallResult;
import org.apache.commons.lang.StringUtils; //import com.tencent.polaris.api.utils.CollectionUtils;
import org.slf4j.Logger; //import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import org.slf4j.LoggerFactory; //import com.tencent.polaris.client.api.SDKContext;
//import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException; //import org.slf4j.Logger;
import org.springframework.context.ApplicationContext; //import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContextAware; //
import org.springframework.http.HttpMethod; //import org.springframework.beans.BeansException;
import org.springframework.http.HttpStatus; //import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatusCode; //import org.springframework.context.ApplicationContextAware;
import org.springframework.http.client.ClientHttpResponse; //import org.springframework.http.HttpMethod;
import org.springframework.lang.NonNull; //import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler; //import org.springframework.lang.NonNull;
import org.springframework.web.client.ResponseErrorHandler; //import org.springframework.web.client.DefaultResponseErrorHandler;
//import org.springframework.web.client.ResponseErrorHandler;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; //
//import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
/** //
* Extend ResponseErrorHandler to get request information. ///**
* // * Extend ResponseErrorHandler to get request information.
* @author wh 2022/6/21 // *
*/ // * @author wh 2022/6/21
public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter implements ResponseErrorHandler, ApplicationContextAware { // */
//public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter implements ResponseErrorHandler, ApplicationContextAware {
/** //
* Polaris-CircuitBreaker-Fallback header flag. // /**
*/ // * Polaris-CircuitBreaker-Fallback header flag.
public static final String POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER = "X-SCT-Polaris-CircuitBreaker-Fallback"; // */
/** // 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. // /**
*/ // * 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); // public static final String HEADER_HAS_ERROR = "X-SCT-Has-Error";
private final ConsumerAPI consumerAPI; // private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedRestTemplateReporter.class);
private final SDKContext context; // private final ConsumerAPI consumerAPI;
private ResponseErrorHandler delegateHandler; // private final SDKContext context;
// private final CircuitBreakAPI circuitBreakAPI;
public EnhancedRestTemplateReporter(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { // private ResponseErrorHandler delegateHandler;
super(properties); //
this.context = context; // public EnhancedRestTemplateReporter(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI, CircuitBreakAPI circuitBreakAPI) {
this.consumerAPI = consumerAPI; // super(properties);
} // this.context = context;
// this.consumerAPI = consumerAPI;
@Override // this.circuitBreakAPI = circuitBreakAPI;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // }
String[] handlerBeanNames = applicationContext.getBeanNamesForType(ResponseErrorHandler.class); //
if (handlerBeanNames.length == 1) { // @Override
if (this.delegateHandler == null) { // public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.delegateHandler = new DefaultResponseErrorHandler(); // String[] handlerBeanNames = applicationContext.getBeanNamesForType(ResponseErrorHandler.class);
} // if (handlerBeanNames.length == 1) {
return; // if (this.delegateHandler == null) {
} // this.delegateHandler = new DefaultResponseErrorHandler();
// }
// inject user custom ResponseErrorHandler // return;
for (String beanName : handlerBeanNames) { // }
// ignore self //
if (StringUtils.equalsIgnoreCase("enhancedRestTemplateReporter", beanName)) { // // inject user custom ResponseErrorHandler
continue; // for (String beanName : handlerBeanNames) {
} // // ignore self
this.delegateHandler = (ResponseErrorHandler) applicationContext.getBean(beanName); // 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 // @Override
boolean hasError = delegateHandler.hasError(response); // public boolean hasError(@NonNull ClientHttpResponse response) throws IOException {
response.getHeaders().add(HEADER_HAS_ERROR, String.valueOf(hasError)); // if (delegateHandler != null) {
} // // Preserve the delegated handler result
return true; // boolean hasError = delegateHandler.hasError(response);
} // response.getHeaders().add(HEADER_HAS_ERROR, String.valueOf(hasError));
// }
@Override // return true;
public void handleError(@NonNull ClientHttpResponse response) throws IOException { // }
if (realHasError(response)) { //
delegateHandler.handleError(response); // @Override
} // public void handleError(@NonNull ClientHttpResponse response) throws IOException {
// if (realHasError(response)) {
clear(response); // delegateHandler.handleError(response);
} // }
//
@Override // clear(response);
public void handleError(@NonNull URI url, @NonNull HttpMethod method, @NonNull ClientHttpResponse response) throws IOException { // }
// report result to polaris //
if (reportProperties.isEnabled()) { // @Override
reportResult(url, response); // public void handleError(@NonNull URI url, @NonNull HttpMethod method, @NonNull ClientHttpResponse response) throws IOException {
} // // report result to polaris
// if (reportProperties.isEnabled()) {
// invoke delegate handler // reportResult(url, response);
invokeDelegateHandler(url, method, response); // }
} //
// // invoke delegate handler
private void reportResult(URI url, ClientHttpResponse response) { // invokeDelegateHandler(url, method, response);
if (Boolean.parseBoolean(response.getHeaders().getFirst(POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER))) { // }
return; //
} // private void reportResult(URI url, ClientHttpResponse response) {
try { // if (Boolean.parseBoolean(response.getHeaders().getFirst(POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER))) {
ServiceCallResult resultRequest = createServiceCallResult(url, response); // return;
Map<String, String> loadBalancerContext = MetadataContextHolder.get().getLoadbalancerMetadata(); // }
// try {
String targetHost = loadBalancerContext.get("host"); // Map<String, String> loadBalancerContext = MetadataContextHolder.get().getLoadbalancerMetadata();
String targetPort = loadBalancerContext.get("port"); // String targetHost = loadBalancerContext.get("host");
String startMillis = loadBalancerContext.get("startMillis"); // String targetPort = loadBalancerContext.get("port");
long delay = System.currentTimeMillis() - Long.parseLong(startMillis); // 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); // if (StringUtils.isBlank(targetHost) || StringUtils.isBlank(targetPort)) {
return; // 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)); // ServiceCallResult resultRequest = createServiceCallResult(url, response);
resultRequest.setDelay(delay); //
// resultRequest.setHost(targetHost);
// checking response http status code // resultRequest.setPort(Integer.parseInt(targetPort));
HttpStatusCode httpStatusCode = response.getStatusCode(); // resultRequest.setDelay(delay);
HttpStatus httpStatus = HttpStatus.valueOf(httpStatusCode.value()); //
if (apply(httpStatus)) { // // checking response http status code
resultRequest.setRetStatus(RetStatus.RetFail); // if (apply(response.getStatusCode())) {
} // resultRequest.setRetStatus(RetStatus.RetFail);
resultRequest.setRetStatus(getRetStatusFromRequest(response.getHeaders(), resultRequest.getRetStatus())); // }
resultRequest.setRuleName(getActiveRuleNameFromRequest(response.getHeaders())); // resultRequest.setRetStatus(getRetStatusFromRequest(response.getHeaders(), resultRequest.getRetStatus()));
if (Objects.nonNull(context)) { // resultRequest.setRuleName(getActiveRuleNameFromRequest(response.getHeaders()));
resultRequest.setCallerIp(context.getConfig().getGlobal().getAPI().getBindIP()); // if (Objects.nonNull(context)) {
} // resultRequest.setCallerIp(context.getConfig().getGlobal().getAPI().getBindIP());
List<String> labels = response.getHeaders().get(RouterConstant.ROUTER_LABEL_HEADER); // }
if (CollectionUtils.isNotEmpty(labels)) { // List<String> labels = response.getHeaders().get(RouterConstant.ROUTER_LABEL_HEADER);
String label = labels.get(0); // if (CollectionUtils.isNotEmpty(labels)) {
try { // String label = labels.get(0);
label = URLDecoder.decode(label, UTF_8); // try {
} // label = URLDecoder.decode(label, UTF_8);
catch (UnsupportedEncodingException e) { // }
LOGGER.error("unsupported charset exception " + UTF_8, e); // catch (UnsupportedEncodingException e) {
} // LOGGER.error("unsupported charset exception " + UTF_8, e);
resultRequest.setLabels(RequestLabelUtils.convertLabel(label)); // }
} // resultRequest.setLabels(RequestLabelUtils.convertLabel(label));
// }
// processing report with consumerAPI . //
LOGGER.debug("Will report result of {}. Request=[{}]. Response=[{}]. Delay=[{}]ms.", resultRequest.getRetStatus() // // processing report with consumerAPI .
.name(), url, httpStatusCode.value(), delay); // LOGGER.debug("Will report result of {}. Request=[{}]. Response=[{}]. Delay=[{}]ms.", resultRequest.getRetStatus()
consumerAPI.updateServiceCallResult(resultRequest); // .name(), url, response.getStatusCode().value(), delay);
} // consumerAPI.updateServiceCallResult(resultRequest);
catch (Exception e) { // ResourceStat resourceStat = ReporterUtils.createInstanceResourceStat(url, response.getRawStatusCode(), delay, resultRequest.getRetStatus());
LOGGER.error("RestTemplate response reporter execute failed of {} url {}", response, url, e); // 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); //
} // private void invokeDelegateHandler(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
// if (realHasError(response)) {
clear(response); // delegateHandler.handleError(url, method, response);
} // }
//
private Boolean realHasError(ClientHttpResponse response) { // clear(response);
if (delegateHandler == null) { // }
return false; //
} // private Boolean realHasError(ClientHttpResponse response) {
// if (delegateHandler == null) {
String hasErrorHeader = response.getHeaders().getFirst(HEADER_HAS_ERROR); // return false;
if (StringUtils.isBlank(hasErrorHeader)) { // }
return false; //
} // String hasErrorHeader = response.getHeaders().getFirst(HEADER_HAS_ERROR);
// if (StringUtils.isBlank(hasErrorHeader)) {
return Boolean.parseBoolean(hasErrorHeader); // return false;
} // }
//
private void clear(ClientHttpResponse response) { // return Boolean.parseBoolean(hasErrorHeader);
response.getHeaders().remove(HEADER_HAS_ERROR); // }
response.getHeaders().remove(POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER); //
} // private void clear(ClientHttpResponse response) {
// response.getHeaders().remove(HEADER_HAS_ERROR);
private ServiceCallResult createServiceCallResult(URI uri, ClientHttpResponse response) throws IOException { // response.getHeaders().remove(POLARIS_CIRCUIT_BREAKER_FALLBACK_HEADER);
ServiceCallResult resultRequest = new ServiceCallResult(); // }
String serviceName = uri.getHost(); //
resultRequest.setService(serviceName); // private ServiceCallResult createServiceCallResult(URI uri, ClientHttpResponse response) throws IOException {
resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE); // ServiceCallResult resultRequest = new ServiceCallResult();
resultRequest.setMethod(uri.getPath()); // String serviceName = uri.getHost();
resultRequest.setRetCode(response.getStatusCode().value()); // resultRequest.setService(serviceName);
resultRequest.setRetStatus(RetStatus.RetSuccess); // resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE);
String sourceNamespace = MetadataContext.LOCAL_NAMESPACE; // resultRequest.setMethod(uri.getPath());
String sourceService = MetadataContext.LOCAL_SERVICE; // resultRequest.setRetCode(response.getStatusCode().value());
if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) { // resultRequest.setRetStatus(RetStatus.RetSuccess);
resultRequest.setCallerService(new ServiceKey(sourceNamespace, sourceService)); // String sourceNamespace = MetadataContext.LOCAL_NAMESPACE;
} // String sourceService = MetadataContext.LOCAL_SERVICE;
return resultRequest; // if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) {
} // resultRequest.setCallerService(new ServiceKey(sourceNamespace, sourceService));
// }
protected ResponseErrorHandler getDelegateHandler() { // return resultRequest;
return this.delegateHandler; // }
} //
// protected ResponseErrorHandler getDelegateHandler() {
protected void setDelegateHandler(ResponseErrorHandler delegateHandler) { // return this.delegateHandler;
this.delegateHandler = delegateHandler; // }
} //
} // protected void setDelegateHandler(ResponseErrorHandler delegateHandler) {
// this.delegateHandler = delegateHandler;
// }
//}

@ -17,6 +17,7 @@
package com.tencent.cloud.rpc.enhancement.resttemplate; package com.tencent.cloud.rpc.enhancement.resttemplate;
import com.tencent.cloud.common.constant.HeaderConstant;
import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.metadata.MetadataContextHolder;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
@ -36,9 +37,9 @@ public final class LoadBalancerClientAspectUtils {
Object server = joinPoint.getArgs()[0]; Object server = joinPoint.getArgs()[0];
if (server instanceof ServiceInstance) { if (server instanceof ServiceInstance) {
ServiceInstance instance = (ServiceInstance) server; ServiceInstance instance = (ServiceInstance) server;
MetadataContextHolder.get().setLoadbalancer("host", instance.getHost()); MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST, instance.getHost());
MetadataContextHolder.get().setLoadbalancer("port", String.valueOf(instance.getPort())); MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT, String.valueOf(instance.getPort()));
MetadataContextHolder.get().setLoadbalancer("startMillis", String.valueOf(System.currentTimeMillis())); MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALL_START_TIME, String.valueOf(System.currentTimeMillis()));
} }
} }
} }

@ -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<String, String> 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;
}
}

@ -1,173 +1,173 @@
/* ///*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available. // * 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. // * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* // *
* Licensed under the BSD 3-Clause License (the "License"); // * Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* https://opensource.org/licenses/BSD-3-Clause // * https://opensource.org/licenses/BSD-3-Clause
* // *
* Unless required by applicable law or agreed to in writing, software distributed // * 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 // * 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 // * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. // * specific language governing permissions and limitations under the License.
*/ // */
//
package com.tencent.cloud.rpc.enhancement.scg; //package com.tencent.cloud.rpc.enhancement.scg;
//
import java.net.SocketTimeoutException; //import java.net.SocketTimeoutException;
import java.util.Map; //import java.util.Map;
import java.util.Objects; //import java.util.Objects;
import java.util.function.BiConsumer; //import java.util.function.BiConsumer;
//
import com.tencent.cloud.common.constant.HeaderConstant; //import com.tencent.cloud.common.constant.HeaderConstant;
import com.tencent.cloud.common.metadata.MetadataContext; //import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder; //import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; //import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; //import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI; //import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.RetStatus; //import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.pojo.ServiceKey; //import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.rpc.ServiceCallResult; //import com.tencent.polaris.api.rpc.ServiceCallResult;
import com.tencent.polaris.client.api.SDKContext; //import com.tencent.polaris.client.api.SDKContext;
import io.netty.handler.codec.http.HttpHeaders; //import io.netty.handler.codec.http.HttpHeaders;
import org.apache.commons.lang.StringUtils; //import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger; //import org.slf4j.Logger;
import org.slf4j.LoggerFactory; //import org.slf4j.LoggerFactory;
import reactor.netty.http.client.HttpClient; //import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientConfig; //import reactor.netty.http.client.HttpClientConfig;
import reactor.netty.http.client.HttpClientResponse; //import reactor.netty.http.client.HttpClientResponse;
//
import org.springframework.http.HttpStatus; //import org.springframework.http.HttpStatus;
//
public class EnhancedPolarisHttpClient extends HttpClient { //public class EnhancedPolarisHttpClient extends HttpClient {
//
private static final Logger LOG = LoggerFactory.getLogger(EnhancedPolarisHttpClient.class); // private static final Logger LOG = LoggerFactory.getLogger(EnhancedPolarisHttpClient.class);
//
private final RpcEnhancementReporterProperties properties; // private final RpcEnhancementReporterProperties properties;
private final SDKContext context; // private final SDKContext context;
private final ConsumerAPI consumerAPI; // private final ConsumerAPI consumerAPI;
private final Reporter adapter; // private final Reporter adapter;
private final BiConsumer<? super HttpClientResponse, ? super Throwable> handler = new BiConsumer<HttpClientResponse, Throwable>() { // private final BiConsumer<? super HttpClientResponse, ? super Throwable> handler = new BiConsumer<HttpClientResponse, Throwable>() {
@Override // @Override
public void accept(HttpClientResponse httpClientResponse, Throwable throwable) { // public void accept(HttpClientResponse httpClientResponse, Throwable throwable) {
if (Objects.isNull(consumerAPI)) { // if (Objects.isNull(consumerAPI)) {
return; // return;
} // }
HttpHeaders responseHeaders = httpClientResponse.responseHeaders(); // HttpHeaders responseHeaders = httpClientResponse.responseHeaders();
//
ServiceCallResult result = new ServiceCallResult(); // ServiceCallResult result = new ServiceCallResult();
result.setCallerService(new ServiceKey(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE)); // result.setCallerService(new ServiceKey(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE));
result.setNamespace(MetadataContext.LOCAL_NAMESPACE); // result.setNamespace(MetadataContext.LOCAL_NAMESPACE);
//
Map<String, String> metadata = MetadataContextHolder.get().getLoadbalancerMetadata(); // Map<String, String> metadata = MetadataContextHolder.get().getLoadbalancerMetadata();
result.setDelay(System.currentTimeMillis() - Long.parseLong(metadata.get("startTime"))); // result.setDelay(System.currentTimeMillis() - Long.parseLong(metadata.get("startTime")));
result.setService(metadata.get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID)); // result.setService(metadata.get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID));
result.setHost(metadata.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST)); // result.setHost(metadata.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST));
result.setPort(Integer.parseInt(metadata.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT))); // result.setPort(Integer.parseInt(metadata.get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT)));
RetStatus status = RetStatus.RetSuccess; // RetStatus status = RetStatus.RetSuccess;
if (Objects.isNull(throwable)) { // if (Objects.isNull(throwable)) {
if (EnhancedPolarisHttpClient.this.adapter.apply(HttpStatus.valueOf(httpClientResponse.status() // if (EnhancedPolarisHttpClient.this.adapter.apply(HttpStatus.valueOf(httpClientResponse.status()
.code()))) { // .code()))) {
status = RetStatus.RetFail; // status = RetStatus.RetFail;
} // }
org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders(); // org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
responseHeaders.forEach(entry -> headers.add(entry.getKey(), entry.getValue())); // responseHeaders.forEach(entry -> headers.add(entry.getKey(), entry.getValue()));
status = adapter.getRetStatusFromRequest(headers, status); // status = adapter.getRetStatusFromRequest(headers, status);
result.setRuleName(adapter.getActiveRuleNameFromRequest(headers)); // result.setRuleName(adapter.getActiveRuleNameFromRequest(headers));
} // }
else { // else {
if (throwable instanceof SocketTimeoutException) { // if (throwable instanceof SocketTimeoutException) {
status = RetStatus.RetTimeout; // status = RetStatus.RetTimeout;
} // }
} // }
result.setMethod(httpClientResponse.uri()); // result.setMethod(httpClientResponse.uri());
result.setRetCode(httpClientResponse.status().code()); // result.setRetCode(httpClientResponse.status().code());
result.setRetStatus(status); // result.setRetStatus(status);
if (Objects.nonNull(context)) { // if (Objects.nonNull(context)) {
result.setCallerIp(context.getConfig().getGlobal().getAPI().getBindIP()); // result.setCallerIp(context.getConfig().getGlobal().getAPI().getBindIP());
} // }
try { // try {
consumerAPI.updateServiceCallResult(result); // consumerAPI.updateServiceCallResult(result);
} // }
catch (Throwable ex) { // catch (Throwable ex) {
LOG.error("update service call result fail", ex); // LOG.error("update service call result fail", ex);
} // }
} // }
}; // };
private HttpClient target; // private HttpClient target;
//
public EnhancedPolarisHttpClient( // public EnhancedPolarisHttpClient(
HttpClient client, // HttpClient client,
RpcEnhancementReporterProperties properties, // RpcEnhancementReporterProperties properties,
SDKContext context, // SDKContext context,
ConsumerAPI consumerAPI) { // ConsumerAPI consumerAPI) {
this.properties = properties; // this.properties = properties;
this.context = context; // this.context = context;
this.consumerAPI = consumerAPI; // this.consumerAPI = consumerAPI;
this.target = client; // this.target = client;
this.adapter = new Reporter(properties); // this.adapter = new Reporter(properties);
this.registerReportHandler(); // this.registerReportHandler();
} // }
//
@Override // @Override
public HttpClientConfig configuration() { // public HttpClientConfig configuration() {
return target.configuration(); // return target.configuration();
} // }
//
@Override // @Override
protected HttpClient duplicate() { // protected HttpClient duplicate() {
return new EnhancedPolarisHttpClient(target, properties, context, consumerAPI); // return new EnhancedPolarisHttpClient(target, properties, context, consumerAPI);
} // }
//
private void registerReportHandler() { // private void registerReportHandler() {
target = target.doOnRequest((request, connection) -> { // target = target.doOnRequest((request, connection) -> {
String serviceId = request.requestHeaders().get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID); // String serviceId = request.requestHeaders().get(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID);
String host = request.requestHeaders().get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST); // String host = request.requestHeaders().get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST);
String port = request.requestHeaders().get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT); // String port = request.requestHeaders().get(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT);
if (StringUtils.isNotBlank(serviceId)) { // if (StringUtils.isNotBlank(serviceId)) {
MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID, 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_HOST, host);
MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT, port); // MetadataContextHolder.get().setLoadbalancer(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT, port);
MetadataContextHolder.get().setLoadbalancer("startTime", System.currentTimeMillis() + ""); // MetadataContextHolder.get().setLoadbalancer("startTime", System.currentTimeMillis() + "");
} // }
//
request.requestHeaders().remove(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID); // request.requestHeaders().remove(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID);
request.requestHeaders().remove(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST); // request.requestHeaders().remove(HeaderConstant.INTERNAL_CALLEE_INSTANCE_HOST);
request.requestHeaders().remove(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT); // request.requestHeaders().remove(HeaderConstant.INTERNAL_CALLEE_INSTANCE_PORT);
}); // });
target = target.doOnResponse((httpClientResponse, connection) -> handler.accept(httpClientResponse, null)); // target = target.doOnResponse((httpClientResponse, connection) -> handler.accept(httpClientResponse, null));
target = target.doOnResponseError(handler); // target = target.doOnResponseError(handler);
} // }
//
//
private static class Reporter extends AbstractPolarisReporterAdapter { // private static class Reporter extends AbstractPolarisReporterAdapter {
//
/** // /**
* Constructor With {@link RpcEnhancementReporterProperties} . // * Constructor With {@link RpcEnhancementReporterProperties} .
* // *
* @param reportProperties instance of {@link RpcEnhancementReporterProperties}. // * @param reportProperties instance of {@link RpcEnhancementReporterProperties}.
*/ // */
protected Reporter(RpcEnhancementReporterProperties reportProperties) { // protected Reporter(RpcEnhancementReporterProperties reportProperties) {
super(reportProperties); // super(reportProperties);
} // }
//
@Override // @Override
public boolean apply(HttpStatus httpStatus) { // public boolean apply(HttpStatus httpStatus) {
return super.apply(httpStatus); // return super.apply(httpStatus);
} // }
//
@Override // @Override
public RetStatus getRetStatusFromRequest(org.springframework.http.HttpHeaders headers, RetStatus defaultVal) { // public RetStatus getRetStatusFromRequest(org.springframework.http.HttpHeaders headers, RetStatus defaultVal) {
return super.getRetStatusFromRequest(headers, defaultVal); // return super.getRetStatusFromRequest(headers, defaultVal);
} // }
//
@Override // @Override
public String getActiveRuleNameFromRequest(org.springframework.http.HttpHeaders headers) { // public String getActiveRuleNameFromRequest(org.springframework.http.HttpHeaders headers) {
return super.getActiveRuleNameFromRequest(headers); // return super.getActiveRuleNameFromRequest(headers);
} // }
} // }
//
} //}

@ -1,43 +1,43 @@
/* ///*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available. // * 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. // * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* // *
* Licensed under the BSD 3-Clause License (the "License"); // * Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* https://opensource.org/licenses/BSD-3-Clause // * https://opensource.org/licenses/BSD-3-Clause
* // *
* Unless required by applicable law or agreed to in writing, software distributed // * 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 // * 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 // * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. // * specific language governing permissions and limitations under the License.
*/ // */
//
package com.tencent.cloud.rpc.enhancement.scg; //package com.tencent.cloud.rpc.enhancement.scg;
//
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; //import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI; //import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.client.api.SDKContext; //import com.tencent.polaris.client.api.SDKContext;
import reactor.netty.http.client.HttpClient; //import reactor.netty.http.client.HttpClient;
//
import org.springframework.cloud.gateway.config.HttpClientCustomizer; //import org.springframework.cloud.gateway.config.HttpClientCustomizer;
//
public class EnhancedPolarisHttpClientCustomizer implements HttpClientCustomizer { //public class EnhancedPolarisHttpClientCustomizer implements HttpClientCustomizer {
//
private final RpcEnhancementReporterProperties properties; // private final RpcEnhancementReporterProperties properties;
private final SDKContext context; // private final SDKContext context;
private final ConsumerAPI consumerAPI; // private final ConsumerAPI consumerAPI;
//
public EnhancedPolarisHttpClientCustomizer(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) { // public EnhancedPolarisHttpClientCustomizer(RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) {
this.properties = properties; // this.properties = properties;
this.context = context; // this.context = context;
this.consumerAPI = consumerAPI; // this.consumerAPI = consumerAPI;
} // }
//
@Override // @Override
public HttpClient customize(HttpClient httpClient) { // public HttpClient customize(HttpClient httpClient) {
return new EnhancedPolarisHttpClient(httpClient, properties, context, consumerAPI); // return new EnhancedPolarisHttpClient(httpClient, properties, context, consumerAPI);
} // }
} //}

@ -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<Void> 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<String, String> 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);
}
}

@ -22,19 +22,23 @@ import java.net.SocketTimeoutException;
import java.net.URI; import java.net.URI;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import com.tencent.cloud.common.constant.HeaderConstant; import com.tencent.cloud.common.constant.HeaderConstant;
import com.tencent.cloud.common.constant.RouterConstant; import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext; 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.common.util.RequestLabelUtils;
import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter; import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI; 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.RetStatus;
import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.rpc.ServiceCallResult; import com.tencent.polaris.api.rpc.ServiceCallResult;
import com.tencent.polaris.api.utils.CollectionUtils; import com.tencent.polaris.api.utils.CollectionUtils;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.client.api.SDKContext;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -44,7 +48,6 @@ import reactor.util.context.Context;
import reactor.util.context.ContextView; import reactor.util.context.ContextView;
import org.springframework.http.HttpHeaders; 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.ClientRequest;
import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction; 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; import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
public class EnhancedWebClientReporter extends AbstractPolarisReporterAdapter implements ExchangeFilterFunction { public class EnhancedWebClientReporter extends AbstractPolarisReporterAdapter implements ExchangeFilterFunction {
private static final Logger LOG = LoggerFactory.getLogger(EnhancedWebClientReporter.class);
protected static final String METRICS_WEBCLIENT_START_TIME = EnhancedWebClientReporter.class.getName()
+ ".START_TIME";
private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedWebClientReporter.class);
private final ConsumerAPI consumerAPI; private final ConsumerAPI consumerAPI;
private final SDKContext context; private final CircuitBreakAPI circuitBreakAPI;
public EnhancedWebClientReporter(RpcEnhancementReporterProperties reportProperties, SDKContext context, ConsumerAPI consumerAPI) { public EnhancedWebClientReporter(RpcEnhancementReporterProperties reportProperties,
super(reportProperties); SDKContext context,
this.context = context; ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
super(reportProperties, context);
this.consumerAPI = consumerAPI; this.consumerAPI = consumerAPI;
this.circuitBreakAPI = circuitBreakAPI;
} }
@Override @Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) { public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
return next.exchange(request).as((responseMono) -> instrumentResponse(request, responseMono)) if (!reportProperties.isEnabled()) {
.contextWrite(this::putStartTime); return next.exchange(request);
} }
Mono<ClientResponse> instrumentResponse(ClientRequest request, Mono<ClientResponse> responseMono) { MetadataContextHolder.get().setLoadbalancer(
return Mono.deferContextual((ctx) -> responseMono.doOnEach((signal) -> { HeaderConstant.INTERNAL_CALLEE_SERVICE_ID,
// report result to polaris request.headers().getFirst(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID)
if (!reportProperties.isEnabled()) { );
return; MetadataContextHolder.get().setLoadbalancer(
} HeaderConstant.INTERNAL_CALL_START_TIME,
ServiceCallResult callResult = new ServiceCallResult(); String.valueOf(System.currentTimeMillis())
Long startTime = getStartTime(ctx); );
callResult.setDelay(System.currentTimeMillis() - startTime); return next.exchange(request)
.doOnSuccess(response -> instrumentResponse(request, response, null))
callResult.setNamespace(MetadataContext.LOCAL_NAMESPACE); .doOnError(t -> instrumentResponse(request, null, t));
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<String> 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);
} }
private Context putStartTime(Context context) { private void instrumentResponse(ClientRequest request, ClientResponse response, Throwable t) {
return context.put(METRICS_WEBCLIENT_START_TIME, System.currentTimeMillis()); Map<String, String> 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);
} }
} }

@ -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.EnhancedFeignPluginRunner;
import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.ExceptionPolarisReporter; 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.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 com.tencent.polaris.api.core.ConsumerAPI;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -60,10 +60,8 @@ public class RpcEnhancementAutoConfigurationTest {
assertThat(context).hasSingleBean(EnhancedFeignBeanPostProcessor.class); assertThat(context).hasSingleBean(EnhancedFeignBeanPostProcessor.class);
assertThat(context).hasSingleBean(SuccessPolarisReporter.class); assertThat(context).hasSingleBean(SuccessPolarisReporter.class);
assertThat(context).hasSingleBean(ExceptionPolarisReporter.class); assertThat(context).hasSingleBean(ExceptionPolarisReporter.class);
assertThat(context).hasSingleBean(EnhancedRestTemplateReporter.class); assertThat(context).hasSingleBean(PolarisRestTemplateReporter.class);
assertThat(context).hasSingleBean(RestTemplate.class); assertThat(context).hasSingleBean(RestTemplate.class);
RestTemplate restTemplate = context.getBean(RestTemplate.class);
assertThat(restTemplate.getErrorHandler() instanceof EnhancedRestTemplateReporter).isTrue();
}); });
} }

@ -1,230 +1,231 @@
/* ///*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available. // * 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. // * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* // *
* Licensed under the BSD 3-Clause License (the "License"); // * Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* https://opensource.org/licenses/BSD-3-Clause // * https://opensource.org/licenses/BSD-3-Clause
* // *
* Unless required by applicable law or agreed to in writing, software distributed // * 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 // * 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 // * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. // * specific language governing permissions and limitations under the License.
*/ // */
//
package com.tencent.cloud.rpc.enhancement.resttemplate; //package com.tencent.cloud.rpc.enhancement.resttemplate;
//
import java.io.IOException; //import java.io.IOException;
import java.io.InputStream; //import java.io.InputStream;
import java.net.URI; //import java.net.URI;
import java.util.HashMap; //import java.util.HashMap;
import java.util.Map; //import java.util.Map;
//
import com.tencent.cloud.common.metadata.MetadataContext; //import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder; //import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils; //import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; //import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI; //import com.tencent.polaris.api.core.ConsumerAPI;
import org.checkerframework.checker.nullness.qual.NonNull; //import org.checkerframework.checker.nullness.qual.NonNull;
import org.junit.jupiter.api.AfterAll; //import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll; //import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; //import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; //import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; //import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks; //import org.mockito.InjectMocks;
import org.mockito.Mock; //import org.mockito.Mock;
import org.mockito.MockedStatic; //import org.mockito.MockedStatic;
import org.mockito.Mockito; //import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension; //import org.mockito.junit.jupiter.MockitoExtension;
//
import org.springframework.context.ApplicationContext; //import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpHeaders; //import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod; //import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; //import org.springframework.http.HttpStatus;
import org.springframework.http.client.AbstractClientHttpResponse; //import org.springframework.http.client.AbstractClientHttpResponse;
import org.springframework.http.client.ClientHttpResponse; //import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler; //import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.ResponseErrorHandler; //import org.springframework.web.client.ResponseErrorHandler;
//
import static org.assertj.core.api.Assertions.assertThat; //import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; //import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; //import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock; //import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; //import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; //import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; //import static org.mockito.Mockito.when;
//
/** ///**
* Test for {@link EnhancedRestTemplateReporter}. // * Test for {@link EnhancedRestTemplateReporter}.
* @author lepdou 2022-09-06 // *
*/ // * @author lepdou 2022-09-06
@ExtendWith(MockitoExtension.class) // */
public class EnhancedRestTemplateReporterTest { //@ExtendWith(MockitoExtension.class)
//public class EnhancedRestTemplateReporterTest {
private static MockedStatic<MetadataContextHolder> mockedMetadataContextHolder; //
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils; // private static MockedStatic<MetadataContextHolder> mockedMetadataContextHolder;
@Mock // private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
private ConsumerAPI consumerAPI; // @Mock
@Mock // private ConsumerAPI consumerAPI;
private RpcEnhancementReporterProperties reporterProperties; // @Mock
@Mock // private RpcEnhancementReporterProperties reporterProperties;
private ResponseErrorHandler delegate; // @Mock
@InjectMocks // private ResponseErrorHandler delegate;
private EnhancedRestTemplateReporter enhancedRestTemplateReporter; // @InjectMocks
// private EnhancedRestTemplateReporter enhancedRestTemplateReporter;
@InjectMocks //
private EnhancedRestTemplateReporter enhancedRestTemplateReporter2; // @InjectMocks
// private EnhancedRestTemplateReporter enhancedRestTemplateReporter2;
@BeforeAll //
static void beforeAll() { // @BeforeAll
mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); // static void beforeAll() {
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) // mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
.thenReturn("caller"); // mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
MetadataContext metadataContext = Mockito.mock(MetadataContext.class); // .thenReturn("caller");
// MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
// mock transitive metadata //
Map<String, String> loadBalancerContext = new HashMap<>(); // // mock transitive metadata
loadBalancerContext.put("host", "1.1.1.1"); // Map<String, String> loadBalancerContext = new HashMap<>();
loadBalancerContext.put("port", "8080"); // loadBalancerContext.put("host", "1.1.1.1");
loadBalancerContext.put("startMillis", String.valueOf(System.currentTimeMillis())); // loadBalancerContext.put("port", "8080");
when(metadataContext.getLoadbalancerMetadata()).thenReturn(loadBalancerContext); // loadBalancerContext.put("startMillis", String.valueOf(System.currentTimeMillis()));
// when(metadataContext.getLoadbalancerMetadata()).thenReturn(loadBalancerContext);
mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class); //
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); // mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class);
} // mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
// }
@AfterAll //
static void afterAll() { // @AfterAll
mockedApplicationContextAwareUtils.close(); // static void afterAll() {
mockedMetadataContextHolder.close(); // mockedApplicationContextAwareUtils.close();
} // mockedMetadataContextHolder.close();
// }
@BeforeEach //
void setUp() { // @BeforeEach
enhancedRestTemplateReporter.setDelegateHandler(delegate); // void setUp() {
} // enhancedRestTemplateReporter.setDelegateHandler(delegate);
// }
@Test //
public void testSetApplicationContext() { // @Test
ApplicationContext applicationContext = mock(ApplicationContext.class); // public void testSetApplicationContext() {
// ApplicationContext applicationContext = mock(ApplicationContext.class);
// test no ResponseErrorHandler //
when(applicationContext.getBeanNamesForType(any(Class.class))) // // test no ResponseErrorHandler
.thenReturn(new String[] {"enhancedRestTemplateReporter"}); // when(applicationContext.getBeanNamesForType(any(Class.class)))
enhancedRestTemplateReporter2.setApplicationContext(applicationContext); // .thenReturn(new String[] {"enhancedRestTemplateReporter"});
assertThat(enhancedRestTemplateReporter2.getDelegateHandler()).isInstanceOf(DefaultResponseErrorHandler.class); // enhancedRestTemplateReporter2.setApplicationContext(applicationContext);
// assertThat(enhancedRestTemplateReporter2.getDelegateHandler()).isInstanceOf(DefaultResponseErrorHandler.class);
// test one other ResponseErrorHandler //
when(applicationContext.getBeanNamesForType(any(Class.class))) // // test one other ResponseErrorHandler
.thenReturn(new String[] {"enhancedRestTemplateReporter", "mockedResponseErrorHandler"}); // when(applicationContext.getBeanNamesForType(any(Class.class)))
when(applicationContext.getBean(anyString())).thenReturn(mock(MockedResponseErrorHandler.class)); // .thenReturn(new String[] {"enhancedRestTemplateReporter", "mockedResponseErrorHandler"});
enhancedRestTemplateReporter2.setApplicationContext(applicationContext); // when(applicationContext.getBean(anyString())).thenReturn(mock(MockedResponseErrorHandler.class));
assertThat(enhancedRestTemplateReporter2.getDelegateHandler()).isInstanceOf(MockedResponseErrorHandler.class); // enhancedRestTemplateReporter2.setApplicationContext(applicationContext);
} // assertThat(enhancedRestTemplateReporter2.getDelegateHandler()).isInstanceOf(MockedResponseErrorHandler.class);
// }
@Test //
public void testHasError() throws IOException { // @Test
when(delegate.hasError(any())).thenReturn(true); // public void testHasError() throws IOException {
// when(delegate.hasError(any())).thenReturn(true);
MockedClientHttpResponse response = new MockedClientHttpResponse(); //
assertThat(enhancedRestTemplateReporter.hasError(response)).isTrue(); // MockedClientHttpResponse response = new MockedClientHttpResponse();
// assertThat(enhancedRestTemplateReporter.hasError(response)).isTrue();
String realHasError = response.getHeaders().getFirst(EnhancedRestTemplateReporter.HEADER_HAS_ERROR); //
assertThat(realHasError).isEqualTo("true"); // String realHasError = response.getHeaders().getFirst(EnhancedRestTemplateReporter.HEADER_HAS_ERROR);
} // assertThat(realHasError).isEqualTo("true");
// }
@Test //
public void testHandleHasError() throws IOException { // @Test
when(reporterProperties.isEnabled()).thenReturn(true); // public void testHandleHasError() throws IOException {
when(delegate.hasError(any())).thenReturn(true); // when(reporterProperties.isEnabled()).thenReturn(true);
// when(delegate.hasError(any())).thenReturn(true);
MockedClientHttpResponse response = new MockedClientHttpResponse(); //
enhancedRestTemplateReporter.hasError(response); // MockedClientHttpResponse response = new MockedClientHttpResponse();
// enhancedRestTemplateReporter.hasError(response);
URI uri = mock(URI.class); //
enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, 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); // verify(consumerAPI, times(1)).updateServiceCallResult(any());
} // verify(delegate).handleError(uri, HttpMethod.GET, response);
// }
@Test //
public void testHandleHasNotError() throws IOException { // @Test
when(reporterProperties.isEnabled()).thenReturn(true); // public void testHandleHasNotError() throws IOException {
when(delegate.hasError(any())).thenReturn(false); // when(reporterProperties.isEnabled()).thenReturn(true);
// when(delegate.hasError(any())).thenReturn(false);
MockedClientHttpResponse response = new MockedClientHttpResponse(); //
enhancedRestTemplateReporter.hasError(response); // MockedClientHttpResponse response = new MockedClientHttpResponse();
// enhancedRestTemplateReporter.hasError(response);
URI uri = mock(URI.class); //
enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, 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); // verify(consumerAPI, times(1)).updateServiceCallResult(any());
} // verify(delegate, times(0)).handleError(uri, HttpMethod.GET, response);
// }
@Test //
public void testReportSwitchOff() throws IOException { // @Test
when(reporterProperties.isEnabled()).thenReturn(false); // public void testReportSwitchOff() throws IOException {
when(delegate.hasError(any())).thenReturn(true); // when(reporterProperties.isEnabled()).thenReturn(false);
// when(delegate.hasError(any())).thenReturn(true);
MockedClientHttpResponse response = new MockedClientHttpResponse(); //
enhancedRestTemplateReporter.hasError(response); // MockedClientHttpResponse response = new MockedClientHttpResponse();
// enhancedRestTemplateReporter.hasError(response);
URI uri = mock(URI.class); //
enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, 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); // verify(consumerAPI, times(0)).updateServiceCallResult(any());
} // verify(delegate).handleError(uri, HttpMethod.GET, response);
// }
static class MockedClientHttpResponse extends AbstractClientHttpResponse { //
// static class MockedClientHttpResponse extends AbstractClientHttpResponse {
private final HttpHeaders headers; //
// private final HttpHeaders headers;
MockedClientHttpResponse() { //
this.headers = new HttpHeaders(); // MockedClientHttpResponse() {
} // this.headers = new HttpHeaders();
// }
@Override //
public int getRawStatusCode() { // @Override
return 0; // public int getRawStatusCode() {
} // return 0;
// }
@Override //
public String getStatusText() { // @Override
return null; // public String getStatusText() {
} // return null;
// }
@Override //
public void close() { // @Override
// public void close() {
} //
// }
@Override //
public InputStream getBody() throws IOException { // @Override
return null; // public InputStream getBody() throws IOException {
} // return null;
// }
@Override //
public HttpHeaders getHeaders() { // @Override
return headers; // public HttpHeaders getHeaders() {
} // return headers;
// }
@Override //
public HttpStatus getStatusCode() throws IOException { // @Override
return HttpStatus.OK; // public HttpStatus getStatusCode() throws IOException {
} // return HttpStatus.OK;
} // }
// }
private static class MockedResponseErrorHandler extends DefaultResponseErrorHandler { //
// private static class MockedResponseErrorHandler extends DefaultResponseErrorHandler {
@Override //
public void handleError(@NonNull ClientHttpResponse response) { // @Override
} // public void handleError(@NonNull ClientHttpResponse response) {
// }
} //
} // }
//}

@ -1,49 +1,49 @@
/* ///*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available. // * 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. // * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* // *
* Licensed under the BSD 3-Clause License (the "License"); // * Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* https://opensource.org/licenses/BSD-3-Clause // * https://opensource.org/licenses/BSD-3-Clause
* // *
* Unless required by applicable law or agreed to in writing, software distributed // * 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 // * 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 // * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. // * specific language governing permissions and limitations under the License.
*/ // */
//
package com.tencent.cloud.rpc.enhancement.scg; //package com.tencent.cloud.rpc.enhancement.scg;
//
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties; //import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI; //import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.client.api.SDKContext; //import com.tencent.polaris.client.api.SDKContext;
import org.junit.jupiter.api.Assertions; //import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; //import org.junit.jupiter.api.Test;
import org.mockito.Mockito; //import org.mockito.Mockito;
import reactor.netty.http.client.HttpClient; //import reactor.netty.http.client.HttpClient;
//
public class EnhancedPolarisHttpClientCustomizerTest { //public class EnhancedPolarisHttpClientCustomizerTest {
//
@Test // @Test
public void testCustomize() { // public void testCustomize() {
RpcEnhancementReporterProperties properties = new RpcEnhancementReporterProperties(); // RpcEnhancementReporterProperties properties = new RpcEnhancementReporterProperties();
properties.setEnabled(true); // properties.setEnabled(true);
properties.getStatuses().clear(); // properties.getStatuses().clear();
properties.getSeries().clear(); // properties.getSeries().clear();
//
SDKContext context = Mockito.mock(SDKContext.class); // SDKContext context = Mockito.mock(SDKContext.class);
ConsumerAPI consumerAPI = Mockito.mock(ConsumerAPI.class); // ConsumerAPI consumerAPI = Mockito.mock(ConsumerAPI.class);
//
EnhancedPolarisHttpClientCustomizer clientCustomizer = new EnhancedPolarisHttpClientCustomizer(properties, context, consumerAPI); // EnhancedPolarisHttpClientCustomizer clientCustomizer = new EnhancedPolarisHttpClientCustomizer(properties, context, consumerAPI);
//
HttpClient client = HttpClient.create(); // HttpClient client = HttpClient.create();
HttpClient proxyClient = clientCustomizer.customize(client); // HttpClient proxyClient = clientCustomizer.customize(client);
//
Assertions.assertNotNull(proxyClient); // Assertions.assertNotNull(proxyClient);
Assertions.assertEquals(EnhancedPolarisHttpClient.class.getName(), proxyClient.getClass().getName()); // Assertions.assertEquals(EnhancedPolarisHttpClient.class.getName(), proxyClient.getClass().getName());
} // }
//
} //}

Loading…
Cancel
Save