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

@ -20,7 +20,6 @@ package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
import java.io.IOException;
import java.lang.reflect.Method;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter;
import com.tencent.polaris.api.pojo.CircuitBreakerStatus;
import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException;
@ -35,8 +34,6 @@ import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import static com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter.HEADER_HAS_ERROR;
/**
* PolarisCircuitBreakerRestTemplateInterceptor.
*
@ -70,15 +67,8 @@ public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpR
() -> {
try {
ClientHttpResponse response = execution.execute(request, body);
// pre handle response error
// EnhancedRestTemplateReporter always return true,
// so we need to check header set by EnhancedRestTemplateReporter
ResponseErrorHandler errorHandler = restTemplate.getErrorHandler();
boolean hasError = errorHandler.hasError(response);
if (errorHandler instanceof EnhancedRestTemplateReporter) {
hasError = Boolean.parseBoolean(response.getHeaders().getFirst(HEADER_HAS_ERROR));
}
if (hasError) {
if (errorHandler.hasError(response)) {
errorHandler.handleError(request.getURI(), request.getMethod(), response);
}
return response;

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

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

@ -47,6 +47,8 @@ public final class HeaderConstant {
*/
public static final String INTERNAL_CALLEE_INSTANCE_PORT = "internal-callee-instance-port";
public static final String INTERNAL_CALL_START_TIME = "internal-call-start-time";
private HeaderConstant() {
}
}

@ -32,6 +32,41 @@
</exclusion>
</exclusions>
</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 -->
<dependency>

@ -17,16 +17,34 @@
package com.tencent.cloud.rpc.enhancement;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import com.tencent.cloud.common.constant.HeaderConstant;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.util.RequestLabelUtils;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat;
import com.tencent.polaris.api.plugin.circuitbreaker.entity.InstanceResource;
import com.tencent.polaris.api.plugin.circuitbreaker.entity.Resource;
import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.rpc.ServiceCallResult;
import com.tencent.polaris.api.utils.CollectionUtils;
import com.tencent.polaris.client.api.SDKContext;
import feign.Request;
import feign.RequestTemplate;
import feign.Response;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,6 +52,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static org.springframework.http.HttpStatus.BAD_GATEWAY;
import static org.springframework.http.HttpStatus.BANDWIDTH_LIMIT_EXCEEDED;
import static org.springframework.http.HttpStatus.GATEWAY_TIMEOUT;
@ -58,17 +77,83 @@ public abstract class AbstractPolarisReporterAdapter {
private static final List<HttpStatus> HTTP_STATUSES = toList(NOT_IMPLEMENTED, BAD_GATEWAY,
SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, HTTP_VERSION_NOT_SUPPORTED, VARIANT_ALSO_NEGOTIATES,
INSUFFICIENT_STORAGE, LOOP_DETECTED, BANDWIDTH_LIMIT_EXCEEDED, NOT_EXTENDED, NETWORK_AUTHENTICATION_REQUIRED);
protected final RpcEnhancementReporterProperties reportProperties;
protected final SDKContext context;
/**
* Constructor With {@link RpcEnhancementReporterProperties} .
*
* @param reportProperties instance of {@link RpcEnhancementReporterProperties}.
*/
protected AbstractPolarisReporterAdapter(RpcEnhancementReporterProperties reportProperties) {
protected AbstractPolarisReporterAdapter(RpcEnhancementReporterProperties reportProperties, SDKContext context) {
this.reportProperties = reportProperties;
this.context = context;
}
/**
* createServiceCallResult
* @param calleeServiceName will pick up url host when null
* @param calleeHost will pick up url host when null
* @param calleePort will pick up url port when null
* @param uri request url
* @param requestHeaders request header
* @param responseHeaders response header
* @param statusCode response status
* @param delay delay
* @param exception exception
* @return ServiceCallResult
*/
public ServiceCallResult createServiceCallResult(
@Nullable String calleeServiceName, @Nullable String calleeHost, @Nullable Integer calleePort,
URI uri, HttpHeaders requestHeaders, @Nullable HttpHeaders responseHeaders,
@Nullable Integer statusCode, long delay, @Nullable Throwable exception) {
ServiceCallResult resultRequest = new ServiceCallResult();
resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE);
resultRequest.setService(StringUtils.isBlank(calleeServiceName) ? uri.getHost() : calleeServiceName);
resultRequest.setMethod(uri.getPath());
resultRequest.setRetCode(statusCode == null ? -1 : statusCode);
resultRequest.setDelay(delay);
resultRequest.setProtocol(getProtocol(uri));
resultRequest.setCallerService(new ServiceKey(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE));
resultRequest.setCallerIp(this.context.getConfig().getGlobal().getAPI().getBindIP());
resultRequest.setHost(StringUtils.isBlank(calleeHost) ? uri.getHost() : calleeHost);
resultRequest.setPort(calleePort == null ? getPort(uri) : calleePort);
resultRequest.setLabels(getLabels(requestHeaders));
resultRequest.setRetStatus(getRetStatusFromRequest(responseHeaders, getDefaultRetStatus(statusCode, exception)));
resultRequest.setRuleName(getActiveRuleNameFromRequest(responseHeaders));
return resultRequest;
}
/**
* createInstanceResourceStat
* @param calleeServiceName will pick up url host when null
* @param calleeHost will pick up url host when null
* @param calleePort will pick up url port when null
* @param uri request url
* @param statusCode response status
* @param delay delay
* @param exception exception
* @return ResourceStat
*/
public ResourceStat createInstanceResourceStat(
@Nullable String calleeServiceName, @Nullable String calleeHost, @Nullable Integer calleePort,
URI uri, @Nullable Integer statusCode, long delay, @Nullable Throwable exception) {
ServiceKey calleeServiceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, StringUtils.isBlank(calleeServiceName) ? uri.getHost() : calleeServiceName);
ServiceKey callerServiceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE);
Resource resource = new InstanceResource(
calleeServiceKey,
StringUtils.isBlank(calleeHost) ? uri.getHost() : calleeHost,
calleePort == null ? getPort(uri) : calleePort,
callerServiceKey,
getProtocol(uri)
);
return new ResourceStat(resource, statusCode == null ? -1 : statusCode, delay, getDefaultRetStatus(statusCode, exception));
}
/**
* Convert items to List.
*
@ -87,7 +172,7 @@ public abstract class AbstractPolarisReporterAdapter {
* @param httpStatus request http status code
* @return true , otherwise return false .
*/
protected boolean apply(@Nullable HttpStatus httpStatus) {
private boolean apply(@Nullable HttpStatus httpStatus) {
if (Objects.isNull(httpStatus)) {
return false;
}
@ -122,8 +207,8 @@ public abstract class AbstractPolarisReporterAdapter {
return false;
}
protected RetStatus getRetStatusFromRequest(HttpHeaders headers, RetStatus defaultVal) {
if (headers.containsKey(HeaderConstant.INTERNAL_CALLEE_RET_STATUS)) {
private RetStatus getRetStatusFromRequest(HttpHeaders headers, RetStatus defaultVal) {
if (headers != null && headers.containsKey(HeaderConstant.INTERNAL_CALLEE_RET_STATUS)) {
List<String> values = headers.get(HeaderConstant.INTERNAL_CALLEE_RET_STATUS);
if (CollectionUtils.isNotEmpty(values)) {
String retStatusVal = com.tencent.polaris.api.utils.StringUtils.defaultString(values.get(0));
@ -138,14 +223,56 @@ public abstract class AbstractPolarisReporterAdapter {
return defaultVal;
}
protected String getActiveRuleNameFromRequest(HttpHeaders headers) {
if (headers.containsKey(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME)) {
private String getActiveRuleNameFromRequest(HttpHeaders headers) {
if (headers != null && headers.containsKey(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME)) {
Collection<String> values = headers.get(HeaderConstant.INTERNAL_ACTIVE_RULE_NAME);
if (CollectionUtils.isNotEmpty(values)) {
String val = com.tencent.polaris.api.utils.StringUtils.defaultString(new ArrayList<>(values).get(0));
return val;
return com.tencent.polaris.api.utils.StringUtils.defaultString(new ArrayList<>(values).get(0));
}
}
return "";
}
private RetStatus getDefaultRetStatus(Integer statusCode, Throwable exception) {
RetStatus retStatus = RetStatus.RetSuccess;
if (exception != null) {
retStatus = RetStatus.RetFail;
if (exception instanceof SocketTimeoutException) {
retStatus = RetStatus.RetTimeout;
}
}else if (statusCode == null || apply(HttpStatus.resolve(statusCode))) {
retStatus = RetStatus.RetFail;
}
return retStatus;
}
private String getProtocol(URI uri) {
String scheme = uri.getScheme();
if (StringUtils.isBlank(scheme)) {
scheme = "http";
}
return scheme;
}
private int getPort(URI uri) {
// -1 means access directly by url, and use http default port number 80
return uri.getPort() == -1 ? 80 : uri.getPort();
}
private String getLabels(HttpHeaders headers) {
Collection<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.SuccessPolarisReporter;
import com.tencent.cloud.rpc.enhancement.resttemplate.BlockingLoadBalancerClientAspect;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter;
import com.tencent.cloud.rpc.enhancement.scg.EnhancedPolarisHttpClientCustomizer;
import com.tencent.cloud.rpc.enhancement.scg.PolarisGatewayReporter;
import com.tencent.cloud.rpc.enhancement.resttemplate.PolarisRestTemplateReporter;
import com.tencent.cloud.rpc.enhancement.scg.EnhancedPolarisHttpHeadersFilter;
import com.tencent.cloud.rpc.enhancement.webclient.EnhancedWebClientReporter;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory;
import com.tencent.polaris.client.api.SDKContext;
import org.springframework.beans.factory.SmartInitializingSingleton;
@ -46,7 +48,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.gateway.config.HttpClientCustomizer;
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -54,6 +55,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Role;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Auto Configuration for Polaris {@link feign.Feign} OR {@link RestTemplate} which can automatically bring in the call
@ -68,6 +70,12 @@ import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
@AutoConfigureAfter(PolarisContextAutoConfiguration.class)
public class RpcEnhancementAutoConfiguration {
@Bean
@ConditionalOnMissingBean(CircuitBreakAPI.class)
public CircuitBreakAPI circuitBreakAPI(SDKContext polarisContext) {
return CircuitBreakAPIFactory.createCircuitBreakAPIByContext(polarisContext);
}
/**
* Configuration for Polaris {@link feign.Feign} which can automatically bring in the call
* results for reporting.
@ -96,16 +104,18 @@ public class RpcEnhancementAutoConfiguration {
@Bean
public SuccessPolarisReporter successPolarisReporter(RpcEnhancementReporterProperties properties,
@Autowired(required = false) SDKContext context,
@Autowired(required = false) ConsumerAPI consumerAPI) {
return new SuccessPolarisReporter(properties, context, consumerAPI);
SDKContext context,
ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
return new SuccessPolarisReporter(properties, context, consumerAPI, circuitBreakAPI);
}
@Bean
public ExceptionPolarisReporter exceptionPolarisReporter(RpcEnhancementReporterProperties properties,
@Autowired(required = false) SDKContext context,
@Autowired(required = false) ConsumerAPI consumerAPI) {
return new ExceptionPolarisReporter(properties, context, consumerAPI);
SDKContext context,
ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
return new ExceptionPolarisReporter(properties, context, consumerAPI, circuitBreakAPI);
}
}
}
@ -125,16 +135,18 @@ public class RpcEnhancementAutoConfiguration {
private List<RestTemplate> restTemplates = Collections.emptyList();
@Bean
public EnhancedRestTemplateReporter enhancedRestTemplateReporter(
RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) {
return new EnhancedRestTemplateReporter(properties, context, consumerAPI);
public PolarisRestTemplateReporter polarisRestTemplateReporter(RpcEnhancementReporterProperties properties,
SDKContext context,
ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
return new PolarisRestTemplateReporter(properties, context, consumerAPI, circuitBreakAPI);
}
@Bean
public SmartInitializingSingleton setErrorHandlerForRestTemplate(EnhancedRestTemplateReporter reporter) {
public SmartInitializingSingleton setPolarisReporterForRestTemplate(PolarisRestTemplateReporter reporter) {
return () -> {
for (RestTemplate restTemplate : restTemplates) {
restTemplate.setErrorHandler(reporter);
restTemplate.getInterceptors().add(reporter);
}
};
}
@ -156,10 +168,22 @@ public class RpcEnhancementAutoConfiguration {
@ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient")
protected static class PolarisWebClientAutoConfiguration {
@Autowired(required = false)
private List<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
public ExchangeFilterFunction exchangeFilterFunction(
RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) {
return new EnhancedWebClientReporter(properties, context, consumerAPI);
public SmartInitializingSingleton addEncodeTransferMetadataFilterForWebClient(EnhancedWebClientReporter reporter) {
return () -> webClientBuilder.forEach(webClient -> {
webClient.filter(reporter);
});
}
}
@ -179,10 +203,12 @@ public class RpcEnhancementAutoConfiguration {
}
@Bean
@ConditionalOnClass(name = {"org.springframework.cloud.gateway.config.HttpClientCustomizer"})
public HttpClientCustomizer httpClientCustomizer(
RpcEnhancementReporterProperties properties, SDKContext context, ConsumerAPI consumerAPI) {
return new EnhancedPolarisHttpClientCustomizer(properties, context, consumerAPI);
@ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.GlobalFilter")
public PolarisGatewayReporter polarisGatewayReporter(RpcEnhancementReporterProperties properties,
SDKContext context,
ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
return new PolarisGatewayReporter(properties, context, consumerAPI, circuitBreakAPI);
}
}

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

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

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

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

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

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

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

@ -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.URLDecoder;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import com.tencent.cloud.common.constant.HeaderConstant;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.RequestLabelUtils;
import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat;
import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.rpc.ServiceCallResult;
import com.tencent.polaris.api.utils.CollectionUtils;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.client.api.SDKContext;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
@ -44,7 +48,6 @@ import reactor.util.context.Context;
import reactor.util.context.ContextView;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
@ -53,93 +56,76 @@ import org.springframework.web.reactive.function.client.ExchangeFunction;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
public class EnhancedWebClientReporter extends AbstractPolarisReporterAdapter implements ExchangeFilterFunction {
protected static final String METRICS_WEBCLIENT_START_TIME = EnhancedWebClientReporter.class.getName()
+ ".START_TIME";
private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedWebClientReporter.class);
private static final Logger LOG = LoggerFactory.getLogger(EnhancedWebClientReporter.class);
private final ConsumerAPI consumerAPI;
private final SDKContext context;
private final CircuitBreakAPI circuitBreakAPI;
public EnhancedWebClientReporter(RpcEnhancementReporterProperties reportProperties, SDKContext context, ConsumerAPI consumerAPI) {
super(reportProperties);
this.context = context;
public EnhancedWebClientReporter(RpcEnhancementReporterProperties reportProperties,
SDKContext context,
ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
super(reportProperties, context);
this.consumerAPI = consumerAPI;
this.circuitBreakAPI = circuitBreakAPI;
}
@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
return next.exchange(request).as((responseMono) -> instrumentResponse(request, responseMono))
.contextWrite(this::putStartTime);
}
Mono<ClientResponse> instrumentResponse(ClientRequest request, Mono<ClientResponse> responseMono) {
return Mono.deferContextual((ctx) -> responseMono.doOnEach((signal) -> {
// report result to polaris
if (!reportProperties.isEnabled()) {
return;
}
ServiceCallResult callResult = new ServiceCallResult();
Long startTime = getStartTime(ctx);
callResult.setDelay(System.currentTimeMillis() - startTime);
callResult.setNamespace(MetadataContext.LOCAL_NAMESPACE);
callResult.setService(request.headers().getFirst(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID));
String sourceNamespace = MetadataContext.LOCAL_NAMESPACE;
String sourceService = MetadataContext.LOCAL_SERVICE;
if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) {
callResult.setCallerService(new ServiceKey(sourceNamespace, sourceService));
}
Collection<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) {
return context.put(METRICS_WEBCLIENT_START_TIME, System.currentTimeMillis());
return next.exchange(request);
}
MetadataContextHolder.get().setLoadbalancer(
HeaderConstant.INTERNAL_CALLEE_SERVICE_ID,
request.headers().getFirst(HeaderConstant.INTERNAL_CALLEE_SERVICE_ID)
);
MetadataContextHolder.get().setLoadbalancer(
HeaderConstant.INTERNAL_CALL_START_TIME,
String.valueOf(System.currentTimeMillis())
);
return next.exchange(request)
.doOnSuccess(response -> instrumentResponse(request, response, null))
.doOnError(t -> instrumentResponse(request, null, t));
}
private void instrumentResponse(ClientRequest request, ClientResponse response, Throwable t) {
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.plugin.reporter.ExceptionPolarisReporter;
import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.SuccessPolarisReporter;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter;
import com.tencent.cloud.rpc.enhancement.resttemplate.PolarisRestTemplateReporter;
import com.tencent.polaris.api.core.ConsumerAPI;
import org.junit.jupiter.api.Test;
@ -60,10 +60,8 @@ public class RpcEnhancementAutoConfigurationTest {
assertThat(context).hasSingleBean(EnhancedFeignBeanPostProcessor.class);
assertThat(context).hasSingleBean(SuccessPolarisReporter.class);
assertThat(context).hasSingleBean(ExceptionPolarisReporter.class);
assertThat(context).hasSingleBean(EnhancedRestTemplateReporter.class);
assertThat(context).hasSingleBean(PolarisRestTemplateReporter.class);
assertThat(context).hasSingleBean(RestTemplate.class);
RestTemplate restTemplate = context.getBean(RestTemplate.class);
assertThat(restTemplate.getErrorHandler() instanceof EnhancedRestTemplateReporter).isTrue();
});
}

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

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

Loading…
Cancel
Save