feat:support TSF router. (#1403)

Co-authored-by: Haotian Zhang <skyebefreeman@qq.com>
pull/1404/head
Fishtail 3 months ago committed by GitHub
parent 775b66cecb
commit 4487317354
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -24,3 +24,4 @@
- [fix: memory cost too many when using wildcard feign calls](https://github.com/Tencent/spring-cloud-tencent/pull/1400)
- [feat:support consul config data.](https://github.com/Tencent/spring-cloud-tencent/pull/1401)
- [feat: support otel trace](https://github.com/Tencent/spring-cloud-tencent/pull/1402)
- [feat:support TSF router.](https://github.com/Tencent/spring-cloud-tencent/pull/1403)

@ -28,6 +28,7 @@ import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.UrlUtils;
import com.tencent.cloud.metadata.provider.ReactiveMetadataProvider;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.api.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
@ -39,8 +40,10 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
import static com.tencent.polaris.metadata.core.constant.MetadataConstants.LOCAL_IP;
/**
* Filter used for storing the metadata from upstream temporarily when web application is
@ -50,9 +53,8 @@ import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUST
*/
public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered {
private PolarisContextProperties polarisContextProperties;
private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataReactiveFilter.class);
private PolarisContextProperties polarisContextProperties;
public DecodeTransferMetadataReactiveFilter(PolarisContextProperties polarisContextProperties) {
this.polarisContextProperties = polarisContextProperties;
@ -67,16 +69,34 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
// Get metadata string from http header.
ServerHttpRequest serverHttpRequest = serverWebExchange.getRequest();
Map<String, String> internalTransitiveMetadata = getIntervalMetadata(serverHttpRequest, CUSTOM_METADATA);
Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(serverWebExchange);
// transitive metadata
// from specific header
Map<String, String> internalTransitiveMetadata = getInternalMetadata(serverHttpRequest, CUSTOM_METADATA);
// from header with specific prefix
Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(serverWebExchange);
Map<String, String> mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(internalTransitiveMetadata);
mergedTransitiveMetadata.putAll(customTransitiveMetadata);
Map<String, String> internalDisposableMetadata = getIntervalMetadata(serverHttpRequest, CUSTOM_DISPOSABLE_METADATA);
// disposable metadata
// from specific header
Map<String, String> internalDisposableMetadata = getInternalMetadata(serverHttpRequest, CUSTOM_DISPOSABLE_METADATA);
Map<String, String> mergedDisposableMetadata = new HashMap<>(internalDisposableMetadata);
ReactiveMetadataProvider metadataProvider = new ReactiveMetadataProvider(serverHttpRequest, polarisContextProperties.getLocalIpAddress());
MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, metadataProvider);
// application metadata
Map<String, String> internalApplicationMetadata = getInternalMetadata(serverHttpRequest, APPLICATION_METADATA);
Map<String, String> mergedApplicationMetadata = new HashMap<>(internalApplicationMetadata);
String callerIp = "";
if (StringUtils.isNotBlank(mergedApplicationMetadata.get(LOCAL_IP))) {
callerIp = mergedApplicationMetadata.get(LOCAL_IP);
}
// message metadata
ReactiveMetadataProvider callerMessageMetadataProvider = new ReactiveMetadataProvider(serverHttpRequest, callerIp);
MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, mergedApplicationMetadata, callerMessageMetadataProvider);
// Save to ServerWebExchange.
serverWebExchange.getAttributes().put(
MetadataConstant.HeaderName.METADATA_CONTEXT,
@ -90,7 +110,7 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
.doFinally((type) -> MetadataContextHolder.remove());
}
private Map<String, String> getIntervalMetadata(ServerHttpRequest serverHttpRequest, String headerName) {
private Map<String, String> getInternalMetadata(ServerHttpRequest serverHttpRequest, String headerName) {
HttpHeaders httpHeaders = serverHttpRequest.getHeaders();
String customMetadataStr = UrlUtils.decode(httpHeaders.getFirst(headerName));
LOG.debug("Get upstream metadata string: {}", customMetadataStr);

@ -33,6 +33,7 @@ import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.UrlUtils;
import com.tencent.cloud.metadata.provider.ServletMetadataProvider;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.api.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -40,8 +41,10 @@ import org.springframework.core.annotation.Order;
import org.springframework.lang.NonNull;
import org.springframework.web.filter.OncePerRequestFilter;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
import static com.tencent.polaris.metadata.core.constant.MetadataConstants.LOCAL_IP;
/**
* Filter used for storing the metadata from upstream temporarily when web application is
@ -64,16 +67,32 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
protected void doFilterInternal(@NonNull HttpServletRequest httpServletRequest,
@NonNull HttpServletResponse httpServletResponse, FilterChain filterChain)
throws ServletException, IOException {
// transitive metadata
// from specific header
Map<String, String> internalTransitiveMetadata = getInternalMetadata(httpServletRequest, CUSTOM_METADATA);
// from header with specific prefix
Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(httpServletRequest);
Map<String, String> mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(internalTransitiveMetadata);
mergedTransitiveMetadata.putAll(customTransitiveMetadata);
// disposable metadata
// from specific header
Map<String, String> internalDisposableMetadata = getInternalMetadata(httpServletRequest, CUSTOM_DISPOSABLE_METADATA);
Map<String, String> mergedDisposableMetadata = new HashMap<>(internalDisposableMetadata);
ServletMetadataProvider metadataProvider = new ServletMetadataProvider(httpServletRequest, polarisContextProperties.getLocalIpAddress());
MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, metadataProvider);
// application metadata
Map<String, String> internalApplicationMetadata = getInternalMetadata(httpServletRequest, APPLICATION_METADATA);
Map<String, String> mergedApplicationMetadata = new HashMap<>(internalApplicationMetadata);
String callerIp = "";
if (StringUtils.isNotBlank(mergedApplicationMetadata.get(LOCAL_IP))) {
callerIp = mergedApplicationMetadata.get(LOCAL_IP);
}
// message metadata
ServletMetadataProvider callerMessageMetadataProvider = new ServletMetadataProvider(httpServletRequest, callerIp);
MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, mergedApplicationMetadata, callerMessageMetadataProvider);
TransHeadersTransfer.transfer(httpServletRequest);

@ -39,6 +39,7 @@ import feign.Request;
import org.springframework.util.CollectionUtils;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
@ -64,6 +65,7 @@ public class EncodeTransferMedataFeignEnhancedPlugin implements EnhancedPlugin {
MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> applicationMetadata = metadataContext.getApplicationMetadata();
Map<String, String> transHeaders = metadataContext.getTransHeadersKV();
MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
@ -77,6 +79,9 @@ public class EncodeTransferMedataFeignEnhancedPlugin implements EnhancedPlugin {
// process custom metadata
this.buildMetadataHeader(request, customMetadata, CUSTOM_METADATA);
// add application metadata
this.buildMetadataHeader(request, applicationMetadata, APPLICATION_METADATA);
// set headers that need to be transmitted from the upstream
this.buildTransmittedHeader(request, transHeaders);
}

@ -35,6 +35,7 @@ import com.tencent.polaris.metadata.core.MetadataType;
import org.springframework.http.HttpRequest;
import org.springframework.util.CollectionUtils;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
@ -60,6 +61,7 @@ public class EncodeTransferMedataRestTemplateEnhancedPlugin implements EnhancedP
MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> applicationMetadata = metadataContext.getApplicationMetadata();
Map<String, String> transHeaders = metadataContext.getTransHeadersKV();
MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
Map<String, String> calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
@ -72,6 +74,9 @@ public class EncodeTransferMedataRestTemplateEnhancedPlugin implements EnhancedP
// build custom metadata request header
this.buildMetadataHeader(httpRequest, customMetadata, CUSTOM_METADATA);
// build application metadata request header
this.buildMetadataHeader(httpRequest, applicationMetadata, APPLICATION_METADATA);
// set headers that need to be transmitted from the upstream
this.buildTransmittedHeader(httpRequest, transHeaders);
}

@ -37,6 +37,7 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
@ -69,6 +70,7 @@ public class EncodeTransferMedataScgEnhancedPlugin implements EnhancedPlugin {
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> applicationMetadata = metadataContext.getApplicationMetadata();
MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
Map<String, String> calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
@ -77,6 +79,7 @@ public class EncodeTransferMedataScgEnhancedPlugin implements EnhancedPlugin {
this.buildMetadataHeader(builder, customMetadata, CUSTOM_METADATA);
this.buildMetadataHeader(builder, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
this.buildMetadataHeader(builder, applicationMetadata, APPLICATION_METADATA);
TransHeadersTransfer.transfer(exchange.getRequest());
context.setOriginRequest(exchange.mutate().request(builder.build()).build());

@ -35,6 +35,7 @@ import com.tencent.polaris.metadata.core.MetadataType;
import org.springframework.util.CollectionUtils;
import org.springframework.web.reactive.function.client.ClientRequest;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
@ -59,6 +60,7 @@ public class EncodeTransferMedataWebClientEnhancedPlugin implements EnhancedPlug
MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> applicationMetadata = metadataContext.getApplicationMetadata();
Map<String, String> transHeaders = metadataContext.getTransHeadersKV();
MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
Map<String, String> calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
@ -70,6 +72,7 @@ public class EncodeTransferMedataWebClientEnhancedPlugin implements EnhancedPlug
this.buildMetadataHeader(requestBuilder, customMetadata, CUSTOM_METADATA);
this.buildMetadataHeader(requestBuilder, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
this.buildMetadataHeader(requestBuilder, applicationMetadata, APPLICATION_METADATA);
this.buildTransmittedHeader(requestBuilder, transHeaders);
context.setOriginRequest(requestBuilder.build());

@ -0,0 +1,75 @@
/*
* 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.metadata.provider;
import java.net.URI;
import java.util.Collection;
import java.util.Map;
import com.tencent.cloud.common.util.UrlUtils;
import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils;
import com.tencent.polaris.metadata.core.MessageMetadataContainer;
import com.tencent.polaris.metadata.core.MetadataProvider;
import com.tencent.polaris.metadata.core.constant.MetadataConstants;
import com.tencent.polaris.metadata.core.manager.CalleeMetadataContainerGroup;
import feign.RequestTemplate;
/**
* MetadataProvider used for Feign RequestTemplate.
*
* @author Haotian Zhang
*/
public class FeignRequestTemplateMetadataProvider implements MetadataProvider {
private final RequestTemplate requestTemplate;
public FeignRequestTemplateMetadataProvider(RequestTemplate requestTemplate) {
this.requestTemplate = requestTemplate;
}
@Override
public String getRawMetadataStringValue(String key) {
switch (key) {
case MessageMetadataContainer.LABEL_KEY_METHOD:
return requestTemplate.method();
case MessageMetadataContainer.LABEL_KEY_PATH:
URI uri = URI.create(requestTemplate.request().url());
return UrlUtils.decode(uri.getPath());
case MessageMetadataContainer.LABEL_KEY_CALLER_IP:
return CalleeMetadataContainerGroup.getStaticApplicationMetadataContainer()
.getRawMetadataStringValue(MetadataConstants.LOCAL_IP);
default:
return null;
}
}
@Override
public String getRawMetadataMapValue(String key, String mapKey) {
Map<String, Collection<String>> headers = requestTemplate.headers();
switch (key) {
case MessageMetadataContainer.LABEL_MAP_KEY_HEADER:
return UrlUtils.decode(ExpressionLabelUtils.getFirstValue(headers, mapKey));
case MessageMetadataContainer.LABEL_MAP_KEY_COOKIE:
return UrlUtils.decode(ExpressionLabelUtils.getCookieFirstValue(headers, mapKey));
case MessageMetadataContainer.LABEL_MAP_KEY_QUERY:
return UrlUtils.decode(ExpressionLabelUtils.getFirstValue(requestTemplate.queries(), mapKey));
default:
return null;
}
}
}

@ -0,0 +1,70 @@
/*
* 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.metadata.provider;
import com.tencent.cloud.common.util.UrlUtils;
import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils;
import com.tencent.polaris.metadata.core.MessageMetadataContainer;
import com.tencent.polaris.metadata.core.MetadataProvider;
import com.tencent.polaris.metadata.core.constant.MetadataConstants;
import com.tencent.polaris.metadata.core.manager.CalleeMetadataContainerGroup;
import org.springframework.http.HttpRequest;
/**
* MetadataProvider used for RestTemplate HttpRequest.
*
* @author Haotian Zhang
*/
public class RestTemplateMetadataProvider implements MetadataProvider {
private final HttpRequest request;
public RestTemplateMetadataProvider(HttpRequest request) {
this.request = request;
}
@Override
public String getRawMetadataStringValue(String key) {
switch (key) {
case MessageMetadataContainer.LABEL_KEY_METHOD:
return request.getMethod().toString();
case MessageMetadataContainer.LABEL_KEY_PATH:
return UrlUtils.decode(request.getURI().getPath());
case MessageMetadataContainer.LABEL_KEY_CALLER_IP:
return CalleeMetadataContainerGroup.getStaticApplicationMetadataContainer()
.getRawMetadataStringValue(MetadataConstants.LOCAL_IP);
default:
return null;
}
}
@Override
public String getRawMetadataMapValue(String key, String mapKey) {
switch (key) {
case MessageMetadataContainer.LABEL_MAP_KEY_HEADER:
return UrlUtils.decode(SpringWebExpressionLabelUtils.getHeaderValue(request, mapKey));
case MessageMetadataContainer.LABEL_MAP_KEY_COOKIE:
return UrlUtils.decode(SpringWebExpressionLabelUtils.getCookieValue(request, mapKey));
case MessageMetadataContainer.LABEL_MAP_KEY_QUERY:
return UrlUtils.decode(SpringWebExpressionLabelUtils.getQueryValue(request, mapKey));
default:
return null;
}
}
}

@ -69,7 +69,7 @@ public class PolarisServiceDiscovery {
InstancesResponse filteredInstances = polarisDiscoveryHandler.getHealthyInstances(serviceId);
ServiceInstances serviceInstances = filteredInstances.toServiceInstances();
for (Instance instance : serviceInstances.getInstances()) {
instances.add(new PolarisServiceInstance(instance));
instances.add(new PolarisServiceInstance(instance, filteredInstances.getMetadata()));
}
return instances;
}

@ -48,8 +48,6 @@ import static com.tencent.cloud.polaris.extend.nacos.NacosContextProperties.DEFA
*/
public class PolarisRegistration implements Registration {
private static final String METADATA_KEY_IP = "internal-ip";
private static final String METADATA_KEY_ADDRESS = "internal-address";
private static final String GROUP_SERVER_ID_FORMAT = "%s__%s";
private static final String NACOS_CLUSTER = "nacos.cluster";
@ -116,10 +114,6 @@ public class PolarisRegistration implements Registration {
if (CollectionUtils.isEmpty(metadata)) {
Map<String, String> instanceMetadata = new HashMap<>();
// put internal metadata
instanceMetadata.put(METADATA_KEY_IP, host);
instanceMetadata.put(METADATA_KEY_ADDRESS, host + ":" + port);
// put internal-nacos-cluster if necessary
if (Objects.nonNull(nacosContextProperties)) {
String clusterName = nacosContextProperties.getClusterName();

@ -44,6 +44,9 @@ import com.tencent.polaris.api.rpc.InstanceRegisterResponse;
import com.tencent.polaris.api.rpc.InstancesResponse;
import com.tencent.polaris.client.util.NamedThreadFactory;
import com.tencent.polaris.factory.config.provider.ServiceConfigImpl;
import com.tencent.polaris.metadata.core.TransitiveType;
import com.tencent.polaris.metadata.core.constant.MetadataConstants;
import com.tencent.polaris.metadata.core.manager.CalleeMetadataContainerGroup;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -121,6 +124,14 @@ public class PolarisServiceRegistry implements ServiceRegistry<PolarisRegistrati
instanceRegisterRequest.setProtocol(polarisDiscoveryProperties.getProtocol());
instanceRegisterRequest.setVersion(polarisDiscoveryProperties.getVersion());
instanceRegisterRequest.setInstanceId(polarisDiscoveryProperties.getInstanceId());
CalleeMetadataContainerGroup.getStaticApplicationMetadataContainer()
.putMetadataStringValue(MetadataConstants.LOCAL_NAMESPACE, polarisDiscoveryProperties.getNamespace(), TransitiveType.DISPOSABLE);
CalleeMetadataContainerGroup.getStaticApplicationMetadataContainer()
.putMetadataStringValue(MetadataConstants.LOCAL_SERVICE, serviceId, TransitiveType.DISPOSABLE);
CalleeMetadataContainerGroup.getStaticApplicationMetadataContainer()
.putMetadataStringValue(MetadataConstants.LOCAL_IP, registration.getHost(), TransitiveType.DISPOSABLE);
CalleeMetadataContainerGroup.getStaticApplicationMetadataContainer()
.putMetadataStringValue(MetadataConstants.LOCAL_PORT, String.valueOf(registration.getPort()), TransitiveType.DISPOSABLE);
try {
ProviderAPI providerClient = polarisSDKContextManager.getProviderAPI();
InstanceRegisterResponse instanceRegisterResponse;

@ -26,6 +26,7 @@ import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.context.PolarisConfigModifier;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.context.tsf.TsfUtils;
import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties;
import com.tencent.cloud.polaris.context.tsf.consul.TsfConsulProperties;
import com.tencent.cloud.polaris.tsf.util.RegistrationUtil;
@ -84,10 +85,10 @@ public class TsfDiscoveryConfigModifier implements PolarisConfigModifier {
System.setProperty("spring.cloud.polaris.namespace", tsfCoreProperties.getTsfNamespaceId());
// application id
polarisDiscoveryProperties.setVersion(tsfDiscoveryProperties.getTsfProgVersion());
polarisDiscoveryProperties.setVersion(tsfCoreProperties.getTsfProgVersion());
// instance id
polarisDiscoveryProperties.setInstanceId(tsfDiscoveryProperties.getInstanceId());
polarisDiscoveryProperties.setInstanceId(tsfCoreProperties.getInstanceId());
boolean consulEnable = tsfCoreProperties.isTsfConsulEnable();
boolean polarisEnable = tsfCoreProperties.isTsePolarisEnable();
@ -133,11 +134,11 @@ public class TsfDiscoveryConfigModifier implements PolarisConfigModifier {
Map<String, String> metadata = serverConnectorConfig.getMetadata();
String appName = RegistrationUtil.getAppName(tsfDiscoveryProperties, context.getEnvironment());
metadata.put(ConsulConstant.MetadataMapKey.SERVICE_NAME_KEY, RegistrationUtil.normalizeForDns(appName));
metadata.put(ConsulConstant.MetadataMapKey.INSTANCE_ID_KEY, RegistrationUtil.getInstanceId(tsfDiscoveryProperties, context));
metadata.put(ConsulConstant.MetadataMapKey.INSTANCE_ID_KEY, RegistrationUtil.getInstanceId(tsfCoreProperties, context));
if (StringUtils.isNotBlank(tsfConsulProperties.getAclToken())) {
serverConnectorConfig.setToken(tsfConsulProperties.getAclToken());
}
metadata.put(ConsulConstant.MetadataMapKey.TAGS_KEY, JacksonUtils.serialize2Json(RegistrationUtil.createTags(tsfDiscoveryProperties)));
metadata.put(ConsulConstant.MetadataMapKey.TAGS_KEY, JacksonUtils.serialize2Json(TsfUtils.createTags(tsfCoreProperties)));
if (StringUtils.isNotBlank(tsfDiscoveryProperties.getDefaultQueryTag())) {
metadata.put(ConsulConstant.MetadataMapKey.QUERY_TAG_KEY, tsfDiscoveryProperties.getDefaultQueryTag());
}

@ -24,7 +24,6 @@ import java.util.Map;
import com.tencent.cloud.common.util.inet.PolarisInetUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.commons.util.InetUtils;
@ -43,11 +42,6 @@ public class TsfDiscoveryProperties {
private InetUtils.HostInfo hostInfo;
/**
* Tags to use when registering service.
*/
private List<String> tags = new ArrayList<>();
/**
* If service discovery enabled.
*/
@ -132,33 +126,6 @@ public class TsfDiscoveryProperties {
*/
private String serviceName;
/**
* Unique service instance id.
*/
@Value("${tsf_instance_id:${spring.cloud.consul.discovery.instanceId:${SPRING_CLOUD_CONSUL_DISCOVERY_INSTANCEID:}}}")
private String instanceId;
/**
* Service instance zone.
*/
private String instanceZone;
/**
* Service instance group.
*/
private String instanceGroup;
/**
* Service instance zone comes from metadata.
* This allows changing the metadata tag name.
*/
private String defaultZoneMetadataName = "zone";
/**
* Whether to register an http or https service.
*/
private String scheme = "http";
/**
* Suffix to use when registering management service.
*/
@ -214,30 +181,6 @@ public class TsfDiscoveryProperties {
*/
private Boolean healthCheckTlsSkipVerify;
/**
* tsf service consul registration tags.
*
* progVersion
*/
@Value("${tsf_prog_version:}")
private String tsfProgVersion;
/**
* tsf service consul registration tags.
*
*
*/
@Value("${tsf_region:}")
private String tsfRegion;
/**
* tsf service consul registration tags.
*
*
*/
@Value("${tsf_zone:}")
private String tsfZone;
/**
* 线.
*/
@ -247,7 +190,6 @@ public class TsfDiscoveryProperties {
private long callbackErrorDelay = 30 * 1000L;
/**
* consul 线0线provider线.
* provider线 consul .
@ -296,14 +238,6 @@ public class TsfDiscoveryProperties {
this.hostInfo = hostInfo;
}
public List<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
public boolean isEnabled() {
return enabled;
}
@ -433,46 +367,6 @@ public class TsfDiscoveryProperties {
this.serviceName = serviceName;
}
public String getInstanceId() {
return instanceId;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
public String getInstanceZone() {
return instanceZone;
}
public void setInstanceZone(String instanceZone) {
this.instanceZone = instanceZone;
}
public String getInstanceGroup() {
return instanceGroup;
}
public void setInstanceGroup(String instanceGroup) {
this.instanceGroup = instanceGroup;
}
public String getDefaultZoneMetadataName() {
return defaultZoneMetadataName;
}
public void setDefaultZoneMetadataName(String defaultZoneMetadataName) {
this.defaultZoneMetadataName = defaultZoneMetadataName;
}
public String getScheme() {
return scheme;
}
public void setScheme(String scheme) {
this.scheme = scheme;
}
public String getManagementSuffix() {
return managementSuffix;
}
@ -553,30 +447,6 @@ public class TsfDiscoveryProperties {
this.healthCheckTlsSkipVerify = healthCheckTlsSkipVerify;
}
public String getTsfProgVersion() {
return tsfProgVersion;
}
public void setTsfProgVersion(final String tsfProgVersion) {
this.tsfProgVersion = tsfProgVersion;
}
public String getTsfRegion() {
return tsfRegion;
}
public void setTsfRegion(final String tsfRegion) {
this.tsfRegion = tsfRegion;
}
public String getTsfZone() {
return tsfZone;
}
public void setTsfZone(final String tsfZone) {
this.tsfZone = tsfZone;
}
public Map<String, String> getServiceMeta() {
return serviceMeta;
}
@ -629,7 +499,6 @@ public class TsfDiscoveryProperties {
public String toString() {
return "ConsulDiscoveryProperties{" +
"hostInfo=" + hostInfo +
", tags=" + tags +
", enabled=" + enabled +
", managementTags=" + managementTags +
", healthCheckPath='" + healthCheckPath + '\'' +
@ -647,11 +516,6 @@ public class TsfDiscoveryProperties {
", catalogServicesWatchDelay=" + catalogServicesWatchDelay +
", catalogServicesWatchTimeout=" + catalogServicesWatchTimeout +
", serviceName='" + serviceName + '\'' +
", instanceId='" + instanceId + '\'' +
", instanceZone='" + instanceZone + '\'' +
", instanceGroup='" + instanceGroup + '\'' +
", defaultZoneMetadataName='" + defaultZoneMetadataName + '\'' +
", scheme='" + scheme + '\'' +
", managementSuffix='" + managementSuffix + '\'' +
", serverListQueryTags=" + serverListQueryTags +
", datacenters=" + datacenters +

@ -44,21 +44,14 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnTsfEnabled
@AutoConfigureBefore(PolarisServiceRegistryAutoConfiguration.class)
public class TsfDiscoveryRegistryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TsfMetadataPolarisRegistrationCustomizer tsfMetadataPolarisRegistrationCustomizer(
TsfCoreProperties tsfCoreProperties, TsfDiscoveryProperties tsfDiscoveryProperties) {
return new TsfMetadataPolarisRegistrationCustomizer(tsfCoreProperties, tsfDiscoveryProperties);
}
@Bean
@ConditionalOnMissingBean
public TsfPortPolarisRegistrationCustomizer tsfPortPolarisRegistrationCustomizer(
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
ApplicationContext context, TsfDiscoveryProperties tsfDiscoveryProperties,
ApplicationContext context, TsfDiscoveryProperties tsfDiscoveryProperties, TsfCoreProperties tsfCoreProperties,
TsfHeartbeatProperties tsfHeartbeatProperties, PolarisSDKContextManager polarisSDKContextManager) {
return new TsfPortPolarisRegistrationCustomizer(autoServiceRegistrationProperties, context,
tsfDiscoveryProperties, tsfHeartbeatProperties, polarisSDKContextManager.getSDKContext());
tsfDiscoveryProperties, tsfCoreProperties, tsfHeartbeatProperties, polarisSDKContextManager.getSDKContext());
}
@Bean

@ -1,74 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.tsf.registry;
import java.util.Map;
import com.tencent.cloud.common.constant.SdkVersion;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties;
import com.tencent.cloud.polaris.registry.PolarisRegistration;
import com.tencent.cloud.polaris.registry.PolarisRegistrationCustomizer;
import com.tencent.cloud.polaris.tsf.TsfDiscoveryProperties;
import com.tencent.cloud.polaris.tsf.consts.WarmupCons;
import com.tencent.cloud.polaris.tsf.util.RegistrationUtil;
import static com.tencent.cloud.common.tsf.TsfConstant.TSF_APPLICATION_ID;
import static com.tencent.cloud.common.tsf.TsfConstant.TSF_GROUP_ID;
import static com.tencent.cloud.common.tsf.TsfConstant.TSF_INSTNACE_ID;
import static com.tencent.cloud.common.tsf.TsfConstant.TSF_NAMESPACE_ID;
import static com.tencent.cloud.common.tsf.TsfConstant.TSF_PROG_VERSION;
import static com.tencent.cloud.common.tsf.TsfConstant.TSF_REGION;
import static com.tencent.cloud.common.tsf.TsfConstant.TSF_SDK_VERSION;
import static com.tencent.cloud.common.tsf.TsfConstant.TSF_TAGS;
import static com.tencent.cloud.common.tsf.TsfConstant.TSF_ZONE;
/**
*
*
* @author Haotian Zhang
*/
public class TsfMetadataPolarisRegistrationCustomizer implements PolarisRegistrationCustomizer {
private final TsfCoreProperties tsfCoreProperties;
private final TsfDiscoveryProperties tsfDiscoveryProperties;
public TsfMetadataPolarisRegistrationCustomizer(TsfCoreProperties tsfCoreProperties, TsfDiscoveryProperties tsfDiscoveryProperties) {
this.tsfCoreProperties = tsfCoreProperties;
this.tsfDiscoveryProperties = tsfDiscoveryProperties;
}
@Override
public void customize(PolarisRegistration registration) {
Map<String, String> metadata = registration.getMetadata();
metadata.put(TSF_APPLICATION_ID, tsfCoreProperties.getTsfApplicationId());
metadata.put(TSF_PROG_VERSION, tsfDiscoveryProperties.getTsfProgVersion());
metadata.put(TSF_GROUP_ID, tsfCoreProperties.getTsfGroupId());
metadata.put(TSF_NAMESPACE_ID, tsfCoreProperties.getTsfNamespaceId());
metadata.put(TSF_INSTNACE_ID, tsfDiscoveryProperties.getInstanceId());
metadata.put(TSF_REGION, tsfDiscoveryProperties.getTsfRegion());
metadata.put(TSF_ZONE, tsfDiscoveryProperties.getTsfZone());
// 处理预热相关的参数
metadata.put(WarmupCons.TSF_START_TIME, String.valueOf(System.currentTimeMillis()));
metadata.put(TSF_SDK_VERSION, SdkVersion.get());
metadata.put(TSF_TAGS, JacksonUtils.serialize2Json(RegistrationUtil.createTags(tsfDiscoveryProperties)));
RegistrationUtil.appendMetaIpAddress(metadata);
}
}

@ -17,6 +17,7 @@
package com.tencent.cloud.polaris.tsf.registry;
import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties;
import com.tencent.cloud.polaris.registry.PolarisRegistration;
import com.tencent.cloud.polaris.registry.PolarisRegistrationCustomizer;
import com.tencent.cloud.polaris.tsf.TsfDiscoveryProperties;
@ -37,15 +38,17 @@ public class TsfPortPolarisRegistrationCustomizer implements PolarisRegistration
private final AutoServiceRegistrationProperties autoServiceRegistrationProperties;
private final ApplicationContext context;
private final TsfDiscoveryProperties tsfDiscoveryProperties;
private final TsfCoreProperties tsfCoreProperties;
private final TsfHeartbeatProperties tsfHeartbeatProperties;
private final SDKContext sdkContext;
public TsfPortPolarisRegistrationCustomizer(AutoServiceRegistrationProperties autoServiceRegistrationProperties,
ApplicationContext context, TsfDiscoveryProperties tsfDiscoveryProperties,
ApplicationContext context, TsfDiscoveryProperties tsfDiscoveryProperties, TsfCoreProperties tsfCoreProperties,
TsfHeartbeatProperties tsfHeartbeatProperties, SDKContext sdkContext) {
this.autoServiceRegistrationProperties = autoServiceRegistrationProperties;
this.context = context;
this.tsfDiscoveryProperties = tsfDiscoveryProperties;
this.tsfCoreProperties = tsfCoreProperties;
this.tsfHeartbeatProperties = tsfHeartbeatProperties;
this.sdkContext = sdkContext;
}
@ -56,7 +59,7 @@ public class TsfPortPolarisRegistrationCustomizer implements PolarisRegistration
registration.setPort(tsfDiscoveryProperties.getPort());
}
// we know the port and can set the check
RegistrationUtil.setCheck(autoServiceRegistrationProperties, tsfDiscoveryProperties, context,
RegistrationUtil.setCheck(autoServiceRegistrationProperties, tsfDiscoveryProperties, tsfCoreProperties, context,
tsfHeartbeatProperties, registration, sdkContext.getConfig());
}
}

@ -17,14 +17,12 @@
package com.tencent.cloud.polaris.tsf.util;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.ecwid.consul.v1.agent.model.NewService;
import com.tencent.cloud.common.util.AddressUtils;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.inet.PolarisInetUtils;
import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties;
import com.tencent.cloud.polaris.tsf.TsfDiscoveryProperties;
import com.tencent.cloud.polaris.tsf.TsfHeartbeatProperties;
import com.tencent.polaris.api.config.Configuration;
@ -58,15 +56,6 @@ public final class RegistrationUtil {
*/
public static final String ID = "consul";
private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationUtil.class);
/**
* IPV4.
*/
public static String TSF_ADDRESS_IPV4 = "TSF_ADDRESS_IPV4";
/**
* IPV6.
*/
public static String TSF_ADDRESS_IPV6 = "TSF_ADDRESS_IPV6";
private RegistrationUtil() {
}
@ -79,7 +68,7 @@ public final class RegistrationUtil {
return env.getProperty("spring.application.name", "application");
}
public static String getInstanceId(TsfDiscoveryProperties properties, ApplicationContext context) {
public static String getInstanceId(TsfCoreProperties properties, ApplicationContext context) {
// tsf consul 不支持 dns所以这里不需要 normalize并且由于优雅下线readiness probe 联动都是依赖 service id 的normalize 后两边对不上,所以需要去掉 normalize
if (!StringUtils.hasText(properties.getInstanceId())) {
return IdUtils.getDefaultInstanceId(context.getEnvironment(), false);
@ -123,40 +112,8 @@ public final class RegistrationUtil {
return normalized.toString();
}
public static List<String> createTags(TsfDiscoveryProperties properties) {
List<String> tags = new LinkedList<>(properties.getTags());
if (StringUtils.hasText(properties.getInstanceZone())) {
tags.add(properties.getDefaultZoneMetadataName() + "=" + properties.getInstanceZone());
}
if (StringUtils.hasText(properties.getInstanceGroup())) {
tags.add("group=" + properties.getInstanceGroup());
}
//store the secure flag in the tags so that clients will be able to figure out whether to use http or https automatically
tags.add("secure=" + properties.getScheme().equalsIgnoreCase("https"));
return tags;
}
public static Map<String, String> appendMetaIpAddress(Map<String, String> meta) {
if (meta == null) {
return null;
}
String ipv4Address = PolarisInetUtils.getIpString(false);
if (ipv4Address != null) {
meta.put(TSF_ADDRESS_IPV4, ipv4Address);
}
String ipv6Address = PolarisInetUtils.getIpString(true);
if (ipv6Address != null) {
meta.put(TSF_ADDRESS_IPV6, ipv6Address);
}
return meta;
}
public static void setCheck(AutoServiceRegistrationProperties autoServiceRegistrationProperties,
TsfDiscoveryProperties properties, ApplicationContext context,
TsfDiscoveryProperties properties, TsfCoreProperties tsfCoreProperties, ApplicationContext context,
TsfHeartbeatProperties tsfHeartbeatProperties, Registration registration, Configuration configuration) {
if (properties.isRegisterHealthCheck()) {
Integer checkPort;
@ -171,7 +128,7 @@ public final class RegistrationUtil {
for (ServerConnectorConfigImpl config : configuration.getGlobal().getServerConnectors()) {
if (org.apache.commons.lang.StringUtils.equals(config.getId(), ID)) {
Map<String, String> metadata = config.getMetadata();
NewService.Check check = createCheck(checkPort, tsfHeartbeatProperties, properties);
NewService.Check check = createCheck(checkPort, tsfHeartbeatProperties, properties, tsfCoreProperties);
String checkJson = JacksonUtils.serialize2Json(check);
LOGGER.debug("Check is : {}", checkJson);
metadata.put(ConsulConstant.MetadataMapKey.CHECK_KEY, checkJson);
@ -182,7 +139,7 @@ public final class RegistrationUtil {
}
public static NewService.Check createCheck(Integer port, TsfHeartbeatProperties ttlConfig,
TsfDiscoveryProperties properties) {
TsfDiscoveryProperties properties, TsfCoreProperties tsfCoreProperties) {
NewService.Check check = new NewService.Check();
if (ttlConfig.isEnabled()) {
check.setTtl(ttlConfig.getTtl());
@ -196,7 +153,7 @@ public final class RegistrationUtil {
check.setHttp(properties.getHealthCheckUrl());
}
else {
check.setHttp(String.format("%s://%s:%s%s", properties.getScheme(),
check.setHttp(String.format("%s://%s:%s%s", tsfCoreProperties.getScheme(),
AddressUtils.getIpCompatible(properties.getHostname()), port,
properties.getHealthCheckPath()));
}

@ -181,7 +181,7 @@ public class PolarisRegistrationTest {
Map<String, String> metadata = polarisRegistration1.getMetadata();
assertThat(metadata).isNotNull();
assertThat(metadata).isNotEmpty();
assertThat(metadata.size()).isEqualTo(4);
assertThat(metadata.size()).isEqualTo(2);
assertThat(metadata.get("key1")).isEqualTo("value1");
}
@ -211,7 +211,7 @@ public class PolarisRegistrationTest {
Map<String, String> metadata = polarisRegistration1.getMetadata();
assertThat(metadata).isNotNull();
assertThat(metadata).isNotEmpty();
assertThat(metadata.size()).isEqualTo(4);
assertThat(metadata.size()).isEqualTo(2);
assertThat(metadata.get("nacos.cluster")).isEqualTo(clusterName);
}
}

@ -18,10 +18,8 @@
package com.tencent.cloud.polaris.router;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@ -79,25 +77,6 @@ public class PolarisRouterContext {
return routerLabels.get(labelKey);
}
public Set<String> getLabelAsSet(String labelKey) {
Map<String, String> routerLabels = labels.get(RouterConstant.ROUTER_LABELS);
if (CollectionUtils.isEmpty(routerLabels)) {
return Collections.emptySet();
}
for (Map.Entry<String, String> entry : routerLabels.entrySet()) {
if (StringUtils.equalsIgnoreCase(labelKey, entry.getKey())) {
String keysStr = entry.getValue();
if (StringUtils.isNotBlank(keysStr)) {
String[] keysArr = StringUtils.split(keysStr, ",");
return new HashSet<>(Arrays.asList(keysArr));
}
}
}
return Collections.emptySet();
}
public void putLabels(String labelType, Map<String, String> subLabels) {
if (this.labels == null) {
this.labels = new HashMap<>();

@ -32,7 +32,6 @@ import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.pojo.PolarisServiceInstance;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerRequest;
import com.tencent.cloud.polaris.router.spi.RouterRequestInterceptor;
import com.tencent.cloud.polaris.router.spi.RouterResponseInterceptor;
import com.tencent.cloud.rpc.enhancement.transformer.InstanceTransformer;
@ -100,14 +99,9 @@ public class PolarisRouterServiceInstanceListSupplier extends DelegatingServiceI
PolarisRouterContext routerContext = null;
DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();
if (requestContext != null) {
if (requestContext instanceof RequestDataContext) {
routerContext = buildRouterContext(((RequestDataContext) requestContext).getClientRequest().getHeaders());
}
else if (requestContext.getClientRequest() instanceof PolarisLoadBalancerRequest) {
routerContext = buildRouterContext(((PolarisLoadBalancerRequest<?>) requestContext.getClientRequest()).getRequest()
.getHeaders());
}
if (requestContext instanceof RequestDataContext) {
routerContext = buildRouterContext(((RequestDataContext) requestContext).getClientRequest()
.getHeaders());
}
if (routerContext == null) {
@ -120,15 +114,11 @@ public class PolarisRouterServiceInstanceListSupplier extends DelegatingServiceI
PolarisRouterContext buildRouterContext(HttpHeaders headers) {
Collection<String> labelHeaderValues = headers.get(RouterConstant.ROUTER_LABEL_HEADER);
if (CollectionUtils.isEmpty(labelHeaderValues)) {
return null;
labelHeaderValues = new ArrayList<>();
}
PolarisRouterContext routerContext = new PolarisRouterContext();
routerContext.putLabels(RouterConstant.TRANSITIVE_LABELS, MetadataContextHolder.get().getTransitiveMetadata());
Map<String, String> labelHeaderValuesMap = new HashMap<>();
try {
Optional<String> labelHeaderValuesOptional = labelHeaderValues.stream().findFirst();
@ -178,6 +168,7 @@ public class PolarisRouterServiceInstanceListSupplier extends DelegatingServiceI
serviceInfo.setNamespace(MetadataContext.LOCAL_NAMESPACE);
serviceInfo.setService(MetadataContext.LOCAL_SERVICE);
processRoutersRequest.setSourceService(serviceInfo);
processRoutersRequest.setMetadataContainerGroup(MetadataContextHolder.get().getMetadataContainerGroup(false));
return processRoutersRequest;
}

@ -1,76 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils;
import com.tencent.cloud.polaris.context.ServiceRuleManager;
import com.tencent.polaris.specification.api.v1.model.ModelProto;
import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto;
import org.springframework.util.CollectionUtils;
/**
* Resolve label expressions from routing rules.
*
* @author lepdou 2022-05-19
*/
public class RouterRuleLabelResolver {
private final ServiceRuleManager serviceRuleManager;
public RouterRuleLabelResolver(ServiceRuleManager serviceRuleManager) {
this.serviceRuleManager = serviceRuleManager;
}
public Set<String> getExpressionLabelKeys(String namespace, String sourceService, String dstService) {
List<RoutingProto.Route> rules = serviceRuleManager.getServiceRouterRule(namespace, sourceService, dstService);
if (CollectionUtils.isEmpty(rules)) {
return Collections.emptySet();
}
Set<String> expressionLabels = new HashSet<>();
for (RoutingProto.Route rule : rules) {
List<RoutingProto.Source> sources = rule.getSourcesList();
if (CollectionUtils.isEmpty(sources)) {
continue;
}
for (RoutingProto.Source source : sources) {
Map<String, ModelProto.MatchString> labels = source.getMetadataMap();
if (CollectionUtils.isEmpty(labels)) {
continue;
}
for (String labelKey : labels.keySet()) {
if (ExpressionLabelUtils.isExpressionLabel(labelKey)) {
expressionLabels.add(labelKey);
}
}
}
}
return expressionLabels;
}
}

@ -19,7 +19,9 @@ package com.tencent.cloud.polaris.router;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -64,12 +66,14 @@ public final class RouterUtils {
.collect(Collectors.toList()))).subscribe(instance -> instanceList.add((Instance) instance));
String serviceName = "";
Map<String, String> serviceMetadata = new HashMap<>();
if (!CollectionUtils.isEmpty(instanceList)) {
serviceName = instanceList.get(0).getService();
serviceMetadata = instanceList.get(0).getServiceMetadata();
}
ServiceKey serviceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, serviceName);
return new DefaultServiceInstances(serviceKey, instanceList);
return new DefaultServiceInstances(serviceKey, instanceList, serviceMetadata);
}
}

@ -1,55 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.beanprocessor;
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerInterceptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
/**
* Replace LoadBalancerInterceptor with PolarisLoadBalancerInterceptor.
* PolarisLoadBalancerInterceptor can pass routing context information.
*
*@author lepdou 2022-05-18
*/
public class LoadBalancerInterceptorBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private BeanFactory factory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.factory = beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof LoadBalancerInterceptor) {
LoadBalancerRequestFactory requestFactory = this.factory.getBean(LoadBalancerRequestFactory.class);
LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class);
return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
return bean;
}
}

@ -1,72 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.beanprocessor;
import java.util.List;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.BeanFactoryUtils;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.scg.PolarisReactiveLoadBalancerClientFilter;
import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
/**
* Replaced ReactiveLoadBalancerClientFilter with PolarisReactiveLoadBalancerClientFilter during creating bean phase.
*
* @author lepdou 2022-06-20
*/
public class ReactiveLoadBalancerClientFilterBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private BeanFactory factory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.factory = beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// Support spring cloud gateway router.
// Replaces the default ReactiveLoadBalancerClientFilter implementation
// and returns a custom PolarisReactiveLoadBalancerClientFilter
if (bean instanceof ReactiveLoadBalancerClientFilter) {
LoadBalancerClientFactory loadBalancerClientFactory = this.factory.getBean(LoadBalancerClientFactory.class);
GatewayLoadBalancerProperties gatewayLoadBalancerProperties = this.factory.getBean(GatewayLoadBalancerProperties.class);
LoadBalancerProperties loadBalancerProperties = this.factory.getBean(LoadBalancerProperties.class);
List<SpringWebRouterLabelResolver> routerLabelResolvers = BeanFactoryUtils.getBeans(factory, SpringWebRouterLabelResolver.class);
StaticMetadataManager staticMetadataManager = this.factory.getBean(StaticMetadataManager.class);
RouterRuleLabelResolver routerRuleLabelResolver = this.factory.getBean(RouterRuleLabelResolver.class);
PolarisContextProperties polarisContextProperties = this.factory.getBean(PolarisContextProperties.class);
return new PolarisReactiveLoadBalancerClientFilter(
loadBalancerClientFactory, gatewayLoadBalancerProperties, loadBalancerProperties,
staticMetadataManager, routerRuleLabelResolver, routerLabelResolvers, polarisContextProperties);
}
return bean;
}
}

@ -18,18 +18,11 @@
package com.tencent.cloud.polaris.router.config;
import java.util.List;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.feign.RouterLabelFeignInterceptor;
import com.tencent.cloud.polaris.router.spi.FeignRouterLabelResolver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
@Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisRouterEnabled
@ -37,10 +30,7 @@ import org.springframework.lang.Nullable;
public class FeignAutoConfiguration {
@Bean
public RouterLabelFeignInterceptor routerLabelInterceptor(@Nullable List<FeignRouterLabelResolver> routerLabelResolvers,
StaticMetadataManager staticMetadataManager,
RouterRuleLabelResolver routerRuleLabelResolver,
PolarisContextProperties polarisContextProperties) {
return new RouterLabelFeignInterceptor(routerLabelResolvers, staticMetadataManager, routerRuleLabelResolver, polarisContextProperties);
public RouterLabelFeignInterceptor routerLabelInterceptor() {
return new RouterLabelFeignInterceptor();
}
}

@ -22,12 +22,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.polaris.context.ServiceRuleManager;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerInterceptorBeanPostProcessor;
import com.tencent.cloud.polaris.router.beanprocessor.ReactiveLoadBalancerClientFilterBeanPostProcessor;
import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties;
@ -35,7 +29,7 @@ import com.tencent.cloud.polaris.router.interceptor.MetadataRouterRequestInterce
import com.tencent.cloud.polaris.router.interceptor.NearbyRouterRequestInterceptor;
import com.tencent.cloud.polaris.router.interceptor.RuleBasedRouterRequestInterceptor;
import com.tencent.cloud.polaris.router.resttemplate.RouterLabelRestTemplateInterceptor;
import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import com.tencent.cloud.polaris.router.scg.RouterLabelGlobalFilter;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateInterceptor;
import org.springframework.beans.factory.SmartInitializingSingleton;
@ -45,41 +39,19 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
/**
* configuration for router module singleton beans.
*
* @author lepdou 2022-05-11
*@author lepdou 2022-05-11
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisRouterEnabled
@LoadBalancerClients(defaultConfiguration = LoadBalancerConfiguration.class)
public class RouterAutoConfiguration {
@Bean
@Order(HIGHEST_PRECEDENCE)
@ConditionalOnClass(name = "org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor")
public LoadBalancerInterceptorBeanPostProcessor loadBalancerInterceptorBeanPostProcessor() {
return new LoadBalancerInterceptorBeanPostProcessor();
}
@Bean
@Order(HIGHEST_PRECEDENCE)
@ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter")
public ReactiveLoadBalancerClientFilterBeanPostProcessor loadBalancerClientFilterBeanPostProcessor() {
return new ReactiveLoadBalancerClientFilterBeanPostProcessor();
}
@Bean
public RouterRuleLabelResolver routerRuleLabelResolver(ServiceRuleManager serviceRuleManager) {
return new RouterRuleLabelResolver(serviceRuleManager);
}
@Bean
@ConditionalOnProperty(value = "spring.cloud.polaris.router.metadata-router.enabled", matchIfMissing = true)
public MetadataRouterRequestInterceptor metadataRouterRequestInterceptor(PolarisMetadataRouterProperties polarisMetadataRouterProperties) {
@ -98,6 +70,21 @@ public class RouterAutoConfiguration {
return new RuleBasedRouterRequestInterceptor(polarisRuleBasedRouterProperties);
}
/**
* Create when gateway application is SCG.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.GlobalFilter")
protected static class RouterLabelScgFilterConfig {
@Bean
public RouterLabelGlobalFilter routerLabelGlobalFilter() {
return new RouterLabelGlobalFilter();
}
}
/**
* Create when RestTemplate exists.
* @author liuye 2022-09-14
@ -111,13 +98,8 @@ public class RouterAutoConfiguration {
private List<RestTemplate> restTemplates = Collections.emptyList();
@Bean
public RouterLabelRestTemplateInterceptor routerLabelRestTemplateInterceptor(
List<SpringWebRouterLabelResolver> routerLabelResolvers,
StaticMetadataManager staticMetadataManager,
RouterRuleLabelResolver routerRuleLabelResolver,
PolarisContextProperties polarisContextProperties) {
return new RouterLabelRestTemplateInterceptor(routerLabelResolvers, staticMetadataManager,
routerRuleLabelResolver, polarisContextProperties);
public RouterLabelRestTemplateInterceptor routerLabelRestTemplateInterceptor() {
return new RouterLabelRestTemplateInterceptor();
}
@Bean

@ -17,6 +17,8 @@
package com.tencent.cloud.polaris.router.config.properties;
import com.tencent.polaris.api.rpc.RuleBasedRouterFailoverType;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
@ -29,6 +31,8 @@ public class PolarisRuleBasedRouterProperties {
private boolean enabled = true;
private RuleBasedRouterFailoverType failOver = RuleBasedRouterFailoverType.all;
public boolean isEnabled() {
return enabled;
}
@ -37,10 +41,19 @@ public class PolarisRuleBasedRouterProperties {
this.enabled = enabled;
}
public RuleBasedRouterFailoverType getFailOver() {
return failOver;
}
public void setFailOver(RuleBasedRouterFailoverType failOver) {
this.failOver = failOver;
}
@Override
public String toString() {
return "PolarisNearByRouterProperties{" +
return "PolarisRuleBasedRouterProperties{" +
"enabled=" + enabled +
", failOver=" + failOver +
'}';
}
}

@ -1,98 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router.feign;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils;
import feign.RequestTemplate;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils;
/**
* Resolve rule expression label from feign request.
*
* @author lepdou 2022-05-20
*/
public final class FeignExpressionLabelUtils {
private FeignExpressionLabelUtils() {
}
public static Map<String, String> resolve(RequestTemplate request, Set<String> labelKeys) {
if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap();
}
Map<String, String> labels = new HashMap<>();
for (String labelKey : labelKeys) {
if (ExpressionLabelUtils.isHeaderLabel(labelKey)) {
String headerKey = ExpressionLabelUtils.parseHeaderKey(labelKey);
if (StringUtils.isBlank(headerKey)) {
continue;
}
labels.put(labelKey, getHeaderValue(request, headerKey));
}
else if (ExpressionLabelUtils.isQueryLabel(labelKey)) {
String queryKey = ExpressionLabelUtils.parseQueryKey(labelKey);
if (StringUtils.isBlank(queryKey)) {
continue;
}
labels.put(labelKey, getQueryValue(request, queryKey));
}
else if (ExpressionLabelUtils.isCookieLabel(labelKey)) {
String cookieKey = ExpressionLabelUtils.parseCookieKey(labelKey);
if (StringUtils.isBlank(cookieKey)) {
continue;
}
labels.put(labelKey, getCookieValue(request, cookieKey));
}
else if (ExpressionLabelUtils.isMethodLabel(labelKey)) {
labels.put(labelKey, request.method());
}
else if (ExpressionLabelUtils.isUriLabel(labelKey)) {
URI uri = URI.create(request.request().url());
labels.put(labelKey, uri.getPath());
}
}
return labels;
}
public static String getHeaderValue(RequestTemplate request, String key) {
Map<String, Collection<String>> headers = request.headers();
return ExpressionLabelUtils.getFirstValue(headers, key);
}
public static String getQueryValue(RequestTemplate request, String key) {
return ExpressionLabelUtils.getFirstValue(request.queries(), key);
}
public static String getCookieValue(RequestTemplate request, String key) {
Map<String, Collection<String>> headers = request.headers();
return ExpressionLabelUtils.getCookieFirstValue(headers, key);
}
}

@ -18,63 +18,21 @@
package com.tencent.cloud.polaris.router.feign;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.FeignRouterLabelResolver;
import com.tencent.cloud.metadata.provider.FeignRequestTemplateMetadataProvider;
import com.tencent.polaris.metadata.core.MetadataType;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.util.CollectionUtils;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
/**
* Resolver labels from request.
* Interceptor used for setting Feign RequestTemplate metadata provider.
*
* @author lepdou, cheese8, Hoatian Zhang
*/
public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered {
private static final Logger LOGGER = LoggerFactory.getLogger(RouterLabelFeignInterceptor.class);
private final List<FeignRouterLabelResolver> routerLabelResolvers;
private final StaticMetadataManager staticMetadataManager;
private final RouterRuleLabelResolver routerRuleLabelResolver;
private final PolarisContextProperties polarisContextProperties;
public RouterLabelFeignInterceptor(List<FeignRouterLabelResolver> routerLabelResolvers,
StaticMetadataManager staticMetadataManager,
RouterRuleLabelResolver routerRuleLabelResolver,
PolarisContextProperties polarisContextProperties) {
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder));
this.routerLabelResolvers = routerLabelResolvers;
}
else {
this.routerLabelResolvers = null;
}
this.staticMetadataManager = staticMetadataManager;
this.routerRuleLabelResolver = routerRuleLabelResolver;
this.polarisContextProperties = polarisContextProperties;
}
@Override
public int getOrder() {
@ -83,62 +41,7 @@ public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered
@Override
public void apply(RequestTemplate requestTemplate) {
// local service labels
Map<String, String> labels = new HashMap<>(staticMetadataManager.getMergedStaticMetadata());
// labels from rule expression
String peerServiceName = requestTemplate.feignTarget().name();
Set<String> expressionLabelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
MetadataContext.LOCAL_SERVICE, peerServiceName);
Map<String, String> ruleExpressionLabels = getRuleExpressionLabels(requestTemplate, expressionLabelKeys);
labels.putAll(ruleExpressionLabels);
// labels from custom spi
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
routerLabelResolvers.forEach(resolver -> {
try {
Map<String, String> customResolvedLabels = resolver.resolve(requestTemplate, expressionLabelKeys);
if (!CollectionUtils.isEmpty(customResolvedLabels)) {
labels.putAll(customResolvedLabels);
}
}
catch (Throwable t) {
LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t);
}
});
}
// labels from downstream
Map<String, String> transitiveLabels = MetadataContextHolder.get().getTransitiveMetadata();
labels.putAll(transitiveLabels);
// pass label by header
String encodedLabelsContent;
try {
encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(labels), UTF_8);
}
catch (UnsupportedEncodingException e) {
throw new RuntimeException("unsupported charset exception " + UTF_8);
}
requestTemplate.header(RouterConstant.ROUTER_LABEL_HEADER, encodedLabelsContent);
}
private Map<String, String> getRuleExpressionLabels(RequestTemplate requestTemplate, Set<String> labelKeys) {
if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap();
}
//enrich labels from request
Map<String, String> labels = FeignExpressionLabelUtils.resolve(requestTemplate, labelKeys);
//enrich caller ip label
for (String labelKey : labelKeys) {
if (ExpressionLabelUtils.isCallerIPLabel(labelKey)) {
labels.put(labelKey, polarisContextProperties.getLocalIpAddress());
}
}
return labels;
MetadataContextHolder.get().getMetadataContainer(MetadataType.MESSAGE, false)
.setMetadataProvider(new FeignRequestTemplateMetadataProvider(requestTemplate));
}
}

@ -18,20 +18,16 @@
package com.tencent.cloud.polaris.router.interceptor;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.polaris.router.PolarisRouterContext;
import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties;
import com.tencent.cloud.polaris.router.spi.RouterRequestInterceptor;
import com.tencent.polaris.api.pojo.RouteArgument;
import com.tencent.polaris.metadata.core.MetadataContainer;
import com.tencent.polaris.metadata.core.MetadataType;
import com.tencent.polaris.metadata.core.TransitiveType;
import com.tencent.polaris.plugins.router.metadata.MetadataRouter;
import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
import org.springframework.util.CollectionUtils;
/**
* Router request interceptor for metadata router.
* @author lepdou, Hoatian Zhang
@ -51,18 +47,10 @@ public class MetadataRouterRequestInterceptor implements RouterRequestIntercepto
return;
}
// 1. get metadata router label keys
Set<String> metadataRouterKeys = routerContext.getLabelAsSet(LABEL_KEY_METADATA_ROUTER_KEYS);
// 2. get metadata router labels
Map<String, String> metadataRouterLabels = routerContext.getLabels(RouterConstant.ROUTER_LABELS,
metadataRouterKeys);
// 3. set metadata router labels to request
Set<RouteArgument> routeArguments = new HashSet<>();
if (!CollectionUtils.isEmpty(metadataRouterKeys)) {
for (Map.Entry<String, String> entry : metadataRouterLabels.entrySet()) {
routeArguments.add(RouteArgument.fromLabel(entry.getKey(), entry.getValue()));
}
}
request.putRouterArgument(MetadataRouter.ROUTER_TYPE_METADATA, routeArguments);
// set metadata router label keys
MetadataContainer metadataContainer = MetadataContextHolder.get()
.getMetadataContainer(MetadataType.CUSTOM, false);
String metadataRouteKeys = metadataContainer.getRawMetadataStringValue(LABEL_KEY_METADATA_ROUTER_KEYS);
metadataContainer.putMetadataMapValue(MetadataRouter.ROUTER_TYPE_METADATA, MetadataRouter.KEY_METADATA_KEYS, metadataRouteKeys, TransitiveType.NONE);
}
}

@ -18,13 +18,13 @@
package com.tencent.cloud.polaris.router.interceptor;
import java.util.HashSet;
import java.util.Set;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.polaris.router.PolarisRouterContext;
import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties;
import com.tencent.cloud.polaris.router.spi.RouterRequestInterceptor;
import com.tencent.polaris.api.pojo.RouteArgument;
import com.tencent.polaris.metadata.core.MetadataContainer;
import com.tencent.polaris.metadata.core.MetadataType;
import com.tencent.polaris.metadata.core.TransitiveType;
import com.tencent.polaris.plugins.router.nearby.NearbyRouter;
import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
@ -42,13 +42,10 @@ public class NearbyRouterRequestInterceptor implements RouterRequestInterceptor
@Override
public void apply(ProcessRoutersRequest request, PolarisRouterContext routerContext) {
if (!polarisNearByRouterProperties.isEnabled()) {
return;
}
Set<RouteArgument> routeArguments = new HashSet<>(1);
routeArguments.add(RouteArgument.buildCustom(NearbyRouter.ROUTER_ENABLED, "true"));
request.putRouterArgument(NearbyRouter.ROUTER_TYPE_NEAR_BY, routeArguments);
// set nearby router enable
boolean nearbyRouterEnabled = polarisNearByRouterProperties.isEnabled();
MetadataContainer metadataContainer = MetadataContextHolder.get()
.getMetadataContainer(MetadataType.CUSTOM, false);
metadataContainer.putMetadataMapValue(NearbyRouter.ROUTER_TYPE_NEAR_BY, NearbyRouter.ROUTER_ENABLED, String.valueOf(nearbyRouterEnabled), TransitiveType.NONE);
}
}

@ -17,20 +17,16 @@
package com.tencent.cloud.polaris.router.interceptor;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.polaris.router.PolarisRouterContext;
import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties;
import com.tencent.cloud.polaris.router.spi.RouterRequestInterceptor;
import com.tencent.polaris.api.pojo.RouteArgument;
import com.tencent.polaris.metadata.core.MetadataContainer;
import com.tencent.polaris.metadata.core.MetadataType;
import com.tencent.polaris.metadata.core.TransitiveType;
import com.tencent.polaris.plugins.router.rule.RuleBasedRouter;
import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
import org.springframework.util.CollectionUtils;
/**
* Router request interceptor for rule based router.
* @author lepdou, Hoatian Zhang
@ -45,24 +41,12 @@ public class RuleBasedRouterRequestInterceptor implements RouterRequestIntercept
@Override
public void apply(ProcessRoutersRequest request, PolarisRouterContext routerContext) {
// set rule based router enable
boolean ruleBasedRouterEnabled = polarisRuleBasedRouterProperties.isEnabled();
// set dynamic switch for rule based router
Set<RouteArgument> routeArguments = new HashSet<>();
routeArguments.add(RouteArgument.buildCustom(RuleBasedRouter.ROUTER_ENABLED, String.valueOf(ruleBasedRouterEnabled)));
// The label information that the rule based routing depends on
// is placed in the metadata of the source service for transmission.
// Later, can consider putting it in routerMetadata like other routers.
if (ruleBasedRouterEnabled) {
Map<String, String> ruleRouterLabels = routerContext.getLabels(RouterConstant.ROUTER_LABELS);
if (!CollectionUtils.isEmpty(ruleRouterLabels)) {
for (Map.Entry<String, String> label : ruleRouterLabels.entrySet()) {
routeArguments.add(RouteArgument.fromLabel(label.getKey(), label.getValue()));
}
}
}
request.putRouterArgument(RuleBasedRouter.ROUTER_TYPE_RULE_BASED, routeArguments);
MetadataContainer metadataContainer = MetadataContextHolder.get()
.getMetadataContainer(MetadataType.CUSTOM, false);
metadataContainer.putMetadataMapValue(RuleBasedRouter.ROUTER_TYPE_RULE_BASED, RuleBasedRouter.ROUTER_ENABLED, String.valueOf(ruleBasedRouterEnabled), TransitiveType.NONE);
// set rule based router fail over type.
request.setRuleBasedRouterFailoverType(polarisRuleBasedRouterProperties.getFailOver());
}
}

@ -1,60 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router.resttemplate;
import java.io.IOException;
import java.net.URI;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
/**
* PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor capabilities.
* Parses the label from the request and puts it into the RouterContext for routing.
*
* @author lepdou, cheese8
*/
public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
private final LoadBalancerClient loadBalancer;
private final LoadBalancerRequestFactory requestFactory;
public PolarisLoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerRequestFactory requestFactory) {
super(loadBalancer, requestFactory);
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String peerServiceName = originalUri.getHost();
Assert.state(peerServiceName != null,
"Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(peerServiceName,
new PolarisLoadBalancerRequest<>(request, this.requestFactory.createRequest(request, body, execution)));
}
}

@ -1,52 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.resttemplate;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
import org.springframework.http.HttpRequest;
/**
* Wrapper of {@link LoadBalancerRequest}.
*
* @author Haotian Zhang
*/
public class PolarisLoadBalancerRequest<T> implements LoadBalancerRequest<T> {
private HttpRequest request;
private LoadBalancerRequest<T> delegate;
public PolarisLoadBalancerRequest(HttpRequest request, LoadBalancerRequest<T> delegate) {
this.request = request;
this.delegate = delegate;
}
@Override
public T apply(ServiceInstance instance) throws Exception {
return delegate.apply(instance);
}
public HttpRequest getRequest() {
return request;
}
public LoadBalancerRequest<T> getDelegate() {
return delegate;
}
}

@ -18,73 +18,25 @@
package com.tencent.cloud.polaris.router.resttemplate;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils;
import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.tencent.cloud.metadata.provider.RestTemplateMetadataProvider;
import com.tencent.polaris.metadata.core.MetadataType;
import org.springframework.core.Ordered;
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;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
/**
* Interceptor used for adding the route label in http headers from context when web client
* is RestTemplate.
* Interceptor used for setting RestTemplate HttpRequest metadata provider.
*
* @author liuye, Hoatian Zhang
*/
public class RouterLabelRestTemplateInterceptor implements ClientHttpRequestInterceptor, Ordered {
private static final Logger LOGGER = LoggerFactory.getLogger(RouterLabelRestTemplateInterceptor.class);
private final List<SpringWebRouterLabelResolver> routerLabelResolvers;
private final StaticMetadataManager staticMetadataManager;
private final RouterRuleLabelResolver routerRuleLabelResolver;
private final PolarisContextProperties polarisContextProperties;
public RouterLabelRestTemplateInterceptor(List<SpringWebRouterLabelResolver> routerLabelResolvers,
StaticMetadataManager staticMetadataManager,
RouterRuleLabelResolver routerRuleLabelResolver,
PolarisContextProperties polarisContextProperties) {
this.staticMetadataManager = staticMetadataManager;
this.routerRuleLabelResolver = routerRuleLabelResolver;
this.polarisContextProperties = polarisContextProperties;
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder));
this.routerLabelResolvers = routerLabelResolvers;
}
else {
this.routerLabelResolvers = null;
}
}
@Override
public int getOrder() {
@ -94,82 +46,8 @@ public class RouterLabelRestTemplateInterceptor implements ClientHttpRequestInte
@Override
public ClientHttpResponse intercept(@NonNull HttpRequest request, @NonNull byte[] body,
@NonNull ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
final URI originalUri = request.getURI();
String peerServiceName = originalUri.getHost();
Assert.state(peerServiceName != null,
"Request URI does not contain a valid hostname: " + originalUri);
setLabelsToHeaders(request, body, peerServiceName);
ClientHttpResponse response = clientHttpRequestExecution.execute(request, body);
if (!CollectionUtils.isEmpty(request.getHeaders().get(RouterConstant.ROUTER_LABEL_HEADER))) {
HttpHeaders responseHeaders = HttpHeaders.writableHttpHeaders(response.getHeaders());
responseHeaders.addAll(RouterConstant.ROUTER_LABEL_HEADER, Objects.requireNonNull(request.getHeaders()
.get(RouterConstant.ROUTER_LABEL_HEADER)));
}
return response;
}
void setLabelsToHeaders(HttpRequest request, byte[] body, String peerServiceName) {
// local service labels
Map<String, String> labels = new HashMap<>(staticMetadataManager.getMergedStaticMetadata());
// labels from rule expression
Set<String> expressionLabelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
MetadataContext.LOCAL_SERVICE, peerServiceName);
Map<String, String> ruleExpressionLabels = getExpressionLabels(request, expressionLabelKeys);
if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
labels.putAll(ruleExpressionLabels);
}
// labels from request
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
routerLabelResolvers.forEach(resolver -> {
try {
Map<String, String> customResolvedLabels = resolver.resolve(request, body, expressionLabelKeys);
if (!CollectionUtils.isEmpty(customResolvedLabels)) {
labels.putAll(customResolvedLabels);
}
}
catch (Throwable t) {
LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t);
}
});
}
// labels from downstream
Map<String, String> transitiveLabels = MetadataContextHolder.get().getTransitiveMetadata();
labels.putAll(transitiveLabels);
// pass label by header
String encodedLabelsContent;
try {
encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(labels), UTF_8);
}
catch (UnsupportedEncodingException e) {
throw new RuntimeException("unsupported charset exception " + UTF_8);
}
request.getHeaders().set(RouterConstant.ROUTER_LABEL_HEADER, encodedLabelsContent);
}
private Map<String, String> getExpressionLabels(HttpRequest request, Set<String> labelKeys) {
if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap();
}
//enrich labels from request
Map<String, String> labels = SpringWebExpressionLabelUtils.resolve(request, labelKeys);
//enrich caller ip label
for (String labelKey : labelKeys) {
if (ExpressionLabelUtils.isCallerIPLabel(labelKey)) {
labels.put(labelKey, polarisContextProperties.getLocalIpAddress());
}
}
return labels;
MetadataContextHolder.get().getMetadataContainer(MetadataType.MESSAGE, false)
.setMetadataProvider(new RestTemplateMetadataProvider(request));
return clientHttpRequestExecution.execute(request, body);
}
}

@ -1,282 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router.scg;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils;
import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.router.PolarisRouterServiceInstanceListSupplier;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.CompletionContext;
import org.springframework.cloud.client.loadbalancer.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycle;
import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycleValidator;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.RequestData;
import org.springframework.cloud.client.loadbalancer.RequestDataContext;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.client.loadbalancer.ResponseData;
import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
/**
* ReactiveLoadBalancerClientFilter does not have the ability to pass route labels, so it is replaced
* with PolarisReactiveLoadBalancerClientFilter. The passed route labels are used in
* {@link PolarisRouterServiceInstanceListSupplier}.
*
* @author lepdou, Hoatian Zhang
*/
public class PolarisReactiveLoadBalancerClientFilter extends ReactiveLoadBalancerClientFilter {
private static final Logger log = LoggerFactory.getLogger(PolarisReactiveLoadBalancerClientFilter.class);
private final LoadBalancerClientFactory clientFactory;
private final GatewayLoadBalancerProperties gatewayLoadBalancerProperties;
private final LoadBalancerProperties loadBalancerProperties;
private final StaticMetadataManager staticMetadataManager;
private final RouterRuleLabelResolver routerRuleLabelResolver;
private final List<SpringWebRouterLabelResolver> routerLabelResolvers;
private final PolarisContextProperties polarisContextProperties;
public PolarisReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory,
GatewayLoadBalancerProperties gatewayLoadBalancerProperties,
LoadBalancerProperties loadBalancerProperties,
StaticMetadataManager staticMetadataManager,
RouterRuleLabelResolver routerRuleLabelResolver,
List<SpringWebRouterLabelResolver> routerLabelResolvers,
PolarisContextProperties polarisContextProperties) {
super(clientFactory, gatewayLoadBalancerProperties, loadBalancerProperties);
this.clientFactory = clientFactory;
this.gatewayLoadBalancerProperties = gatewayLoadBalancerProperties;
this.loadBalancerProperties = loadBalancerProperties;
this.staticMetadataManager = staticMetadataManager;
this.routerRuleLabelResolver = routerRuleLabelResolver;
this.routerLabelResolvers = routerLabelResolvers;
this.polarisContextProperties = polarisContextProperties;
}
/**
* Copied from ReactiveLoadBalancerClientFilter, and create new RequestData for passing router labels.
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
return chain.filter(exchange);
}
// preserve the original url
addOriginalRequestUrl(exchange, url);
if (log.isTraceEnabled()) {
log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);
}
URI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
String serviceId = requestUri.getHost();
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
RequestDataContext.class, ResponseData.class, ServiceInstance.class);
// Pass route tags through http headers
HttpHeaders routerHttpHeaders = genRouterHttpHeaders(exchange, serviceId);
ServerHttpRequest request = exchange.getRequest();
RequestData requestData = new RequestData(request.getMethod(), request.getURI(), routerHttpHeaders,
new HttpHeaders(), new HashMap<>());
DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(new RequestDataContext(
requestData, getHint(serviceId, loadBalancerProperties.getHint())));
return choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext(response -> {
if (!response.hasServer()) {
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
.onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, response)));
throw NotFoundException.create(gatewayLoadBalancerProperties.isUse404(),
"Unable to find instance for " + url.getHost());
}
ServiceInstance retrievedInstance = response.getServer();
URI uri = exchange.getRequest().getURI();
// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
// if the loadbalancer doesn't provide one.
String overrideScheme = retrievedInstance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(retrievedInstance,
overrideScheme);
URI requestUrl = reconstructURI(serviceInstance, uri);
if (log.isTraceEnabled()) {
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
}
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
exchange.getAttributes().put(GATEWAY_LOADBALANCER_RESPONSE_ATTR, response);
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, response));
}).then(chain.filter(exchange))
.doOnError(throwable -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
CompletionContext.Status.FAILED, throwable, lbRequest,
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR)))))
.doOnSuccess(aVoid -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
CompletionContext.Status.SUCCESS, lbRequest,
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR),
new ResponseData(exchange.getResponse(), new RequestData(exchange.getRequest()))))));
}
@Override
protected URI reconstructURI(ServiceInstance serviceInstance, URI original) {
return LoadBalancerUriTools.reconstructURI(serviceInstance, original);
}
private Mono<Response<ServiceInstance>> choose(Request<RequestDataContext> lbRequest, String serviceId,
Set<LoadBalancerLifecycle> supportedLifecycleProcessors) {
ReactorLoadBalancer<ServiceInstance> loadBalancer = this.clientFactory.getInstance(serviceId,
ReactorServiceInstanceLoadBalancer.class);
if (loadBalancer == null) {
throw new NotFoundException("No loadbalancer available for " + serviceId);
}
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
return loadBalancer.choose(lbRequest);
}
// no actual used
private String getHint(String serviceId, Map<String, String> hints) {
String defaultHint = hints.getOrDefault("default", "default");
String hintPropertyValue = hints.get(serviceId);
return hintPropertyValue != null ? hintPropertyValue : defaultHint;
}
// In order to be consistent with feign and restTemplate,
// the router label is passed through the http header uniformly instead of the original hint mechanism.
HttpHeaders genRouterHttpHeaders(ServerWebExchange exchange, String peerServiceName) {
HttpHeaders headers = new HttpHeaders();
headers.add(RouterConstant.ROUTER_LABEL_HEADER, genRouterHint(exchange, peerServiceName));
return headers;
}
private String genRouterHint(ServerWebExchange exchange, String peerServiceName) {
Map<String, String> routerLabels = genRouterLabels(exchange, peerServiceName);
String encodedLabelsContent;
try {
encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(routerLabels), UTF_8);
}
catch (UnsupportedEncodingException e) {
throw new RuntimeException("unsupported charset exception " + UTF_8);
}
return encodedLabelsContent;
}
private Map<String, String> genRouterLabels(ServerWebExchange exchange, String peerServiceName) {
// local service labels
Map<String, String> labels = new HashMap<>(staticMetadataManager.getMergedStaticMetadata());
// labels from rule expression
Set<String> expressionLabelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
MetadataContext.LOCAL_SERVICE, peerServiceName);
Map<String, String> ruleExpressionLabels = getExpressionLabels(exchange, expressionLabelKeys);
if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
labels.putAll(ruleExpressionLabels);
}
// labels from request
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
routerLabelResolvers.forEach(resolver -> {
try {
Map<String, String> customResolvedLabels = resolver.resolve(exchange, expressionLabelKeys);
if (!CollectionUtils.isEmpty(customResolvedLabels)) {
labels.putAll(customResolvedLabels);
}
}
catch (Throwable t) {
log.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t);
}
});
}
// labels from downstream
Map<String, String> transitiveLabels = MetadataContextHolder.get().getTransitiveMetadata();
labels.putAll(transitiveLabels);
return labels;
}
private Map<String, String> getExpressionLabels(ServerWebExchange exchange, Set<String> labelKeys) {
if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap();
}
//enrich labels from request
Map<String, String> labels = SpringWebExpressionLabelUtils.resolve(exchange, labelKeys);
//enrich caller ip label
for (String labelKey : labelKeys) {
if (ExpressionLabelUtils.isCallerIPLabel(labelKey)) {
labels.put(labelKey, polarisContextProperties.getLocalIpAddress());
}
}
return labels;
}
}

@ -0,0 +1,57 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router.scg;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.metadata.provider.ReactiveMetadataProvider;
import com.tencent.polaris.metadata.core.MetadataType;
import com.tencent.polaris.metadata.core.constant.MetadataConstants;
import com.tencent.polaris.metadata.core.manager.CalleeMetadataContainerGroup;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER;
/**
* Interceptor used for setting SCG ServerWebExchange metadata provider.
*
* @author Hoatian Zhang
*/
public class RouterLabelGlobalFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return LOAD_BALANCER_CLIENT_FILTER_ORDER - 1;
}
/**
* Copied from ReactiveLoadBalancerClientFilter, and create new RequestData for passing router labels.
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
MetadataContextHolder.get().getMetadataContainer(MetadataType.MESSAGE, false)
.setMetadataProvider(new ReactiveMetadataProvider(exchange.getRequest(),
CalleeMetadataContainerGroup.getStaticApplicationMetadataContainer()
.getRawMetadataStringValue(MetadataConstants.LOCAL_IP)));
return chain.filter(exchange);
}
}

@ -1,42 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router.spi;
import java.util.Map;
import java.util.Set;
import feign.RequestTemplate;
import org.springframework.core.Ordered;
/**
* Router label resolver for feign request.
* @author lepdou 2022-07-20
*/
public interface FeignRouterLabelResolver extends Ordered {
/**
* Resolve labels from feign request. User can customize expression parser to extract labels.
*
* @param requestTemplate the feign request.
* @param expressionLabelKeys the expression labels which are configured in router rule.
* @return resolved labels
*/
Map<String, String> resolve(RequestTemplate requestTemplate, Set<String> expressionLabelKeys);
}

@ -1,57 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.spi;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.springframework.core.Ordered;
import org.springframework.http.HttpRequest;
import org.springframework.web.server.ServerWebExchange;
/**
* Router label resolver for spring web http request.
* @author lepdou 2022-07-20
*/
public interface SpringWebRouterLabelResolver extends Ordered {
/**
* resolve labels from rest template request. User can customize expression parser to extract labels.
*
* @param request the rest template request.
* @param body the rest template request body.
* @param expressionLabelKeys the expression labels which are configured in router rule.
* @return resolved labels
*/
default Map<String, String> resolve(HttpRequest request, byte[] body, Set<String> expressionLabelKeys) {
return Collections.emptyMap();
}
/**
* resolve labels from server web exchange. User can customize expression parser to extract labels.
*
* @param exchange the server web exchange.
* @param expressionLabelKeys the expression labels which are configured in router rule.
* @return resolved labels
*/
default Map<String, String> resolve(ServerWebExchange exchange, Set<String> expressionLabelKeys) {
return Collections.emptyMap();
}
}

@ -24,6 +24,12 @@
"defaultValue": true,
"description": "the switch for rule based router."
},
{
"name": "spring.cloud.polaris.router.rule-router.failOver",
"type": "java.lang.String",
"defaultValue": "all",
"description": "the fail over type for rule based router."
},
{
"name": "spring.cloud.polaris.router.enabled",
"type": "java.lang.Boolean",

@ -20,7 +20,6 @@ package com.tencent.cloud.polaris.router;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Sets;
import com.tencent.cloud.common.constant.RouterConstant;
@ -97,20 +96,4 @@ public class PolarisRouterContextTest {
String resolvedLabel = routerContext.getLabel("k1");
assertThat(resolvedLabel).isEqualTo("v1");
}
@Test
public void testGetLabelAsSet() {
Map<String, String> labels = new HashMap<>();
labels.put("k1", "v1,v2,v3");
PolarisRouterContext routerContext = new PolarisRouterContext();
routerContext.putLabels(RouterConstant.ROUTER_LABELS, labels);
Set<String> resolvedLabels = routerContext.getLabelAsSet("k1");
assertThat(resolvedLabels.size()).isEqualTo(3);
assertThat(resolvedLabels).contains("v1");
assertThat(resolvedLabels).contains("v2");
assertThat(resolvedLabels).contains("v3");
}
}

@ -24,7 +24,6 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import com.tencent.cloud.common.constant.RouterConstant;
@ -38,7 +37,6 @@ import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouter
import com.tencent.cloud.polaris.router.interceptor.MetadataRouterRequestInterceptor;
import com.tencent.cloud.polaris.router.interceptor.NearbyRouterRequestInterceptor;
import com.tencent.cloud.polaris.router.interceptor.RuleBasedRouterRequestInterceptor;
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerRequest;
import com.tencent.cloud.polaris.router.spi.RouterRequestInterceptor;
import com.tencent.cloud.polaris.router.spi.RouterResponseInterceptor;
import com.tencent.cloud.rpc.enhancement.transformer.PolarisInstanceTransformer;
@ -46,9 +44,11 @@ import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.api.pojo.DefaultInstance;
import com.tencent.polaris.api.pojo.DefaultServiceInstances;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.RouteArgument;
import com.tencent.polaris.api.pojo.ServiceInstances;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.metadata.core.MetadataContainer;
import com.tencent.polaris.metadata.core.MetadataType;
import com.tencent.polaris.metadata.core.TransitiveType;
import com.tencent.polaris.plugins.router.metadata.MetadataRouter;
import com.tencent.polaris.plugins.router.nearby.NearbyRouter;
import com.tencent.polaris.plugins.router.rule.RuleBasedRouter;
@ -66,7 +66,6 @@ import reactor.core.publisher.Flux;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.DefaultRequestContext;
import org.springframework.cloud.client.loadbalancer.RequestData;
import org.springframework.cloud.client.loadbalancer.RequestDataContext;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
@ -123,27 +122,18 @@ public class PolarisRouterServiceInstanceListSupplierTest {
PolarisRouterServiceInstanceListSupplier polarisSupplier = new PolarisRouterServiceInstanceListSupplier(
delegate, routerAPI, requestInterceptors, null, new PolarisInstanceTransformer());
MetadataContainer metadataContainer = MetadataContextHolder.get()
.getMetadataContainer(MetadataType.CUSTOM, false);
metadataContainer.putMetadataStringValue("system-metadata-router-keys", "k2", TransitiveType.NONE);
ServiceInstances serviceInstances = assembleServiceInstances();
PolarisRouterContext routerContext = assembleRouterContext();
Map<String, String> oldRouterLabels = routerContext.getLabels(RouterConstant.ROUTER_LABELS);
Map<String, String> newRouterLabels = new HashMap<>(oldRouterLabels);
newRouterLabels.put("system-metadata-router-keys", "k2");
routerContext.putLabels(RouterConstant.ROUTER_LABELS, newRouterLabels);
ProcessRoutersRequest request = polarisSupplier.buildProcessRoutersRequest(serviceInstances, routerContext);
polarisSupplier.processRouterRequestInterceptors(request, routerContext);
Set<RouteArgument> routerMetadata = request.getRouterArguments(MetadataRouter.ROUTER_TYPE_METADATA);
assertThat(routerMetadata.size()).isEqualTo(1);
assertThat(request.getRouterArguments(NearbyRouter.ROUTER_TYPE_NEAR_BY).size()).isEqualTo(0);
assertThat(request.getRouterArguments(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size()).isEqualTo(1);
for (RouteArgument routeArgument : request.getRouterArguments(RuleBasedRouter.ROUTER_TYPE_RULE_BASED)) {
assertThat(routeArgument.getKey()).isEqualTo(RuleBasedRouter.ROUTER_ENABLED);
assertThat(routeArgument.getValue()).isEqualTo("false");
}
String result = metadataContainer.getRawMetadataMapValue(MetadataRouter.ROUTER_TYPE_METADATA, MetadataRouter.KEY_METADATA_KEYS);
assertThat(result).isEqualTo("k2");
}
}
@ -166,22 +156,10 @@ public class PolarisRouterServiceInstanceListSupplierTest {
ProcessRoutersRequest request = polarisSupplier.buildProcessRoutersRequest(serviceInstances, routerContext);
polarisSupplier.processRouterRequestInterceptors(request, routerContext);
Set<RouteArgument> routerMetadata = request.getRouterArguments(NearbyRouter.ROUTER_TYPE_NEAR_BY);
assertThat(request.getRouterArguments(MetadataRouter.ROUTER_TYPE_METADATA).size()).isEqualTo(0);
assertThat(routerMetadata.size()).isEqualTo(1);
for (RouteArgument routeArgument : routerMetadata) {
assertThat(routeArgument.getKey()).isEqualTo(RuleBasedRouter.ROUTER_ENABLED);
assertThat(routeArgument.getValue()).isEqualTo("true");
}
assertThat(request.getRouterArguments(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size()).isEqualTo(1);
for (RouteArgument routeArgument : request.getRouterArguments(RuleBasedRouter.ROUTER_TYPE_RULE_BASED)) {
assertThat(routeArgument.getKey()).isEqualTo(RuleBasedRouter.ROUTER_ENABLED);
assertThat(routeArgument.getValue()).isEqualTo("false");
}
MetadataContainer metadataContainer = MetadataContextHolder.get()
.getMetadataContainer(MetadataType.CUSTOM, false);
String result = metadataContainer.getRawMetadataMapValue(NearbyRouter.ROUTER_TYPE_NEAR_BY, NearbyRouter.ROUTER_ENABLED);
assertThat(result).isEqualTo("true");
}
}
@ -204,11 +182,10 @@ public class PolarisRouterServiceInstanceListSupplierTest {
ProcessRoutersRequest request = polarisSupplier.buildProcessRoutersRequest(serviceInstances, routerContext);
polarisSupplier.processRouterRequestInterceptors(request, routerContext);
Set<RouteArgument> routerMetadata = request.getRouterArguments(RuleBasedRouter.ROUTER_TYPE_RULE_BASED);
assertThat(routerMetadata.size()).isEqualTo(3);
assertThat(request.getRouterArguments(MetadataRouter.ROUTER_TYPE_METADATA).size()).isEqualTo(0);
assertThat(request.getRouterArguments(NearbyRouter.ROUTER_TYPE_NEAR_BY).size()).isEqualTo(0);
MetadataContainer metadataContainer = MetadataContextHolder.get()
.getMetadataContainer(MetadataType.CUSTOM, false);
String result = metadataContainer.getRawMetadataMapValue(RuleBasedRouter.ROUTER_TYPE_RULE_BASED, RuleBasedRouter.ROUTER_ENABLED);
assertThat(result).isEqualTo("true");
}
}
@ -242,7 +219,7 @@ public class PolarisRouterServiceInstanceListSupplierTest {
HttpHeaders headers = new HttpHeaders();
PolarisRouterContext context = polarisSupplier.buildRouterContext(headers);
assertThat(context).isNull();
assertThat(context).isNotNull();
// mock
try (MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
@ -266,25 +243,6 @@ public class PolarisRouterServiceInstanceListSupplierTest {
@Test
public void testGet02() {
try (MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
.thenReturn(testCallerService);
PolarisRouterServiceInstanceListSupplier polarisSupplier = new PolarisRouterServiceInstanceListSupplier(
delegate, routerAPI, requestInterceptors, null, new PolarisInstanceTransformer());
MockServerHttpRequest httpRequest = MockServerHttpRequest.get("/" + testCalleeService + "/users")
.header("k1", "v1")
.queryParam("userid", "zhangsan")
.build();
RequestDataContext requestDataContext = new RequestDataContext(new RequestData(httpRequest), "blue");
DefaultRequest request = new DefaultRequest(requestDataContext);
assertThat(polarisSupplier.get(request)).isNull();
}
}
@Test
public void testGet03() {
try (MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
.thenReturn(testCallerService);
@ -299,14 +257,14 @@ public class PolarisRouterServiceInstanceListSupplierTest {
MockServerHttpRequest httpRequest = MockServerHttpRequest.get("/" + testCalleeService + "/users")
.header("k1", "v1")
.header(RouterConstant.ROUTER_LABEL_HEADER, "{\"k1\":\"v1\"}")
.queryParam("userid", "zhangsan")
.build();
DefaultRequestContext requestDataContext = new DefaultRequestContext(new PolarisLoadBalancerRequest(httpRequest, null), "blue");
RequestDataContext requestDataContext = new RequestDataContext(new RequestData(httpRequest), "blue");
DefaultRequest request = new DefaultRequest(requestDataContext);
assertThat(polarisSupplier.get(request).blockFirst().size()).isEqualTo(0);
assertThat(polarisSupplier.get(request)).isNotNull();
}
}
private void setTransitiveMetadata() {
if (initTransitiveMetadata.compareAndSet(false, true)) {
// mock transitive metadata

@ -1,95 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.polaris.context.ServiceRuleManager;
import com.tencent.polaris.specification.api.v1.model.ModelProto;
import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* test for {@link RouterRuleLabelResolver}.
*
* @author lepdou 2022-05-26
*/
@ExtendWith(MockitoExtension.class)
public class RouterRuleLabelResolverTest {
private final String testNamespace = "testNamespace";
private final String testSourceService = "sourceService";
private final String testDstService = "dstService";
@Mock
private ServiceRuleManager serviceRuleManager;
@Test
public void test() {
Map<String, ModelProto.MatchString> labels = new HashMap<>();
ModelProto.MatchString matchString = ModelProto.MatchString.getDefaultInstance();
String validKey1 = "${http.header.uid}";
String validKey2 = "${http.query.name}";
String validKey3 = "${http.method}";
String validKey4 = "${http.uri}";
String validKey5 = "${http.body.customkey}";
String invalidKey = "$http.expression.wrong}";
labels.put(validKey1, matchString);
labels.put(validKey2, matchString);
labels.put(validKey3, matchString);
labels.put(validKey4, matchString);
labels.put(validKey5, matchString);
labels.put(invalidKey, matchString);
RoutingProto.Source source1 = RoutingProto.Source.newBuilder().putAllMetadata(labels).build();
RoutingProto.Source source2 = RoutingProto.Source.newBuilder().putAllMetadata(labels).build();
RoutingProto.Source source3 = RoutingProto.Source.newBuilder().putAllMetadata(new HashMap<>()).build();
List<RoutingProto.Route> routes = new LinkedList<>();
RoutingProto.Route route = RoutingProto.Route.newBuilder()
.addAllSources(Lists.list(source1, source2, source3))
.build();
routes.add(route);
when(serviceRuleManager.getServiceRouterRule(testNamespace, testSourceService, testDstService)).thenReturn(routes);
RouterRuleLabelResolver resolver = new RouterRuleLabelResolver(serviceRuleManager);
Set<String> resolvedExpressionLabelKeys = resolver.getExpressionLabelKeys(testNamespace, testSourceService, testDstService);
assertThat(resolvedExpressionLabelKeys).isNotNull();
assertThat(resolvedExpressionLabelKeys.size()).isEqualTo(6);
assertThat(resolvedExpressionLabelKeys).contains(validKey1);
assertThat(resolvedExpressionLabelKeys).contains(validKey2);
assertThat(resolvedExpressionLabelKeys).contains(validKey3);
assertThat(resolvedExpressionLabelKeys).contains(validKey4);
assertThat(resolvedExpressionLabelKeys).contains(validKey5);
assertThat(resolvedExpressionLabelKeys).contains(invalidKey);
}
}

@ -28,6 +28,7 @@ import com.tencent.cloud.rpc.enhancement.transformer.PolarisInstanceTransformer;
import com.tencent.polaris.api.pojo.DefaultInstance;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInstances;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
@ -51,6 +52,11 @@ public class RouterUtilsTest {
private static final String testNamespaceAndService = "testNamespaceAndService";
@BeforeAll
static void beforeAll() {
MetadataContext.LOCAL_NAMESPACE = testNamespaceAndService;
}
@Test
public void testTransferEmptyInstances() {
ServiceInstances serviceInstances = RouterUtils.transferServersToServiceInstances(Flux.empty(), new PolarisInstanceTransformer());

@ -1,86 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.beanprocessor;
import com.tencent.cloud.common.util.BeanFactoryUtils;
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerInterceptor;
import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* Test for ${@link LoadBalancerInterceptorBeanPostProcessor}.
*
* @author lepdou 2022-05-26
*/
@ExtendWith(MockitoExtension.class)
public class LoadBalancerInterceptorBeanPostProcessorTest {
@Mock
private LoadBalancerClient loadBalancerClient;
@Mock
private LoadBalancerRequestFactory loadBalancerRequestFactory;
@Mock
private BeanFactory beanFactory;
@Test
public void testWrapperLoadBalancerInterceptor() {
when(beanFactory.getBean(LoadBalancerRequestFactory.class)).thenReturn(loadBalancerRequestFactory);
when(beanFactory.getBean(LoadBalancerClient.class)).thenReturn(loadBalancerClient);
try (MockedStatic<BeanFactoryUtils> mockedBeanFactoryUtils = Mockito.mockStatic(BeanFactoryUtils.class)) {
mockedBeanFactoryUtils.when(() -> BeanFactoryUtils.getBeans(beanFactory, SpringWebRouterLabelResolver.class))
.thenReturn(null);
LoadBalancerInterceptor loadBalancerInterceptor = new LoadBalancerInterceptor(loadBalancerClient, loadBalancerRequestFactory);
LoadBalancerInterceptorBeanPostProcessor processor = new LoadBalancerInterceptorBeanPostProcessor();
processor.setBeanFactory(beanFactory);
Object bean = processor.postProcessBeforeInitialization(loadBalancerInterceptor, "");
assertThat(bean).isInstanceOf(PolarisLoadBalancerInterceptor.class);
}
}
@Test
public void testNotWrapperLoadBalancerInterceptor() {
LoadBalancerInterceptorBeanPostProcessor processor = new LoadBalancerInterceptorBeanPostProcessor();
processor.setBeanFactory(beanFactory);
OtherBean otherBean = new OtherBean();
Object bean = processor.postProcessBeforeInitialization(otherBean, "");
assertThat(bean).isNotInstanceOf(PolarisLoadBalancerInterceptor.class);
assertThat(bean).isInstanceOf(OtherBean.class);
}
static class OtherBean {
}
}

@ -1,100 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.beanprocessor;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.BeanFactoryUtils;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.scg.PolarisReactiveLoadBalancerClientFilter;
import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* Test for ${@link ReactiveLoadBalancerClientFilterBeanPostProcessor}.
*
* @author lepdou 2022-07-04
*/
@ExtendWith(MockitoExtension.class)
public class ReactiveLoadBalancerClientFilterBeanPostProcessorTest {
@Mock
private BeanFactory beanFactory;
@Mock
private LoadBalancerClientFactory loadBalancerClientFactory;
@Mock
private GatewayLoadBalancerProperties gatewayLoadBalancerProperties;
@Mock
private LoadBalancerProperties loadBalancerProperties;
@Mock
private StaticMetadataManager staticMetadataManager;
@Mock
private RouterRuleLabelResolver routerRuleLabelResolver;
@Test
public void testWrapReactiveLoadBalancerClientFilter() {
when(beanFactory.getBean(LoadBalancerClientFactory.class)).thenReturn(loadBalancerClientFactory);
when(beanFactory.getBean(GatewayLoadBalancerProperties.class)).thenReturn(gatewayLoadBalancerProperties);
when(beanFactory.getBean(LoadBalancerProperties.class)).thenReturn(loadBalancerProperties);
when(beanFactory.getBean(StaticMetadataManager.class)).thenReturn(staticMetadataManager);
when(beanFactory.getBean(RouterRuleLabelResolver.class)).thenReturn(routerRuleLabelResolver);
try (MockedStatic<BeanFactoryUtils> mockedBeanFactoryUtils = Mockito.mockStatic(BeanFactoryUtils.class)) {
mockedBeanFactoryUtils.when(() -> BeanFactoryUtils.getBeans(beanFactory, SpringWebRouterLabelResolver.class))
.thenReturn(null);
ReactiveLoadBalancerClientFilter reactiveLoadBalancerClientFilter = new ReactiveLoadBalancerClientFilter(
loadBalancerClientFactory, gatewayLoadBalancerProperties, loadBalancerProperties);
ReactiveLoadBalancerClientFilterBeanPostProcessor processor = new ReactiveLoadBalancerClientFilterBeanPostProcessor();
processor.setBeanFactory(beanFactory);
Object bean = processor.postProcessBeforeInitialization(reactiveLoadBalancerClientFilter, "");
assertThat(bean).isInstanceOf(PolarisReactiveLoadBalancerClientFilter.class);
}
}
@Test
public void testNotWrapLoadBalancerInterceptor() {
ReactiveLoadBalancerClientFilterBeanPostProcessor processor = new ReactiveLoadBalancerClientFilterBeanPostProcessor();
processor.setBeanFactory(beanFactory);
OtherBean otherBean = new OtherBean();
Object bean = processor.postProcessBeforeInitialization(otherBean, "");
assertThat(bean).isNotInstanceOf(PolarisReactiveLoadBalancerClientFilter.class);
assertThat(bean).isInstanceOf(OtherBean.class);
}
static class OtherBean {
}
}

@ -49,7 +49,7 @@ public class PolarisRuleBasedRouterPropertiesTest {
@Test
public void testToString() {
assertThat(properties.toString())
.isEqualTo("PolarisNearByRouterProperties{enabled=true}");
.isEqualTo("PolarisRuleBasedRouterProperties{enabled=true, failOver=all}");
}
}

@ -1,162 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router.feign;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import feign.Request;
import feign.RequestTemplate;
import org.junit.jupiter.api.Test;
import static java.util.stream.Collectors.toSet;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link FeignExpressionLabelUtils}.
*
* @author lepdou 2022-05-26
*/
public class FeignExpressionLabelUtilsTest {
@Test
public void testGetHeaderLabel() {
String headerKey = "uid";
String headerValue = "1000";
String headerKey2 = "teacher.age";
String headerValue2 = "1000";
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.header(headerKey, headerValue);
requestTemplate.header(headerKey2, headerValue2);
String labelKey1 = "${http.header.uid}";
String labelKey2 = "${http.header.name}";
String labelKey3 = "${http.headername}";
String labelKey4 = "${http.header.}";
String labelKey5 = "${http.header.teacher.age}";
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate,
Stream.of(labelKey1, labelKey2, labelKey3, labelKey4, labelKey5).collect(toSet()));
assertThat(result).isNotEmpty();
assertThat(result.get(labelKey1)).isEqualTo(headerValue);
assertThat(result.get(labelKey5)).isEqualTo(headerValue2);
assertThat(result.get(labelKey2)).isBlank();
assertThat(result.get(labelKey3)).isBlank();
assertThat(result.get(labelKey4)).isBlank();
}
@Test
public void testGetQueryLabel() {
String headerKey = "uid";
String headerValue = "1000";
String headerKey2 = "teacher.age";
String headerValue2 = "1000";
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.query(headerKey, headerValue);
requestTemplate.query(headerKey2, headerValue2);
String labelKey1 = "${http.query.uid}";
String labelKey2 = "${http.query.name}";
String labelKey3 = "${http.queryname}";
String labelKey4 = "${http.query.}";
String labelKey5 = "${http.query.teacher.age}";
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate,
Stream.of(labelKey1, labelKey2, labelKey3, labelKey4, labelKey5).collect(toSet()));
assertThat(result).isNotEmpty();
assertThat(result.get(labelKey1)).isEqualTo(headerValue);
assertThat(result.get(labelKey5)).isEqualTo(headerValue2);
assertThat(result.get(labelKey2)).isBlank();
assertThat(result.get(labelKey3)).isBlank();
assertThat(result.get(labelKey4)).isBlank();
}
@Test
public void testGetMethod() {
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.method(Request.HttpMethod.GET);
String labelKey1 = "${http.method}";
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate, Stream.of(labelKey1)
.collect(toSet()));
assertThat(result).isNotEmpty();
assertThat(result.get(labelKey1)).isEqualTo("GET");
}
@Test
public void testGetUri() {
String uri = "/user/get";
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.uri(uri);
requestTemplate.method(Request.HttpMethod.GET);
requestTemplate.target("http://localhost");
requestTemplate = requestTemplate.resolve(new HashMap<>());
String labelKey1 = "${http.uri}";
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate, Stream.of(labelKey1)
.collect(toSet()));
assertThat(result).isNotEmpty();
assertThat(result.get(labelKey1)).isEqualTo(uri);
}
@Test
public void testGetUri2() {
String uri = "/";
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.uri(uri);
requestTemplate.method(Request.HttpMethod.GET);
requestTemplate.target("http://localhost");
requestTemplate = requestTemplate.resolve(new HashMap<>());
String labelKey1 = "${http.uri}";
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate, Stream.of(labelKey1)
.collect(toSet()));
assertThat(result).isNotEmpty();
assertThat(result.get(labelKey1)).isEqualTo(uri);
}
@Test
public void testGetCookie() {
String uri = "/";
String cookieValue = "zhangsan";
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.uri(uri);
requestTemplate.method(Request.HttpMethod.GET);
requestTemplate.target("http://localhost");
requestTemplate = requestTemplate.resolve(new HashMap<>());
requestTemplate.header("cookie", Collections.singleton("uid=zhangsan; auth-token=dfhuwshfy77"));
String labelKey1 = "${http.cookie.uid}";
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate, Stream.of(labelKey1)
.collect(toSet()));
assertThat(result).isNotEmpty();
assertThat(result.get(labelKey1)).isEqualTo(cookieValue);
}
}

@ -18,124 +18,81 @@
package com.tencent.cloud.polaris.router.feign;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.FeignRouterLabelResolver;
import com.tencent.polaris.metadata.core.MessageMetadataContainer;
import com.tencent.polaris.metadata.core.MetadataContainer;
import com.tencent.polaris.metadata.core.MetadataType;
import feign.Request;
import feign.RequestTemplate;
import feign.Target;
import io.netty.handler.codec.http.HttpHeaderNames;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
/**
* test for {@link RouterLabelFeignInterceptor}.
*
* @author lepdou, cheese8
* @author lepdou 2022-05-26
*/
@ExtendWith(MockitoExtension.class)
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = RouterLabelFeignInterceptorTest.TestApplication.class,
properties = {"spring.cloud.polaris.namespace=test", "spring.application.name=test", "spring.cloud.gateway.enabled=false"})
public class RouterLabelFeignInterceptorTest {
@Mock
private StaticMetadataManager staticMetadataManager;
@Mock
private RouterRuleLabelResolver routerRuleLabelResolver;
@Mock
private FeignRouterLabelResolver routerLabelResolver;
@Mock
private PolarisContextProperties polarisContextProperties;
@Test
public void testResolveRouterLabel() throws UnsupportedEncodingException {
RouterLabelFeignInterceptor routerLabelFeignInterceptor = new RouterLabelFeignInterceptor(
Collections.singletonList(routerLabelResolver),
staticMetadataManager, routerRuleLabelResolver, polarisContextProperties);
public void testRouterLabel() {
RouterLabelFeignInterceptor routerLabelFeignInterceptor = new RouterLabelFeignInterceptor();
assertThat(routerLabelFeignInterceptor.getOrder()).isEqualTo(OrderConstant.Client.Feign.ROUTER_LABEL_INTERCEPTOR_ORDER);
// mock request template
RequestTemplate requestTemplate = new RequestTemplate();
RequestTemplate requestTemplate = mock(RequestTemplate.class);
String headerUidKey = "uid";
String headerUidValue = "1000";
requestTemplate.header(headerUidKey, headerUidValue);
String peerService = "peerService";
Target.EmptyTarget<Object> target = Target.EmptyTarget.create(Object.class, peerService);
requestTemplate.feignTarget(target);
// mock ApplicationContextAwareUtils#getProperties
try (MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
String testService = "callerService";
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
.thenReturn(testService);
MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
// mock transitive metadata
Map<String, String> transitiveLabels = new HashMap<>();
transitiveLabels.put("k1", "v1");
transitiveLabels.put("k2", "v22");
when(metadataContext.getTransitiveMetadata()).thenReturn(transitiveLabels);
// mock MetadataContextHolder#get
try (MockedStatic<MetadataContextHolder> mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) {
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
// mock expression rule labels
Set<String> expressionKeys = new HashSet<>();
expressionKeys.add("${http.header.uid}");
expressionKeys.add("${http.header.name}");
when(routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
MetadataContext.LOCAL_SERVICE, peerService)).thenReturn(expressionKeys);
// mock custom resolved labels from request
Map<String, String> customResolvedLabels = new HashMap<>();
customResolvedLabels.put("k2", "v2");
customResolvedLabels.put("k3", "v3");
when(routerLabelResolver.resolve(requestTemplate, expressionKeys)).thenReturn(customResolvedLabels);
Map<String, String> localMetadata = new HashMap<>();
localMetadata.put("k3", "v31");
localMetadata.put("k4", "v4");
when(staticMetadataManager.getMergedStaticMetadata()).thenReturn(localMetadata);
routerLabelFeignInterceptor.apply(requestTemplate);
Collection<String> routerLabels = requestTemplate.headers().get(RouterConstant.ROUTER_LABEL_HEADER);
Map<String, List<String>> headerMap = new HashMap<>();
headerMap.put(headerUidKey, Collections.singletonList(headerUidValue));
headerMap.put(HttpHeaderNames.COOKIE.toString(), Collections.singletonList("k1=v1"));
doReturn(headerMap).when(requestTemplate).headers();
doReturn(Request.HttpMethod.POST.toString()).when(requestTemplate).method();
Request request = mock(Request.class);
doReturn(request).when(requestTemplate).request();
doReturn("http://callee/test/path").when(request).url();
Map<String, List<String>> queryMap = new HashMap<>();
queryMap.put("q1", Collections.singletonList("a1"));
doReturn(queryMap).when(requestTemplate).queries();
routerLabelFeignInterceptor.apply(requestTemplate);
// get message metadata container
MetadataContainer metadataContainer = MetadataContextHolder.get()
.getMetadataContainer(MetadataType.MESSAGE, false);
// method
assertThat(metadataContainer.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_METHOD)).isEqualTo(Request.HttpMethod.POST.toString());
// path
assertThat(metadataContainer.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo("/test/path");
// header
assertThat(metadataContainer.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, headerUidKey)).isEqualTo(headerUidValue);
// cookie
assertThat(metadataContainer.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, "k1")).isEqualTo("v1");
// query
assertThat(metadataContainer.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, "q1")).isEqualTo("a1");
}
assertThat(routerLabels).isNotNull();
for (String value : routerLabels) {
Map<String, String> labels = JacksonUtils.deserialize2Map(URLDecoder.decode(value, "UTF-8"));
@SpringBootApplication
protected static class TestApplication {
assertThat(labels.get("k1")).isEqualTo("v1");
assertThat(labels.get("k2")).isEqualTo("v22");
assertThat(labels.get("k3")).isEqualTo("v3");
assertThat(labels.get("k4")).isEqualTo("v4");
assertThat(labels.get("${http.header.uid}")).isEqualTo(headerUidValue);
assertThat(labels.get("${http.header.name}")).isEqualTo("");
}
}
}
}
}

@ -1,59 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router.resttemplate;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
import org.springframework.http.HttpRequest;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
/**
* test for {@link PolarisLoadBalancerRequest}.
* @author dongyinuo
*/
public class PolarisLoadBalancerRequestTests {
@Test
public void test() throws Exception {
String calleeService = "calleeService";
HttpRequest request = new RouterLabelRestTemplateInterceptorTest.MockedHttpRequest("http://" + calleeService + "/user/get");
MockLoadBalancerRequest mockLoadBalancerRequest = new MockLoadBalancerRequest();
PolarisLoadBalancerRequest<ServiceInstance> polarisLoadBalancerRequest = new PolarisLoadBalancerRequest<>(request, mockLoadBalancerRequest);
DefaultServiceInstance serviceInstance = new DefaultServiceInstance();
serviceInstance.setServiceId(calleeService);
ServiceInstance apply = polarisLoadBalancerRequest.apply(serviceInstance);
assertThat(apply.getServiceId()).isEqualTo(calleeService);
assertThat(polarisLoadBalancerRequest.getRequest()).isEqualTo(request);
assertThat(polarisLoadBalancerRequest.getDelegate()).isEqualTo(mockLoadBalancerRequest);
}
static class MockLoadBalancerRequest implements LoadBalancerRequest {
@Override
public Object apply(ServiceInstance instance) throws Exception {
return instance;
}
}
}

@ -13,39 +13,26 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router.resttemplate;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import com.tencent.polaris.metadata.core.MessageMetadataContainer;
import com.tencent.polaris.metadata.core.MetadataContainer;
import com.tencent.polaris.metadata.core.MetadataType;
import feign.Request;
import io.netty.handler.codec.http.HttpHeaderNames;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.Ordered;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
@ -53,14 +40,11 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@ -68,117 +52,63 @@ import static org.mockito.Mockito.when;
*
* @author liuye, Haotian Zhang
*/
@ExtendWith(MockitoExtension.class)
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = RouterLabelRestTemplateInterceptorTest.TestApplication.class,
properties = {"spring.cloud.polaris.namespace=test", "spring.application.name=test", "spring.cloud.gateway.enabled=false"})
public class RouterLabelRestTemplateInterceptorTest {
private static final String testNamespaceAndService = "testNamespaceAndService";
@Mock
private SpringWebRouterLabelResolver routerLabelResolver;
@Mock
private StaticMetadataManager staticMetadataManager;
@Mock
private RouterRuleLabelResolver routerRuleLabelResolver;
@Mock
private PolarisContextProperties polarisContextProperties;
@Mock
private ClientHttpRequestExecution clientHttpRequestExecution;
@Test
public void testRouterContext() throws Exception {
try (
MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = mockStatic(ApplicationContextAwareUtils.class);
MockedStatic<MetadataContextHolder> mockedMetadataContextHolder = mockStatic(MetadataContextHolder.class)
) {
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
.thenReturn(testNamespaceAndService);
String calleeService = "calleeService";
HttpRequest request = new MockedHttpRequest("http://" + calleeService + "/user/get");
// mock local metadata
Map<String, String> localMetadata = new HashMap<>();
localMetadata.put("k1", "v1");
localMetadata.put("k2", "v2");
when(staticMetadataManager.getMergedStaticMetadata()).thenReturn(localMetadata);
Map<String, String> routerLabels = new HashMap<>(localMetadata);
// mock expression rule labels
Set<String> expressionKeys = new HashSet<>();
expressionKeys.add("${http.method}");
expressionKeys.add("${http.uri}");
when(routerRuleLabelResolver.getExpressionLabelKeys(testNamespaceAndService, testNamespaceAndService, calleeService)).thenReturn(expressionKeys);
routerLabels.putAll(SpringWebExpressionLabelUtils.resolve(request, expressionKeys));
// mock custom resolved from request
Map<String, String> customResolvedLabels = new HashMap<>();
customResolvedLabels.put("k2", "v22");
customResolvedLabels.put("k4", "v4");
when(routerLabelResolver.resolve(request, null, expressionKeys)).thenReturn(customResolvedLabels);
routerLabels.putAll(customResolvedLabels);
MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
// mock transitive metadata
Map<String, String> transitiveLabels = new HashMap<>();
transitiveLabels.put("k1", "v1");
transitiveLabels.put("k2", "v22");
when(metadataContext.getTransitiveMetadata()).thenReturn(transitiveLabels);
routerLabels.putAll(transitiveLabels);
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
RouterLabelRestTemplateInterceptor routerLabelRestTemplateInterceptor = new RouterLabelRestTemplateInterceptor(
Collections.singletonList(routerLabelResolver), staticMetadataManager, routerRuleLabelResolver, polarisContextProperties);
ClientHttpResponse mockedResponse = new MockClientHttpResponse(new byte[] {}, HttpStatus.OK);
when(clientHttpRequestExecution.execute(eq(request), any())).thenReturn(mockedResponse);
assertThat(routerLabelRestTemplateInterceptor.getOrder()).isEqualTo(Ordered.LOWEST_PRECEDENCE);
routerLabelRestTemplateInterceptor.intercept(request, null, clientHttpRequestExecution);
verify(staticMetadataManager).getMergedStaticMetadata();
verify(routerRuleLabelResolver).getExpressionLabelKeys(testNamespaceAndService, testNamespaceAndService, calleeService);
verify(routerLabelResolver).resolve(request, null, expressionKeys);
Map<String, String> headers = JacksonUtils.deserialize2Map(URLDecoder.decode(Objects.requireNonNull(request.getHeaders()
.get(RouterConstant.ROUTER_LABEL_HEADER)).get(0), "UTF-8"));
assertThat("v1").isEqualTo(headers.get("k1"));
assertThat("v22").isEqualTo(headers.get("k2"));
assertThat("v4").isEqualTo(headers.get("k4"));
assertThat("GET").isEqualTo(headers.get("${http.method}"));
assertThat("/user/get").isEqualTo(headers.get("${http.uri}"));
String encodedLabelsContent;
try {
encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(routerLabels), UTF_8);
}
catch (UnsupportedEncodingException e) {
throw new RuntimeException("unsupported charset exception " + UTF_8);
}
assertThat(mockedResponse.getHeaders().get(RouterConstant.ROUTER_LABEL_HEADER).get(0))
.isEqualTo(encodedLabelsContent);
}
public void testRouterLabel() throws Exception {
RouterLabelRestTemplateInterceptor routerLabelRestTemplateInterceptor = new RouterLabelRestTemplateInterceptor();
assertThat(routerLabelRestTemplateInterceptor.getOrder()).isEqualTo(OrderConstant.Client.RestTemplate.ROUTER_LABEL_INTERCEPTOR_ORDER);
String calleeService = "calleeService";
HttpRequest request = new MockedHttpRequest("http://" + calleeService + "/test/path?q1=a1");
ClientHttpResponse mockedResponse = new MockClientHttpResponse(new byte[] {}, HttpStatus.OK);
when(clientHttpRequestExecution.execute(eq(request), any())).thenReturn(mockedResponse);
routerLabelRestTemplateInterceptor.intercept(request, null, clientHttpRequestExecution);
// get message metadata container
MetadataContainer metadataContainer = MetadataContextHolder.get()
.getMetadataContainer(MetadataType.MESSAGE, false);
// method
assertThat(metadataContainer.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_METHOD)).isEqualTo(Request.HttpMethod.POST.toString());
// path
assertThat(metadataContainer.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo("/test/path");
// header
assertThat(metadataContainer.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, "uid")).isEqualTo("1000");
// cookie
assertThat(metadataContainer.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, "k1")).isEqualTo("v1");
// query
assertThat(metadataContainer.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, "q1")).isEqualTo("a1");
}
static class MockedHttpRequest implements HttpRequest {
private URI uri;
private final URI uri;
private HttpHeaders httpHeaders = new HttpHeaders();
private final HttpHeaders httpHeaders = new HttpHeaders();
MockedHttpRequest(String url) {
this.uri = URI.create(url);
this.httpHeaders.add("uid", "1000");
this.httpHeaders.add(HttpHeaderNames.COOKIE.toString(), "k1=v1");
}
@Override
public HttpMethod getMethod() {
return HttpMethod.POST;
}
@Override
public String getMethodValue() {
return HttpMethod.GET.name();
return HttpMethod.POST.toString();
}
@Override
public URI getURI() {
return uri;
@ -189,4 +119,9 @@ public class RouterLabelRestTemplateInterceptorTest {
return httpHeaders;
}
}
@SpringBootApplication
protected static class TestApplication {
}
}

@ -1,201 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router.scg;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import org.assertj.core.util.Lists;
import org.assertj.core.util.Sets;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.loadbalancer.support.SimpleObjectProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR;
/**
* Test for ${@link PolarisReactiveLoadBalancerClientFilter}.
*
* @author lepdou 2022-07-04
*/
@ExtendWith(MockitoExtension.class)
public class PolarisReactiveLoadBalancerClientFilterTest {
private static final String testNamespaceAndService = "testNamespaceAndService";
private static final String calleeService = "calleeService";
@Mock
private StaticMetadataManager staticMetadataManager;
@Mock
private SpringWebRouterLabelResolver routerLabelResolver;
@Mock
private RouterRuleLabelResolver routerRuleLabelResolver;
@Mock
private LoadBalancerClientFactory loadBalancerClientFactory;
@Mock
private GatewayLoadBalancerProperties gatewayLoadBalancerProperties;
@Mock
private LoadBalancerProperties loadBalancerProperties;
@Mock
private PolarisContextProperties polarisContextProperties;
@Test
public void testGenRouterHttpHeaders() throws UnsupportedEncodingException {
try (
MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = mockStatic(ApplicationContextAwareUtils.class);
MockedStatic<MetadataContextHolder> mockedMetadataContextHolder = mockStatic(MetadataContextHolder.class)
) {
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
.thenReturn(testNamespaceAndService);
MetadataContext metadataContext = mock(MetadataContext.class);
// mock transitive metadata
Map<String, String> transitiveLabels = new HashMap<>();
transitiveLabels.put("t1", "v1");
transitiveLabels.put("t2", "v2");
when(metadataContext.getTransitiveMetadata()).thenReturn(transitiveLabels);
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
PolarisReactiveLoadBalancerClientFilter filter = new PolarisReactiveLoadBalancerClientFilter(loadBalancerClientFactory,
gatewayLoadBalancerProperties, loadBalancerProperties, staticMetadataManager, routerRuleLabelResolver,
Collections.singletonList(routerLabelResolver), polarisContextProperties);
Map<String, String> localMetadata = new HashMap<>();
localMetadata.put("env", "blue");
when(staticMetadataManager.getMergedStaticMetadata()).thenReturn(localMetadata);
Set<String> expressionLabelKeys = Sets.newLinkedHashSet("${http.header.k1}", "${http.query.userid}");
when(routerRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString(), anyString())).thenReturn(expressionLabelKeys);
MockServerHttpRequest request = MockServerHttpRequest.get("/" + calleeService + "/users")
.header("k1", "v1")
.queryParam("userid", "zhangsan")
.build();
MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build();
Map<String, String> customMetadata = new HashMap<>();
customMetadata.put("k2", "v2");
when(routerLabelResolver.resolve(webExchange, expressionLabelKeys)).thenReturn(customMetadata);
HttpHeaders headers = filter.genRouterHttpHeaders(webExchange, calleeService);
assertThat(headers).isNotNull();
List<String> routerHeaders = headers.get(RouterConstant.ROUTER_LABEL_HEADER);
assertThat(routerHeaders).isNotNull();
Map<String, String> routerLabels = JacksonUtils.deserialize2Map(URLDecoder.decode(routerHeaders.get(0), UTF_8));
assertThat(routerLabels.get("${http.header.k1}")).isEqualTo("v1");
assertThat(routerLabels.get("${http.query.userid}")).isEqualTo("zhangsan");
assertThat(routerLabels.get("env")).isEqualTo("blue");
assertThat(routerLabels.get("t1")).isEqualTo("v1");
assertThat(routerLabels.get("t2")).isEqualTo("v2");
}
}
@Test
public void testFilter01() throws Exception {
try (
MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = mockStatic(ApplicationContextAwareUtils.class);
MockedStatic<MetadataContextHolder> mockedMetadataContextHolder = mockStatic(MetadataContextHolder.class)
) {
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
.thenReturn(testNamespaceAndService);
MetadataContext metadataContext = mock(MetadataContext.class);
// mock transitive metadata
Map<String, String> transitiveLabels = new HashMap<>();
transitiveLabels.put("t1", "v1");
transitiveLabels.put("t2", "v2");
when(metadataContext.getTransitiveMetadata()).thenReturn(transitiveLabels);
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
PolarisReactiveLoadBalancerClientFilter filter = new PolarisReactiveLoadBalancerClientFilter(loadBalancerClientFactory,
gatewayLoadBalancerProperties, loadBalancerProperties, staticMetadataManager, routerRuleLabelResolver,
Lists.list(routerLabelResolver), polarisContextProperties);
MockServerHttpRequest request = MockServerHttpRequest.get("/" + calleeService + "/users").build();
MockServerWebExchange exchange = new MockServerWebExchange.Builder(request).build();
// mock no lb
EmptyGatewayFilterChain chain = new EmptyGatewayFilterChain();
Mono<Void> ret = filter.filter(exchange, chain);
assertThat(ret).isEqualTo(Mono.empty());
// mock with lb
exchange = new MockServerWebExchange.Builder(request).build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, new URI("https://" + calleeService + ":8091"));
exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, "lb");
NoopServiceInstanceListSupplier serviceInstanceListSupplier = new NoopServiceInstanceListSupplier();
RoundRobinLoadBalancer roundRobinLoadBalancer = new RoundRobinLoadBalancer(new SimpleObjectProvider<>(serviceInstanceListSupplier), calleeService);
when(loadBalancerClientFactory.getInstance(calleeService, ReactorServiceInstanceLoadBalancer.class)).thenReturn(roundRobinLoadBalancer);
filter.filter(exchange, chain);
}
}
static class EmptyGatewayFilterChain implements GatewayFilterChain {
@Override
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.empty();
}
}
}

@ -0,0 +1,95 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.router.scg;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.polaris.metadata.core.MessageMetadataContainer;
import com.tencent.polaris.metadata.core.MetadataContainer;
import com.tencent.polaris.metadata.core.MetadataType;
import feign.Request;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpCookie;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER;
/**
* Test for ${@link RouterLabelGlobalFilter}.
*
* @author Haotian Zhang
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = RouterLabelGlobalFilterTest.TestApplication.class,
properties = {"spring.cloud.polaris.namespace=test", "spring.application.name=test", "spring.main.web-application-type=reactive"})
public class RouterLabelGlobalFilterTest {
@Test
public void testRouterLabel() {
RouterLabelGlobalFilter routerLabelGlobalFilter = new RouterLabelGlobalFilter();
assertThat(routerLabelGlobalFilter.getOrder())
.isEqualTo(LOAD_BALANCER_CLIENT_FILTER_ORDER - 1);
MockServerHttpRequest request = MockServerHttpRequest.post("/test/path")
.header("uid", "1000")
.cookie(new HttpCookie("k1", "v1"))
.queryParam("q1", "a1")
.build();
MockServerWebExchange mockWebExchange = new MockServerWebExchange.Builder(request).build();
routerLabelGlobalFilter.filter(mockWebExchange, new EmptyGatewayFilterChain());
// get message metadata container
MetadataContainer metadataContainer = MetadataContextHolder.get()
.getMetadataContainer(MetadataType.MESSAGE, false);
// method
assertThat(metadataContainer.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_METHOD)).isEqualTo(Request.HttpMethod.POST.toString());
// path
assertThat(metadataContainer.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo("/test/path");
// header
assertThat(metadataContainer.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, "uid")).isEqualTo("1000");
// cookie
assertThat(metadataContainer.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, "k1")).isEqualTo("v1");
// query
assertThat(metadataContainer.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, "q1")).isEqualTo("a1");
}
static class EmptyGatewayFilterChain implements GatewayFilterChain {
@Override
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.empty();
}
}
@SpringBootApplication
protected static class TestApplication {
}
}

@ -23,7 +23,6 @@ import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.rpc.enhancement.transformer.PolarisInstanceTransformer;
import com.tencent.polaris.api.pojo.DefaultInstance;
import com.tencent.polaris.api.pojo.Instance;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
@ -37,25 +36,20 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class PolarisInstanceTransformerTest {
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
@BeforeAll
public static void beforeAll() {
mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.namespace"))
.thenReturn("default");
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.service"))
.thenReturn("test");
}
@Test
public void test() {
PolarisInstanceTransformer polarisInstanceTransformer = new PolarisInstanceTransformer();
DefaultInstance instance = new DefaultInstance();
instance.setZone("zone");
PolarisServiceInstance polarisServiceInstance = new PolarisServiceInstance(instance);
Instance instance1 = polarisInstanceTransformer.transform(polarisServiceInstance);
assertThat(instance1.getZone()).isEqualTo("zone");
try (
MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.namespace"))
.thenReturn("default");
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.service"))
.thenReturn("test");
PolarisInstanceTransformer polarisInstanceTransformer = new PolarisInstanceTransformer();
DefaultInstance instance = new DefaultInstance();
instance.setZone("zone");
PolarisServiceInstance polarisServiceInstance = new PolarisServiceInstance(instance);
Instance instance1 = polarisInstanceTransformer.transform(polarisServiceInstance);
assertThat(instance1.getZone()).isEqualTo("zone");
}
}
}

@ -62,9 +62,9 @@ public final class MetadataConstant {
public static final String CUSTOM_DISPOSABLE_METADATA = "SCT-CUSTOM-DISPOSABLE-METADATA";
/**
* System Metadata.
* Application Metadata.
*/
public static final String SYSTEM_METADATA = "SCT-SYSTEM-METADATA";
public static final String APPLICATION_METADATA = "SCT-APPLICATION-METADATA";
/**
* Metadata context.

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.tsf.consts;
package com.tencent.cloud.common.constant;
/**
* .

@ -56,6 +56,16 @@ public class MetadataContext extends com.tencent.polaris.metadata.core.manager.M
*/
public static final String FRAGMENT_UPSTREAM_DISPOSABLE = "upstream-disposable";
/**
* disposable Context.
*/
public static final String FRAGMENT_APPLICATION = "application";
/**
* upstream disposable Context.
*/
public static final String FRAGMENT_UPSTREAM_APPLICATION = "upstream-application";
/**
* the key of the header(key) list needed to be transmitted from upstream to downstream.
*/
@ -93,8 +103,6 @@ public class MetadataContext extends com.tencent.polaris.metadata.core.manager.M
if (!StringUtils.hasText(namespace)) {
LOG.error("namespace should not be blank. please configure spring.cloud.polaris.namespace or "
+ "spring.cloud.polaris.discovery.namespace");
throw new RuntimeException("namespace should not be blank. please configure spring.cloud.polaris.namespace or "
+ "spring.cloud.polaris.discovery.namespace");
}
namespace = DiscoveryUtil.rewriteNamespace(namespace);
LOCAL_NAMESPACE = namespace;
@ -109,8 +117,6 @@ public class MetadataContext extends com.tencent.polaris.metadata.core.manager.M
if (!StringUtils.hasText(serviceName)) {
LOG.error("service name should not be blank. please configure spring.cloud.polaris.service or "
+ "spring.cloud.polaris.discovery.service or spring.application.name");
throw new RuntimeException("service name should not be blank. please configure spring.cloud.polaris.service or "
+ "spring.cloud.polaris.discovery.service or spring.application.name");
}
serviceName = DiscoveryUtil.rewriteServiceId(serviceName);
LOCAL_SERVICE = serviceName;
@ -120,8 +126,8 @@ public class MetadataContext extends com.tencent.polaris.metadata.core.manager.M
super(MetadataConstant.POLARIS_TRANSITIVE_HEADER_PREFIX);
}
private Map<String, String> getMetadataAsMap(MetadataType metadataType, TransitiveType transitiveType, boolean downstream) {
MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream);
private Map<String, String> getMetadataAsMap(MetadataType metadataType, TransitiveType transitiveType, boolean caller) {
MetadataContainer metadataContainer = getMetadataContainer(metadataType, caller);
Map<String, String> values = new HashMap<>();
metadataContainer.iterateMetadataValues(new BiConsumer<String, MetadataValue>() {
@Override
@ -144,8 +150,8 @@ public class MetadataContext extends com.tencent.polaris.metadata.core.manager.M
}
}
private Map<String, String> getMapMetadataAsMap(MetadataType metadataType, String mapKey, TransitiveType transitiveType, boolean downstream) {
MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream);
private Map<String, String> getMapMetadataAsMap(MetadataType metadataType, String mapKey, TransitiveType transitiveType, boolean caller) {
MetadataContainer metadataContainer = getMetadataContainer(metadataType, caller);
Map<String, String> values = new HashMap<>();
MetadataValue metadataValue = metadataContainer.getMetadataValue(mapKey);
if (!(metadataValue instanceof MetadataMapValue)) {
@ -167,8 +173,8 @@ public class MetadataContext extends com.tencent.polaris.metadata.core.manager.M
}
private void putMapMetadataAsMap(MetadataType metadataType, String mapKey,
TransitiveType transitiveType, boolean downstream, Map<String, String> values) {
MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream);
TransitiveType transitiveType, boolean caller, Map<String, String> values) {
MetadataContainer metadataContainer = getMetadataContainer(metadataType, caller);
for (Map.Entry<String, String> entry : values.entrySet()) {
metadataContainer.putMetadataMapValue(mapKey, entry.getKey(), entry.getValue(), transitiveType);
}
@ -178,10 +184,22 @@ public class MetadataContext extends com.tencent.polaris.metadata.core.manager.M
return getFragmentContext(FRAGMENT_DISPOSABLE);
}
public void setDisposableMetadata(Map<String, String> disposableMetadata) {
putFragmentContext(FRAGMENT_DISPOSABLE, Collections.unmodifiableMap(disposableMetadata));
}
public Map<String, String> getTransitiveMetadata() {
return getFragmentContext(FRAGMENT_TRANSITIVE);
}
public void setTransitiveMetadata(Map<String, String> transitiveMetadata) {
putFragmentContext(FRAGMENT_TRANSITIVE, Collections.unmodifiableMap(transitiveMetadata));
}
public Map<String, String> getApplicationMetadata() {
return getFragmentContext(FRAGMENT_APPLICATION);
}
public Map<String, String> getCustomMetadata() {
Map<String, String> transitiveMetadata = this.getTransitiveMetadata();
Map<String, String> disposableMetadata = this.getDisposableMetadata();
@ -227,14 +245,6 @@ public class MetadataContext extends com.tencent.polaris.metadata.core.manager.M
metadataContainer.putMetadataMapObjectValue(FRAGMENT_LB_METADATA, key, value);
}
public void setTransitiveMetadata(Map<String, String> transitiveMetadata) {
putFragmentContext(FRAGMENT_TRANSITIVE, Collections.unmodifiableMap(transitiveMetadata));
}
public void setDisposableMetadata(Map<String, String> disposableMetadata) {
putFragmentContext(FRAGMENT_DISPOSABLE, Collections.unmodifiableMap(disposableMetadata));
}
public void setUpstreamDisposableMetadata(Map<String, String> upstreamDisposableMetadata) {
putFragmentContext(FRAGMENT_UPSTREAM_DISPOSABLE, Collections.unmodifiableMap(upstreamDisposableMetadata));
}
@ -255,6 +265,10 @@ public class MetadataContext extends com.tencent.polaris.metadata.core.manager.M
return getMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, false);
case FRAGMENT_UPSTREAM_DISPOSABLE:
return getMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, true);
case FRAGMENT_APPLICATION:
return getMetadataAsMap(MetadataType.APPLICATION, TransitiveType.DISPOSABLE, false);
case FRAGMENT_UPSTREAM_APPLICATION:
return getMetadataAsMap(MetadataType.APPLICATION, TransitiveType.DISPOSABLE, true);
case FRAGMENT_RAW_TRANSHEADERS:
return getMapMetadataAsMap(MetadataType.CUSTOM, FRAGMENT_RAW_TRANSHEADERS, TransitiveType.NONE, false);
case FRAGMENT_RAW_TRANSHEADERS_KV:
@ -289,6 +303,12 @@ public class MetadataContext extends com.tencent.polaris.metadata.core.manager.M
case FRAGMENT_UPSTREAM_DISPOSABLE:
putMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, true, context);
break;
case FRAGMENT_APPLICATION:
putMetadataAsMap(MetadataType.APPLICATION, TransitiveType.DISPOSABLE, false, context);
break;
case FRAGMENT_UPSTREAM_APPLICATION:
putMetadataAsMap(MetadataType.APPLICATION, TransitiveType.DISPOSABLE, true, context);
break;
case FRAGMENT_RAW_TRANSHEADERS:
putMapMetadataAsMap(MetadataType.CUSTOM, FRAGMENT_RAW_TRANSHEADERS, TransitiveType.NONE, false, context);
break;

@ -22,7 +22,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.polaris.metadata.core.MessageMetadataContainer;
import com.tencent.polaris.metadata.core.MetadataContainer;
@ -43,8 +42,6 @@ import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_UPSTREA
*/
public final class MetadataContextHolder {
private static MetadataLocalProperties metadataLocalProperties;
private static StaticMetadataManager staticMetadataManager;
static {
@ -60,28 +57,38 @@ public final class MetadataContextHolder {
private static MetadataContext createMetadataManager() {
MetadataContext metadataManager = new MetadataContext();
if (metadataLocalProperties == null) {
metadataLocalProperties = ApplicationContextAwareUtils.getApplicationContext()
.getBean(MetadataLocalProperties.class);
}
if (staticMetadataManager == null) {
staticMetadataManager = ApplicationContextAwareUtils.getApplicationContext()
.getBean(StaticMetadataManager.class);
}
// local custom metadata
MetadataContainer metadataContainer = metadataManager.getMetadataContainer(MetadataType.CUSTOM, false);
Map<String, String> mergedStaticMetadata = staticMetadataManager.getMergedStaticMetadata();
for (Map.Entry<String, String> entry : mergedStaticMetadata.entrySet()) {
metadataContainer.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.NONE);
}
// local custom transitive metadata
Map<String, String> mergedStaticTransitiveMetadata = staticMetadataManager.getMergedStaticTransitiveMetadata();
for (Map.Entry<String, String> entry : mergedStaticTransitiveMetadata.entrySet()) {
metadataContainer.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.PASS_THROUGH);
}
// local custom disposable metadata
Map<String, String> mergedStaticDisposableMetadata = staticMetadataManager.getMergedStaticDisposableMetadata();
for (Map.Entry<String, String> entry : mergedStaticDisposableMetadata.entrySet()) {
metadataContainer.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.DISPOSABLE);
}
// local trans header
if (StringUtils.hasText(staticMetadataManager.getTransHeader())) {
String transHeader = staticMetadataManager.getTransHeader();
metadataContainer.putMetadataMapValue(MetadataContext.FRAGMENT_RAW_TRANSHEADERS, transHeader, "", TransitiveType.NONE);
}
// local application disposable metadata
MetadataContainer applicationMetadataContainer = metadataManager.getMetadataContainer(MetadataType.APPLICATION, false);
Map<String, String> mergedApplicationMetadata = staticMetadataManager.getMergedStaticMetadata();
for (Map.Entry<String, String> entry : mergedApplicationMetadata.entrySet()) {
applicationMetadataContainer.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.DISPOSABLE);
}
return metadataManager;
}
@ -133,24 +140,36 @@ public final class MetadataContextHolder {
* Save metadata map to thread local.
*
* @param dynamicTransitiveMetadata custom metadata collection
* @param dynamicDisposableMetadata custom disposable metadata connection
* @param dynamicDisposableMetadata custom disposable metadata collection
* @param dynamicApplicationMetadata application metadata collection
* @param callerMetadataProvider caller metadata provider
*/
public static void init(Map<String, String> dynamicTransitiveMetadata, Map<String, String> dynamicDisposableMetadata,
MetadataProvider callerMetadataProvider) {
Map<String, String> dynamicApplicationMetadata, MetadataProvider callerMetadataProvider) {
com.tencent.polaris.metadata.core.manager.MetadataContextHolder.refresh(metadataManager -> {
// caller transitive metadata to local custom transitive metadata
MetadataContainer metadataContainerUpstream = metadataManager.getMetadataContainer(MetadataType.CUSTOM, false);
if (!CollectionUtils.isEmpty(dynamicTransitiveMetadata)) {
for (Map.Entry<String, String> entry : dynamicTransitiveMetadata.entrySet()) {
metadataContainerUpstream.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.PASS_THROUGH);
}
}
// caller disposable metadata to caller custom disposable metadata
MetadataContainer metadataContainerDownstream = metadataManager.getMetadataContainer(MetadataType.CUSTOM, true);
if (!CollectionUtils.isEmpty(dynamicDisposableMetadata)) {
for (Map.Entry<String, String> entry : dynamicDisposableMetadata.entrySet()) {
metadataContainerDownstream.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.DISPOSABLE);
}
}
// caller application metadata to caller application disposable metadata
MetadataContainer applicationMetadataContainerDownstream = metadataManager.getMetadataContainer(MetadataType.APPLICATION, true);
if (!CollectionUtils.isEmpty(dynamicApplicationMetadata)) {
for (Map.Entry<String, String> entry : dynamicApplicationMetadata.entrySet()) {
applicationMetadataContainerDownstream.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.DISPOSABLE);
}
}
// caller message metadata
if (callerMetadataProvider != null) {
MessageMetadataContainer callerMessageContainer = metadataManager.getMetadataContainer(MetadataType.MESSAGE, true);
callerMessageContainer.setMetadataProvider(callerMetadataProvider);

@ -261,7 +261,8 @@ public class StaticMetadataManager {
List<InstanceMetadataProvider> instanceMetadataProviders) {
// resolve region info
if (!CollectionUtils.isEmpty(instanceMetadataProviders)) {
Set<String> providerRegions = instanceMetadataProviders.stream().map(InstanceMetadataProvider::getRegion).filter(region -> !StringUtils.isBlank(region)).collect(Collectors.toSet());
Set<String> providerRegions = instanceMetadataProviders.stream().map(InstanceMetadataProvider::getRegion)
.filter(region -> !StringUtils.isBlank(region)).collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(providerRegions)) {
if (providerRegions.size() > 1) {
throw new IllegalArgumentException("Multiple Regions Provided in InstanceMetadataProviders");
@ -278,7 +279,8 @@ public class StaticMetadataManager {
// resolve zone info
if (!CollectionUtils.isEmpty(instanceMetadataProviders)) {
Set<String> providerZones = instanceMetadataProviders.stream().map(InstanceMetadataProvider::getZone).filter(zone -> !StringUtils.isBlank(zone)).collect(Collectors.toSet());
Set<String> providerZones = instanceMetadataProviders.stream().map(InstanceMetadataProvider::getZone)
.filter(zone -> !StringUtils.isBlank(zone)).collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(providerZones)) {
if (providerZones.size() > 1) {
throw new IllegalArgumentException("Multiple Zones Provided in InstanceMetadataProviders");
@ -295,7 +297,8 @@ public class StaticMetadataManager {
// resolve campus info
if (!CollectionUtils.isEmpty(instanceMetadataProviders)) {
Set<String> providerCampus = instanceMetadataProviders.stream().map(InstanceMetadataProvider::getCampus).filter(campus -> !StringUtils.isBlank(campus)).collect(Collectors.toSet());
Set<String> providerCampus = instanceMetadataProviders.stream().map(InstanceMetadataProvider::getCampus)
.filter(campus -> !StringUtils.isBlank(campus)).collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(providerCampus)) {
if (providerCampus.size() > 1) {
throw new IllegalArgumentException("Multiple Campus Provided in InstanceMetadataProviders");

@ -41,7 +41,7 @@ public class PolarisMetadataEndpoint {
@ReadOperation
public Map<String, Object> metadata() {
Map<String, Object> result = new HashMap<>();
Map<String, Object> result = new HashMap<>();
result.put("Env", staticMetadataManager.getAllEnvMetadata());
result.put("EnvTransitive", staticMetadataManager.getEnvTransitiveMetadata());
result.put("ConfigTransitive", staticMetadataManager.getConfigTransitiveMetadata());

@ -18,10 +18,12 @@
package com.tencent.cloud.common.pojo;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.utils.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.client.DefaultServiceInstance;
@ -40,7 +42,13 @@ public class PolarisServiceInstance implements ServiceInstance {
private final String scheme;
private final Map<String, String> serviceMetadata;
public PolarisServiceInstance(Instance instance) {
this(instance, null);
}
public PolarisServiceInstance(Instance instance, Map<String, String> metadata) {
this.instance = instance;
this.isSecure = StringUtils.equalsIgnoreCase(instance.getProtocol(), "https");
if (isSecure) {
@ -49,6 +57,10 @@ public class PolarisServiceInstance implements ServiceInstance {
else {
scheme = "http";
}
this.serviceMetadata = new HashMap<>();
if (CollectionUtils.isNotEmpty(metadata)) {
this.serviceMetadata.putAll(metadata);
}
}
public Instance getPolarisInstance() {
@ -95,6 +107,10 @@ public class PolarisServiceInstance implements ServiceInstance {
return this.scheme;
}
public Map<String, String> getServiceMetadata() {
return serviceMetadata;
}
/**
* To fix loadbalancer not working bug when importing spring-retry.
* @param o object
@ -116,4 +132,14 @@ public class PolarisServiceInstance implements ServiceInstance {
public int hashCode() {
return Objects.hash(instance, scheme);
}
@Override
public String toString() {
return "PolarisServiceInstance{" +
"instance=" + instance +
", isSecure=" + isSecure +
", scheme='" + scheme + '\'' +
", serviceMetadata=" + serviceMetadata +
'}';
}
}

@ -1,73 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.common.tsf;
/**
* Constant for TSF.
*
* @author Haotian Zhang
*/
public final class TsfConstant {
/**
* tsf application id.
*/
public static String TSF_APPLICATION_ID = "TSF_APPLICATION_ID";
/**
* tsf program version.
*/
public static String TSF_PROG_VERSION = "TSF_PROG_VERSION";
/**
* tsf group id.
*/
public static String TSF_GROUP_ID = "TSF_GROUP_ID";
/**
* tsf namespace id.
*/
public static String TSF_NAMESPACE_ID = "TSF_NAMESPACE_ID";
/**
* tsf instance id.
*/
public static String TSF_INSTNACE_ID = "TSF_INSTNACE_ID";
/**
* tsf region.
*/
public static String TSF_REGION = "TSF_REGION";
/**
* tsf zone.
*/
public static String TSF_ZONE = "TSF_ZONE";
/**
* tsf SDK version.
*/
public static String TSF_SDK_VERSION = "TSF_SDK_VERSION";
/**
* tsf tags.
*/
public static String TSF_TAGS = "TSF_TAGS";
private TsfConstant() {
}
}

@ -137,7 +137,7 @@ public final class ExpressionLabelUtils {
}
public static String getQueryValue(String queryString, String queryKey) {
return getQueryValue(queryString, queryKey, StringUtils.EMPTY);
return getQueryValue(queryString, queryKey, null);
}
public static String getQueryValue(String queryString, String queryKey, String defaultValue) {
@ -159,31 +159,31 @@ public final class ExpressionLabelUtils {
public static String getFirstValue(Map<String, Collection<String>> valueMaps, String key) {
if (CollectionUtils.isEmpty(valueMaps)) {
return StringUtils.EMPTY;
return null;
}
Collection<String> values = valueMaps.get(key);
if (CollectionUtils.isEmpty(values)) {
return StringUtils.EMPTY;
return null;
}
for (String value : values) {
return value;
}
return StringUtils.EMPTY;
return null;
}
public static String getCookieFirstValue(Map<String, Collection<String>> valueMaps, String key) {
if (CollectionUtils.isEmpty(valueMaps)) {
return StringUtils.EMPTY;
return null;
}
Collection<String> values = valueMaps.get(HttpHeaderNames.COOKIE.toString());
if (CollectionUtils.isEmpty(values)) {
return StringUtils.EMPTY;
return null;
}
for (String value : values) {
@ -195,6 +195,6 @@ public final class ExpressionLabelUtils {
}
}
}
return StringUtils.EMPTY;
return null;
}
}

@ -75,7 +75,7 @@ public final class SpringWebExpressionLabelUtils {
labels.put(labelKey, getCookieValue(exchange.getRequest(), cookieKey));
}
else if (ExpressionLabelUtils.isMethodLabel(labelKey)) {
labels.put(labelKey, exchange.getRequest().getMethodValue());
labels.put(labelKey, exchange.getRequest().getMethod().toString());
}
else if (ExpressionLabelUtils.isUriLabel(labelKey)) {
labels.put(labelKey, exchange.getRequest().getURI().getPath());
@ -118,7 +118,7 @@ public final class SpringWebExpressionLabelUtils {
labels.put(labelKey, getCookieValue(request, cookieKey));
}
else if (ExpressionLabelUtils.isMethodLabel(labelKey)) {
labels.put(labelKey, request.getMethodValue());
labels.put(labelKey, request.getMethod().toString());
}
else if (ExpressionLabelUtils.isUriLabel(labelKey)) {
labels.put(labelKey, request.getURI().getPath());
@ -129,7 +129,7 @@ public final class SpringWebExpressionLabelUtils {
}
public static String getHeaderValue(ServerHttpRequest request, String key) {
return getHeaderValue(request, key, StringUtils.EMPTY);
return getHeaderValue(request, key, null);
}
public static String getHeaderValue(ServerHttpRequest request, String key, String defaultValue) {
@ -141,7 +141,7 @@ public final class SpringWebExpressionLabelUtils {
}
public static String getQueryValue(ServerHttpRequest request, String key) {
return getQueryValue(request, key, StringUtils.EMPTY);
return getQueryValue(request, key, null);
}
public static String getQueryValue(ServerHttpRequest request, String key, String defaultValue) {
@ -157,7 +157,7 @@ public final class SpringWebExpressionLabelUtils {
}
public static String getCookieValue(ServerHttpRequest request, String key) {
return getCookieValue(request, key, StringUtils.EMPTY);
return getCookieValue(request, key, null);
}
public static String getCookieValue(ServerHttpRequest request, String key, String defaultValue) {
@ -181,7 +181,7 @@ public final class SpringWebExpressionLabelUtils {
public static String getCookieValue(HttpRequest request, String key) {
String first = request.getHeaders().getFirst(HttpHeaders.COOKIE);
if (StringUtils.isEmpty(first)) {
return StringUtils.EMPTY;
return null;
}
String[] cookieArray = StringUtils.split(first, ";");
for (String cookieItem : cookieArray) {
@ -190,6 +190,6 @@ public final class SpringWebExpressionLabelUtils {
return cookieKv[1];
}
}
return StringUtils.EMPTY;
return null;
}
}

@ -63,7 +63,7 @@ public class MetadataContextHolderTest {
customMetadata.put("a", "1");
customMetadata.put("b", "22");
customMetadata.put("c", "3");
MetadataContextHolder.init(customMetadata, new HashMap<>(), null);
MetadataContextHolder.init(customMetadata, new HashMap<>(), new HashMap<>(), null);
metadataContext = MetadataContextHolder.get();
customMetadata = metadataContext.getTransitiveMetadata();
Assertions.assertThat(customMetadata.get("a")).isEqualTo("1");

@ -123,4 +123,10 @@ public class QuickstartCalleeController {
LOG.info("Quickstart Callee Service [{}:{}] is detected right.", ip, port);
return String.format("Quickstart Callee Service [%s:%s] is detected right.", ip, port);
}
@GetMapping("/test/{num}/echo")
public String test(@PathVariable int num) {
LOG.info("Quickstart Callee Service [%s] is detected right.", num);
return String.format("Quickstart Callee Service [%s] is detected right.", num);
}
}

@ -18,18 +18,24 @@
package com.tencent.cloud.quickstart.caller;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.polaris.api.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@ -76,8 +82,25 @@ public class QuickstartCallerController {
* @return information of callee
*/
@GetMapping("/rest")
public String rest() {
return restTemplate.getForObject("http://QuickstartCalleeService/quickstart/callee/info", String.class);
public ResponseEntity<String> rest(@RequestHeader Map<String, String> headerMap) {
String url = "http://QuickstartCalleeService/quickstart/callee/info";
HttpHeaders headers = new HttpHeaders();
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
if (StringUtils.isNotBlank(entry.getKey()) && StringUtils.isNotBlank(entry.getValue())
&& !entry.getKey().contains("sct-")
&& !entry.getKey().contains("SCT-")
&& !entry.getKey().contains("polaris-")
&& !entry.getKey().contains("POLARIS-")) {
headers.add(entry.getKey(), entry.getValue());
}
}
// 创建 HttpEntity 实例并传入 HttpHeaders
HttpEntity<String> entity = new HttpEntity<>(headers);
// 使用 exchange 方法发送 GET 请求,并获取响应
return restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
}
/**

@ -1,54 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.quickstart.caller.router;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.google.gson.Gson;
import com.tencent.cloud.polaris.router.spi.FeignRouterLabelResolver;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
/**
*
* Customize the business tag information obtained from the request
*
*@author lepdou 2022-05-12
*/
@Component
public class CustomRouterLabelResolver implements FeignRouterLabelResolver {
private final Gson gson = new Gson();
@Override
public Map<String, String> resolve(RequestTemplate requestTemplate, Set<String> expressionLabelKeys) {
Map<String, String> labels = new HashMap<>();
labels.put("label1", "value1");
return labels;
}
@Override
public int getOrder() {
return 0;
}
}

@ -27,6 +27,11 @@
<artifactId>spring-cloud-starter-tencent-polaris-contract</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>

@ -22,6 +22,7 @@ import java.util.Map;
import com.tencent.cloud.tsf.demo.consumer.proxy.ProviderDemoService;
import com.tencent.cloud.tsf.demo.consumer.proxy.ProviderService;
import com.tencent.polaris.api.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.tsf.core.context.TsfContext;
@ -29,6 +30,7 @@ import org.springframework.tsf.core.entity.Tag;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@ -42,7 +44,12 @@ public class ConsumerController {
private ProviderDemoService providerDemoService;
@RequestMapping(value = "/echo-rest/{str}", method = RequestMethod.GET)
public String restProvider(@PathVariable String str) {
public String restProvider(@PathVariable String str,
@RequestParam(required = false) String tagName,
@RequestParam(required = false) String tagValue) {
if (StringUtils.isNotBlank(tagName)) {
TsfContext.putTag(tagName, tagValue);
}
TsfContext.putTag("operation", "rest");
Map<String, String> mTags = new HashMap<>();
mTags.put("rest-trace-key1", "value1");
@ -52,7 +59,12 @@ public class ConsumerController {
}
@RequestMapping(value = "/echo-feign/{str}", method = RequestMethod.GET)
public String feignProvider(@PathVariable String str) {
public String feignProvider(@PathVariable String str,
@RequestParam(required = false) String tagName,
@RequestParam(required = false) String tagValue) {
if (StringUtils.isNotBlank(tagName)) {
TsfContext.putTag(tagName, tagValue);
}
TsfContext.putTag("operation", "feign");
Map<String, String> mTags = new HashMap<>();
mTags.put("feign-trace-key1", "value1");
@ -62,7 +74,12 @@ public class ConsumerController {
}
@RequestMapping(value = "/echo-feign-url/{str}", method = RequestMethod.GET)
public String feignUrlProvider(@PathVariable String str) {
public String feignUrlProvider(@PathVariable String str,
@RequestParam(required = false) String tagName,
@RequestParam(required = false) String tagValue) {
if (StringUtils.isNotBlank(tagName)) {
TsfContext.putTag(tagName, tagValue);
}
TsfContext.putTag("operation", "feignUrl");
Map<String, String> mTags = new HashMap<>();
mTags.put("feignUrl-trace-key1", "value1");

@ -21,11 +21,11 @@ package com.tencent.cloud.plugin.trace.tsf;
import java.util.HashMap;
import java.util.Map;
import com.tencent.cloud.common.tsf.TsfConstant;
import com.tencent.cloud.plugin.trace.SpanAttributesProvider;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext;
import com.tencent.polaris.api.utils.CollectionUtils;
import com.tencent.polaris.api.utils.StringUtils;
import com.tencent.polaris.metadata.core.constant.TsfMetadataConstants;
import org.springframework.cloud.client.ServiceInstance;
@ -39,11 +39,11 @@ public class TsfSpanAttributesProvider implements SpanAttributesProvider {
}
ServiceInstance targetServiceInstance = context.getTargetServiceInstance();
if (null != targetServiceInstance && CollectionUtils.isNotEmpty(targetServiceInstance.getMetadata())) {
String nsId = targetServiceInstance.getMetadata().get(TsfConstant.TSF_NAMESPACE_ID);
String nsId = targetServiceInstance.getMetadata().get(TsfMetadataConstants.TSF_NAMESPACE_ID);
attributes.put("remote.namespace-id", StringUtils.defaultString(nsId));
String groupId = targetServiceInstance.getMetadata().get(TsfConstant.TSF_GROUP_ID);
String groupId = targetServiceInstance.getMetadata().get(TsfMetadataConstants.TSF_GROUP_ID);
attributes.put("remote.group-id", StringUtils.defaultString(groupId));
String applicationId = targetServiceInstance.getMetadata().get(TsfConstant.TSF_APPLICATION_ID);
String applicationId = targetServiceInstance.getMetadata().get(TsfMetadataConstants.TSF_APPLICATION_ID);
attributes.put("remote.application-id", StringUtils.defaultString(applicationId));
}
return attributes;

@ -173,6 +173,16 @@
<artifactId>polaris-router-factory</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-circuitbreaker-factory</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-ratelimit-factory</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>loadbalancer-random</artifactId>

@ -0,0 +1,80 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.context.tsf;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.tencent.cloud.common.util.inet.PolarisInetUtils;
import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties;
import org.springframework.util.StringUtils;
/**
*
*
* @author Haotian Zhang
*/
public final class TsfUtils {
/**
* IPV4.
*/
public static String TSF_ADDRESS_IPV4 = "TSF_ADDRESS_IPV4";
/**
* IPV6.
*/
public static String TSF_ADDRESS_IPV6 = "TSF_ADDRESS_IPV6";
private TsfUtils() {
}
public static List<String> createTags(TsfCoreProperties properties) {
List<String> tags = new LinkedList<>(properties.getTags());
if (StringUtils.hasText(properties.getInstanceZone())) {
tags.add(properties.getDefaultZoneMetadataName() + "=" + properties.getInstanceZone());
}
if (StringUtils.hasText(properties.getInstanceGroup())) {
tags.add("group=" + properties.getInstanceGroup());
}
//store the secure flag in the tags so that clients will be able to figure out whether to use http or https automatically
tags.add("secure=" + properties.getScheme().equalsIgnoreCase("https"));
return tags;
}
public static Map<String, String> appendMetaIpAddress(Map<String, String> meta) {
if (meta == null) {
return null;
}
String ipv4Address = PolarisInetUtils.getIpString(false);
if (ipv4Address != null) {
meta.put(TSF_ADDRESS_IPV4, ipv4Address);
}
String ipv6Address = PolarisInetUtils.getIpString(true);
if (ipv6Address != null) {
meta.put(TSF_ADDRESS_IPV6, ipv6Address);
}
return meta;
}
}

@ -17,6 +17,9 @@
package com.tencent.cloud.polaris.context.tsf.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ -37,6 +40,12 @@ public class TsfCoreProperties {
@Value("${tse_polaris_enable:false}")
private boolean tsePolarisEnable = false;
/**
* Unique service instance id.
*/
@Value("${tsf_instance_id:${spring.cloud.consul.discovery.instanceId:${SPRING_CLOUD_CONSUL_DISCOVERY_INSTANCEID:}}}")
private String instanceId;
/**
* tsf service consul registration tags.
* <p>
@ -53,12 +62,67 @@ public class TsfCoreProperties {
@Value("${tsf_group_id:}")
private String tsfGroupId;
/**
* tsf service consul registration tags.
*
* progVersion
*/
@Value("${tsf_prog_version:}")
private String tsfProgVersion;
/**
* 使.
*/
@Value("${tsf_namespace_id:}")
private String tsfNamespaceId;
/**
* tsf service consul registration tags.
*
*
*/
@Value("${tsf_region:}")
private String tsfRegion;
/**
* tsf service consul registration tags.
*
*
*/
@Value("${tsf_zone:}")
private String tsfZone;
/**
* Tags to use when registering service.
*/
@Value("${tsf.discovery.tags:}")
private List<String> tags = new ArrayList<>();
/**
* Service instance zone.
*/
@Value("${tsf.discovery.instanceZone:}")
private String instanceZone;
/**
* Service instance group.
*/
@Value("${tsf.discovery.instanceGroup:}")
private String instanceGroup;
/**
* Service instance zone comes from metadata.
* This allows changing the metadata tag name.
*/
@Value("${tsf.discovery.defaultZoneMetadataName:zone}")
private String defaultZoneMetadataName = "zone";
/**
* Whether to register an http or https service.
*/
@Value("${tsf.discovery.scheme:http}")
private String scheme = "http";
public String getTsePolarisIp() {
return tsePolarisIp;
}
@ -83,6 +147,14 @@ public class TsfCoreProperties {
this.tsePolarisEnable = tsePolarisEnable;
}
public String getInstanceId() {
return instanceId;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
public String getTsfApplicationId() {
return tsfApplicationId;
}
@ -99,6 +171,14 @@ public class TsfCoreProperties {
this.tsfGroupId = tsfGroupId;
}
public String getTsfProgVersion() {
return tsfProgVersion;
}
public void setTsfProgVersion(final String tsfProgVersion) {
this.tsfProgVersion = tsfProgVersion;
}
public String getTsfNamespaceId() {
return tsfNamespaceId;
}
@ -107,15 +187,80 @@ public class TsfCoreProperties {
this.tsfNamespaceId = tsfNamespaceId;
}
public String getTsfRegion() {
return tsfRegion;
}
public void setTsfRegion(final String tsfRegion) {
this.tsfRegion = tsfRegion;
}
public String getTsfZone() {
return tsfZone;
}
public void setTsfZone(final String tsfZone) {
this.tsfZone = tsfZone;
}
public List<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
public String getInstanceZone() {
return instanceZone;
}
public void setInstanceZone(String instanceZone) {
this.instanceZone = instanceZone;
}
public String getInstanceGroup() {
return instanceGroup;
}
public void setInstanceGroup(String instanceGroup) {
this.instanceGroup = instanceGroup;
}
public String getDefaultZoneMetadataName() {
return defaultZoneMetadataName;
}
public void setDefaultZoneMetadataName(String defaultZoneMetadataName) {
this.defaultZoneMetadataName = defaultZoneMetadataName;
}
public String getScheme() {
return scheme;
}
public void setScheme(String scheme) {
this.scheme = scheme;
}
@Override
public String toString() {
return "TsfCoreProperties{" +
"tsePolarisIp='" + tsePolarisIp + '\'' +
", tsfConsulEnable=" + tsfConsulEnable +
", tsePolarisEnable=" + tsePolarisEnable +
", instanceId='" + instanceId + '\'' +
", tsfApplicationId='" + tsfApplicationId + '\'' +
", tsfGroupId='" + tsfGroupId + '\'' +
", tsfProgVersion='" + tsfProgVersion + '\'' +
", tsfNamespaceId='" + tsfNamespaceId + '\'' +
", tsfRegion='" + tsfRegion + '\'' +
", tsfZone='" + tsfZone + '\'' +
", tags=" + tags +
", instanceZone='" + instanceZone + '\'' +
", instanceGroup='" + instanceGroup + '\'' +
", defaultZoneMetadataName='" + defaultZoneMetadataName + '\'' +
", scheme='" + scheme + '\'' +
'}';
}
}

@ -122,7 +122,7 @@ public final class TsfCoreEnvironmentPostProcessor implements EnvironmentPostPro
// tse_polaris_ip
defaultProperties.put("spring.cloud.polaris.address", "grpc://" + environment.getProperty("tse_polaris_ip", "") + ":8091");
// tsf_sctt_extensions_port
// tse_polaris_ip
defaultProperties.put("spring.cloud.polaris.stat.port", environment.getProperty("tsf_sctt_extensions_port", "11134"));
// rule based router fail over type

@ -0,0 +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.polaris.context.tsf.metadata;
import com.tencent.cloud.common.spi.InstanceMetadataProvider;
import com.tencent.cloud.common.tsf.ConditionalOnTsfEnabled;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Auto configuration for instanceMetadataProvider for TSF.
*
* @author Hoatian Zhang
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(PolarisContextAutoConfiguration.class)
@ConditionalOnTsfEnabled
public class TsfInstanceMetadataAutoConfiguration {
@Bean
public InstanceMetadataProvider tsfInstanceMetadataProvider(TsfCoreProperties tsfCoreProperties) {
return new TsfInstanceMetadataProvider(tsfCoreProperties);
}
}

@ -0,0 +1,77 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.context.tsf.metadata;
import java.util.HashMap;
import java.util.Map;
import com.tencent.cloud.common.constant.SdkVersion;
import com.tencent.cloud.common.constant.WarmupCons;
import com.tencent.cloud.common.spi.InstanceMetadataProvider;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.inet.PolarisInetUtils;
import com.tencent.cloud.polaris.context.tsf.TsfUtils;
import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties;
import com.tencent.polaris.api.utils.StringUtils;
import com.tencent.polaris.metadata.core.constant.TsfMetadataConstants;
import static com.tencent.cloud.polaris.context.tsf.TsfUtils.TSF_ADDRESS_IPV4;
import static com.tencent.cloud.polaris.context.tsf.TsfUtils.TSF_ADDRESS_IPV6;
/**
* InstanceMetadataProvider for TSF.
*
* @author Hoatian Zhang
*/
public class TsfInstanceMetadataProvider implements InstanceMetadataProvider {
private final TsfCoreProperties tsfCoreProperties;
public TsfInstanceMetadataProvider(TsfCoreProperties tsfCoreProperties) {
this.tsfCoreProperties = tsfCoreProperties;
}
@Override
public Map<String, String> getMetadata() {
return new HashMap<String, String>() {{
put(TsfMetadataConstants.TSF_PROG_VERSION, tsfCoreProperties.getTsfProgVersion());
put(TsfMetadataConstants.TSF_APPLICATION_ID, tsfCoreProperties.getTsfApplicationId());
put(TsfMetadataConstants.TSF_GROUP_ID, tsfCoreProperties.getTsfGroupId());
put(TsfMetadataConstants.TSF_APPLICATION_ID, tsfCoreProperties.getTsfApplicationId());
put(TsfMetadataConstants.TSF_PROG_VERSION, tsfCoreProperties.getTsfProgVersion());
put(TsfMetadataConstants.TSF_GROUP_ID, tsfCoreProperties.getTsfGroupId());
put(TsfMetadataConstants.TSF_NAMESPACE_ID, tsfCoreProperties.getTsfNamespaceId());
put(TsfMetadataConstants.TSF_INSTNACE_ID, tsfCoreProperties.getInstanceId());
put(TsfMetadataConstants.TSF_REGION, tsfCoreProperties.getTsfRegion());
put(TsfMetadataConstants.TSF_ZONE, tsfCoreProperties.getTsfZone());
// 处理预热相关的参数
put(WarmupCons.TSF_START_TIME, String.valueOf(System.currentTimeMillis()));
put(TsfMetadataConstants.TSF_SDK_VERSION, SdkVersion.get());
put(TsfMetadataConstants.TSF_TAGS, JacksonUtils.serialize2Json(TsfUtils.createTags(tsfCoreProperties)));
String ipv4Address = PolarisInetUtils.getIpString(false);
if (StringUtils.isNotBlank(ipv4Address)) {
put(TSF_ADDRESS_IPV4, ipv4Address);
}
String ipv6Address = PolarisInetUtils.getIpString(true);
if (StringUtils.isNotBlank(ipv6Address)) {
put(TSF_ADDRESS_IPV6, ipv6Address);
}
}};
}
}

@ -2,7 +2,8 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration,\
com.tencent.cloud.polaris.context.config.PolarisContextPostConfiguration,\
com.tencent.cloud.polaris.context.tsf.consul.TsfConsulAutoConfiguration,\
com.tencent.cloud.polaris.context.tsf.config.TsfCorePropertiesAutoConfiguration
com.tencent.cloud.polaris.context.tsf.config.TsfCorePropertiesAutoConfiguration,\
com.tencent.cloud.polaris.context.tsf.metadata.TsfInstanceMetadataAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.polaris.context.config.PolarisContextBootstrapAutoConfiguration,\
com.tencent.cloud.polaris.context.tsf.config.TsfCorePropertiesBootstrapConfiguration,\

@ -20,6 +20,7 @@ package com.tencent.cloud.rpc.enhancement.transformer;
import com.tencent.cloud.common.pojo.PolarisServiceInstance;
import com.tencent.polaris.api.pojo.DefaultInstance;
import com.tencent.polaris.api.utils.CollectionUtils;
import org.springframework.cloud.client.ServiceInstance;
@ -38,6 +39,9 @@ public class PolarisInstanceTransformer implements InstanceTransformer {
instance.setZone(polarisServiceInstance.getPolarisInstance().getZone());
instance.setCampus(polarisServiceInstance.getPolarisInstance().getCampus());
instance.setWeight(polarisServiceInstance.getPolarisInstance().getWeight());
if (CollectionUtils.isNotEmpty(polarisServiceInstance.getServiceMetadata())) {
instance.getServiceMetadata().putAll(polarisServiceInstance.getServiceMetadata());
}
}
}

Loading…
Cancel
Save