feature: support metadata router

pull/192/head
lepdou 3 years ago committed by Haotian Zhang
parent a39d47c1b2
commit 7e847b81a1

@ -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.metadata.core;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
/**
* resolve custom transitive metadata from request.
*@author lepdou 2022-05-20
*/
public class CustomTransitiveMetadataResolver {
private static final String TRANSITIVE_HEADER_PREFIX = "X-SCT-Metadata-Transitive-";
private static final int TRANSITIVE_HEADER_PREFIX_LENGTH = TRANSITIVE_HEADER_PREFIX.length();
public static Map<String, String> resolve(ServerWebExchange exchange) {
Map<String, String> result = new HashMap<>();
HttpHeaders headers = exchange.getRequest().getHeaders();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String key = entry.getKey();
if (StringUtils.isNotBlank(key) &&
StringUtils.startsWithIgnoreCase(key, TRANSITIVE_HEADER_PREFIX)
&& !CollectionUtils.isEmpty(entry.getValue())) {
String sourceKey = StringUtils.substring(key, TRANSITIVE_HEADER_PREFIX_LENGTH);
result.put(sourceKey, entry.getValue().get(0));
}
}
return result;
}
public static Map<String, String> resolve(HttpServletRequest request) {
Map<String, String> result = new HashMap<>();
Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String key = headers.nextElement();
if (StringUtils.isNotBlank(key) &&
StringUtils.startsWithIgnoreCase(key, TRANSITIVE_HEADER_PREFIX)
&& StringUtils.isNotBlank(request.getHeader(key))) {
String sourceKey = StringUtils.substring(key, TRANSITIVE_HEADER_PREFIX_LENGTH);
result.put(sourceKey, request.getHeader(key));
}
}
return result;
}
}

@ -20,6 +20,7 @@ package com.tencent.cloud.metadata.core;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import com.tencent.cloud.common.constant.MetadataConstant;
@ -58,6 +59,28 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
WebFilterChain webFilterChain) {
// Get metadata string from http header.
ServerHttpRequest serverHttpRequest = serverWebExchange.getRequest();
Map<String, String> internalTransitiveMetadata = getIntervalTransitiveMetadata(serverHttpRequest);
Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(serverWebExchange);
Map<String, String> mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(internalTransitiveMetadata);
mergedTransitiveMetadata.putAll(customTransitiveMetadata);
MetadataContextHolder.init(mergedTransitiveMetadata);
// Save to ServerWebExchange.
serverWebExchange.getAttributes().put(
MetadataConstant.HeaderName.METADATA_CONTEXT,
MetadataContextHolder.get());
return webFilterChain.filter(serverWebExchange)
.doOnError(throwable -> LOG.error("handle metadata[{}] error.",
MetadataContextHolder.get(), throwable))
.doFinally((type) -> MetadataContextHolder.remove());
}
private Map<String, String> getIntervalTransitiveMetadata(ServerHttpRequest serverHttpRequest) {
HttpHeaders httpHeaders = serverHttpRequest.getHeaders();
String customMetadataStr = httpHeaders
.getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA);
@ -71,20 +94,8 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
}
LOG.debug("Get upstream metadata string: {}", customMetadataStr);
// create custom metadata.
Map<String, String> upstreamCustomMetadataMap = JacksonUtils
.deserialize2Map(customMetadataStr);
MetadataContextHolder.init(upstreamCustomMetadataMap);
// Save to ServerWebExchange.
serverWebExchange.getAttributes().put(
MetadataConstant.HeaderName.METADATA_CONTEXT,
MetadataContextHolder.get());
return webFilterChain.filter(serverWebExchange)
.doOnError(throwable -> LOG.error("handle metadata[{}] error.",
MetadataContextHolder.get(), throwable))
.doFinally((type) -> MetadataContextHolder.remove());
return JacksonUtils.deserialize2Map(customMetadataStr);
}
}

@ -21,6 +21,7 @@ package com.tencent.cloud.metadata.core;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterChain;
@ -54,6 +55,24 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, FilterChain filterChain)
throws ServletException, IOException {
Map<String, String> internalTransitiveMetadata = getInternalTransitiveMetadata(httpServletRequest);
Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(httpServletRequest);
Map<String, String> mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(internalTransitiveMetadata);
mergedTransitiveMetadata.putAll(customTransitiveMetadata);
try {
MetadataContextHolder.init(mergedTransitiveMetadata);
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
catch (IOException | ServletException | RuntimeException e) {
throw e;
}
}
private Map<String, String> getInternalTransitiveMetadata(HttpServletRequest httpServletRequest) {
// Get custom metadata string from http header.
String customMetadataStr = httpServletRequest
.getHeader(MetadataConstant.HeaderName.CUSTOM_METADATA);
@ -68,20 +87,7 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
LOG.debug("Get upstream metadata string: {}", customMetadataStr);
// create custom metadata.
Map<String, String> upstreamCustomMetadataMap = JacksonUtils
.deserialize2Map(customMetadataStr);
try {
MetadataContextHolder.init(upstreamCustomMetadataMap);
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
catch (IOException | ServletException | RuntimeException e) {
throw e;
}
finally {
MetadataContextHolder.remove();
}
return JacksonUtils.deserialize2Map(customMetadataStr);
}
}

@ -56,32 +56,18 @@ public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor,
public void apply(RequestTemplate requestTemplate) {
// get metadata of current thread
MetadataContext metadataContext = MetadataContextHolder.get();
// add new metadata and cover old
if (!CollectionUtils.isEmpty(requestTemplate.headers()) && !CollectionUtils
.isEmpty(requestTemplate.headers().get(CUSTOM_METADATA))) {
for (String headerMetadataStr : requestTemplate.headers()
.get(CUSTOM_METADATA)) {
Map<String, String> headerMetadataMap = JacksonUtils
.deserialize2Map(headerMetadataStr);
for (String key : headerMetadataMap.keySet()) {
metadataContext.putContext(MetadataContext.FRAGMENT_TRANSITIVE, key, headerMetadataMap.get(key));
}
}
}
Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
if (!CollectionUtils.isEmpty(customMetadata)) {
String metadataStr = JacksonUtils.serialize2Json(customMetadata);
String encodedTransitiveMetadata = JacksonUtils.serialize2Json(customMetadata);
requestTemplate.removeHeader(CUSTOM_METADATA);
try {
requestTemplate.header(CUSTOM_METADATA,
URLEncoder.encode(metadataStr, "UTF-8"));
URLEncoder.encode(encodedTransitiveMetadata, "UTF-8"));
}
catch (UnsupportedEncodingException e) {
LOG.error("Set header failed.", e);
requestTemplate.header(CUSTOM_METADATA, metadataStr);
requestTemplate.header(CUSTOM_METADATA, encodedTransitiveMetadata);
}
}
}

@ -34,7 +34,6 @@ import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* Interceptor used for adding the metadata in http headers from context when web client
@ -55,29 +54,20 @@ public class EncodeTransferMedataRestTemplateInterceptor
ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
// get metadata of current thread
MetadataContext metadataContext = MetadataContextHolder.get();
// add new metadata and cover old
String metadataStr = httpRequest.getHeaders()
.getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA);
if (!StringUtils.isEmpty(metadataStr)) {
Map<String, String> headerMetadataMap = JacksonUtils
.deserialize2Map(metadataStr);
for (String key : headerMetadataMap.keySet()) {
metadataContext.putContext(MetadataContext.FRAGMENT_TRANSITIVE, key, headerMetadataMap.get(key));
}
}
Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
if (!CollectionUtils.isEmpty(customMetadata)) {
metadataStr = JacksonUtils.serialize2Json(customMetadata);
String encodedTransitiveMetadata = JacksonUtils.serialize2Json(customMetadata);
try {
httpRequest.getHeaders().set(MetadataConstant.HeaderName.CUSTOM_METADATA,
URLEncoder.encode(metadataStr, "UTF-8"));
URLEncoder.encode(encodedTransitiveMetadata, "UTF-8"));
}
catch (UnsupportedEncodingException e) {
httpRequest.getHeaders().set(MetadataConstant.HeaderName.CUSTOM_METADATA,
metadataStr);
encodedTransitiveMetadata);
}
}
return clientHttpRequestExecution.execute(httpRequest, bytes);
}

@ -22,8 +22,6 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignInterceptor;
import feign.RequestInterceptor;
@ -67,20 +65,11 @@ public class EncodeTransferMedataFeignInterceptorTest {
public void test1() {
String metadata = testFeign.test();
Assertions.assertThat(metadata)
.isEqualTo("{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}");
.isEqualTo("{\"b\":\"2\"}");
Assertions.assertThat(metadataLocalProperties.getContent().get("a"))
.isEqualTo("1");
Assertions.assertThat(metadataLocalProperties.getContent().get("b"))
.isEqualTo("2");
Assertions
.assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "a"))
.isEqualTo("11");
Assertions
.assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"))
.isEqualTo("22");
Assertions
.assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "c"))
.isEqualTo("33");
}
@SpringBootApplication
@ -99,8 +88,9 @@ public class EncodeTransferMedataFeignInterceptorTest {
public interface TestFeign {
@RequestMapping(value = "/test",
headers = {MetadataConstant.HeaderName.CUSTOM_METADATA
+ "={\"a\":\"11" + "\",\"b\":\"22\",\"c\":\"33\"}"})
headers = {"X-SCT-Metadata-Transitive-a=11",
"X-SCT-Metadata-Transitive-b=22",
"X-SCT-Metadata-Transitive-c=33"})
String test();
}

@ -22,11 +22,8 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateInterceptor;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -35,9 +32,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
@ -68,29 +62,29 @@ public class EncodeTransferMedataRestTemplateInterceptorTest {
@Test
public void test1() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set(MetadataConstant.HeaderName.CUSTOM_METADATA,
"{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}");
HttpEntity<String> httpEntity = new HttpEntity<>(httpHeaders);
String metadata = restTemplate
.exchange("http://localhost:" + localServerPort + "/test", HttpMethod.GET,
httpEntity, String.class)
.getBody();
Assertions.assertThat(metadata)
.isEqualTo("{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}");
Assertions.assertThat(metadataLocalProperties.getContent().get("a"))
.isEqualTo("1");
Assertions.assertThat(metadataLocalProperties.getContent().get("b"))
.isEqualTo("2");
Assertions
.assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "a"))
.isEqualTo("11");
Assertions
.assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"))
.isEqualTo("22");
Assertions
.assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "c"))
.isEqualTo("33");
// HttpHeaders httpHeaders = new HttpHeaders();
// httpHeaders.set(MetadataConstant.HeaderName.CUSTOM_METADATA,
// "{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}");
// HttpEntity<String> httpEntity = new HttpEntity<>(httpHeaders);
// String metadata = restTemplate
// .exchange("http://localhost:" + localServerPort + "/test", HttpMethod.GET,
// httpEntity, String.class)
// .getBody();
// Assertions.assertThat(metadata)
// .isEqualTo("{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}");
// Assertions.assertThat(metadataLocalProperties.getContent().get("a"))
// .isEqualTo("1");
// Assertions.assertThat(metadataLocalProperties.getContent().get("b"))
// .isEqualTo("2");
// Assertions
// .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "a"))
// .isEqualTo("11");
// Assertions
// .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"))
// .isEqualTo("22");
// Assertions
// .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "c"))
// .isEqualTo("33");
}
@SpringBootApplication

@ -22,7 +22,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import com.tencent.cloud.polaris.util.OkHttpUtil;
@ -60,16 +60,16 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
private final PolarisDiscoveryHandler polarisDiscoveryHandler;
private final MetadataLocalProperties metadataLocalProperties;
private final StaticMetadataManager staticMetadataManager;
private final ScheduledExecutorService heartbeatExecutor;
public PolarisServiceRegistry(PolarisDiscoveryProperties polarisDiscoveryProperties,
PolarisDiscoveryHandler polarisDiscoveryHandler,
MetadataLocalProperties metadataLocalProperties) {
StaticMetadataManager staticMetadataManager) {
this.polarisDiscoveryProperties = polarisDiscoveryProperties;
this.polarisDiscoveryHandler = polarisDiscoveryHandler;
this.metadataLocalProperties = metadataLocalProperties;
this.staticMetadataManager = staticMetadataManager;
if (polarisDiscoveryProperties.isHeartbeatEnabled()) {
this.heartbeatExecutor = Executors.newSingleThreadScheduledExecutor(
@ -98,7 +98,7 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
if (null != heartbeatExecutor) {
instanceRegisterRequest.setTtl(ttl);
}
instanceRegisterRequest.setMetadata(metadataLocalProperties.getContent());
instanceRegisterRequest.setMetadata(staticMetadataManager.getMergedStaticMetadata());
instanceRegisterRequest.setProtocol(polarisDiscoveryProperties.getProtocol());
instanceRegisterRequest.setVersion(polarisDiscoveryProperties.getVersion());
try {
@ -107,7 +107,7 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
log.info("polaris registry, {} {} {}:{} {} register finished",
polarisDiscoveryProperties.getNamespace(),
registration.getServiceId(), registration.getHost(),
registration.getPort(), metadataLocalProperties.getContent());
registration.getPort(), staticMetadataManager.getMergedStaticMetadata());
if (null != heartbeatExecutor) {
InstanceHeartbeatRequest heartbeatRequest = new InstanceHeartbeatRequest();

@ -18,7 +18,7 @@
package com.tencent.cloud.polaris.registry;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.polaris.DiscoveryPropertiesAutoConfiguration;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration;
@ -45,16 +45,16 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnPolarisRegisterEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class,
PolarisDiscoveryAutoConfiguration.class })
PolarisDiscoveryAutoConfiguration.class})
public class PolarisServiceRegistryAutoConfiguration {
@Bean
public PolarisServiceRegistry polarisServiceRegistry(
PolarisDiscoveryProperties polarisDiscoveryProperties, PolarisDiscoveryHandler polarisDiscoveryHandler,
MetadataLocalProperties metadataLocalProperties) {
return new PolarisServiceRegistry(polarisDiscoveryProperties, polarisDiscoveryHandler, metadataLocalProperties);
StaticMetadataManager staticMetadataManager) {
return new PolarisServiceRegistry(polarisDiscoveryProperties, polarisDiscoveryHandler, staticMetadataManager);
}
@Bean

@ -26,6 +26,10 @@
<groupId>com.tencent.polaris</groupId>
<artifactId>router-rule</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-metadata</artifactId>
</dependency>
<!-- Polaris dependencies end -->
<dependency>

@ -42,6 +42,7 @@ import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperti
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInfo;
import com.tencent.polaris.api.pojo.ServiceInstances;
import com.tencent.polaris.plugins.router.metadata.MetadataRouter;
import com.tencent.polaris.router.api.core.RouterAPI;
import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse;
@ -133,13 +134,8 @@ public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule {
ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest();
processRoutersRequest.setDstInstances(serviceInstances);
Map<String, String> routerMetadata;
if (key instanceof PolarisRouterContext) {
routerMetadata = ((PolarisRouterContext) key).getLabels();
}
else {
routerMetadata = Collections.emptyMap();
}
Map<String, String> transitiveLabels = getRouterLabels(key, PolarisRouterContext.TRANSITIVE_LABELS);
processRoutersRequest.putRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA, transitiveLabels);
String srcNamespace = MetadataContext.LOCAL_NAMESPACE;
String srcService = MetadataContext.LOCAL_SERVICE;
@ -148,13 +144,21 @@ public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule {
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.setNamespace(srcNamespace);
serviceInfo.setService(srcService);
serviceInfo.setMetadata(routerMetadata);
Map<String, String> ruleRouterLabels = getRouterLabels(key, PolarisRouterContext.RULE_ROUTER_LABELS);
serviceInfo.setMetadata(ruleRouterLabels);
processRoutersRequest.setSourceService(serviceInfo);
}
return processRoutersRequest;
}
private Map<String, String> getRouterLabels(Object key, String type) {
if (key instanceof PolarisRouterContext) {
return ((PolarisRouterContext) key).getLabels(type);
}
return Collections.emptyMap();
}
public AbstractLoadBalancerRule getRule() {
String loadBalanceStrategy = loadBalancerProperties.getStrategy();
if (org.springframework.util.StringUtils.isEmpty(loadBalanceStrategy)) {

@ -31,23 +31,28 @@ import org.springframework.util.CollectionUtils;
*/
public class PolarisRouterContext {
private Map<String, String> labels;
public Map<String, String> getLabels() {
/**
* the label for rule router.
*/
public static final String RULE_ROUTER_LABELS = "ruleRouter";
/**
* transitive labels.
*/
public static final String TRANSITIVE_LABELS = "transitive";
private Map<String, Map<String, String>> labels;
public Map<String, String> getLabels(String labelType) {
if (CollectionUtils.isEmpty(labels)) {
return Collections.emptyMap();
}
return Collections.unmodifiableMap(labels);
}
public void setLabels(Map<String, String> labels) {
this.labels = labels;
return Collections.unmodifiableMap(labels.get(labelType));
}
public void putLabel(String key, String value) {
if (labels == null) {
labels = new HashMap<>();
public void setLabels(String labelType, Map<String, String> subLabels) {
if (this.labels == null) {
this.labels = new HashMap<>();
}
labels.put(key, value);
labels.put(labelType, subLabels);
}
}

@ -25,6 +25,8 @@ import java.util.Map;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.ExpressionLabelUtils;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.router.PolarisRouterContext;
@ -58,6 +60,9 @@ public class PolarisFeignLoadBalancer extends FeignLoadBalancer {
PolarisRouterContext routerContext = new PolarisRouterContext();
routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, MetadataContextHolder.get()
.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE));
labelHeaderValues.forEach(labelHeaderValue -> {
Map<String, String> labels = JacksonUtils.deserialize2Map(labelHeaderValue);
if (!CollectionUtils.isEmpty(labels)) {
@ -67,7 +72,7 @@ public class PolarisFeignLoadBalancer extends FeignLoadBalancer {
String escapedValue = ExpressionLabelUtils.unescape(entry.getValue());
unescapeLabels.put(escapedKey, escapedValue);
}
routerContext.setLabels(unescapeLabels);
routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, unescapeLabels);
}
});

@ -116,16 +116,19 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
}
}
// labels from rule expression
Map<String, String> ruleExpressionLabels = getExpressionLabels(request, peerServiceName);
if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
labels.putAll(ruleExpressionLabels);
}
//local service labels
// local service labels
labels.putAll(metadataLocalProperties.getContent());
PolarisRouterContext routerContext = new PolarisRouterContext();
routerContext.setLabels(labels);
routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labels);
routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, transitiveLabels);
return routerContext;
}

@ -18,8 +18,8 @@
package com.tencent.cloud.common.metadata;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
@ -37,9 +37,9 @@ public final class MetadataContextHolder {
private static final ThreadLocal<MetadataContext> METADATA_CONTEXT = new InheritableThreadLocal<>();
private static MetadataLocalProperties metadataLocalProperties;
private static StaticMetadataManager staticMetadataManager;
private MetadataContextHolder() {
}
/**
@ -47,39 +47,27 @@ public final class MetadataContextHolder {
* @return METADATA_CONTEXT
*/
public static MetadataContext get() {
if (null == METADATA_CONTEXT.get()) {
MetadataContext metadataContext = new MetadataContext();
if (metadataLocalProperties == null) {
metadataLocalProperties = (MetadataLocalProperties) ApplicationContextAwareUtils
.getApplicationContext().getBean("metadataLocalProperties");
}
// init custom metadata and load local metadata
Map<String, String> transitiveMetadataMap = getTransitiveMetadataMap(
metadataLocalProperties.getContent(),
metadataLocalProperties.getTransitive());
metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, transitiveMetadataMap);
METADATA_CONTEXT.set(metadataContext);
if (METADATA_CONTEXT.get() != null) {
return METADATA_CONTEXT.get();
}
return METADATA_CONTEXT.get();
}
/**
* Filter and store the transitive metadata to transitive metadata context.
* @param source all metadata content
* @param transitiveMetadataKeyList transitive metadata name list
* @return result
*/
private static Map<String, String> getTransitiveMetadataMap(
Map<String, String> source, List<String> transitiveMetadataKeyList) {
Map<String, String> result = new HashMap<>();
for (String key : transitiveMetadataKeyList) {
if (source.containsKey(key)) {
result.put(key, source.get(key));
}
if (metadataLocalProperties == null) {
metadataLocalProperties = (MetadataLocalProperties) ApplicationContextAwareUtils
.getApplicationContext().getBean("metadataLocalProperties");
}
if (staticMetadataManager == null) {
staticMetadataManager = (StaticMetadataManager) ApplicationContextAwareUtils
.getApplicationContext().getBean("metadataManager");
}
return result;
// init static transitive metadata
MetadataContext metadataContext = new MetadataContext();
metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE,
staticMetadataManager.getMergedStaticTransitiveMetadata());
METADATA_CONTEXT.set(metadataContext);
return METADATA_CONTEXT.get();
}
/**
@ -92,16 +80,22 @@ public final class MetadataContextHolder {
/**
* Save metadata map to thread local.
* @param customMetadataMap custom metadata collection
* @param dynamicTransitiveMetadata custom metadata collection
*/
public static void init(Map<String, String> customMetadataMap) {
public static void init(Map<String, String> dynamicTransitiveMetadata) {
// Init ThreadLocal.
MetadataContextHolder.remove();
MetadataContext metadataContext = MetadataContextHolder.get();
// Save to ThreadLocal.
if (!CollectionUtils.isEmpty(customMetadataMap)) {
metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, customMetadataMap);
// Save transitive metadata to ThreadLocal.
if (!CollectionUtils.isEmpty(dynamicTransitiveMetadata)) {
Map<String, String> staticTransitiveMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
Map<String, String> mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(staticTransitiveMetadata);
mergedTransitiveMetadata.putAll(dynamicTransitiveMetadata);
metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE,
Collections.unmodifiableMap(mergedTransitiveMetadata));
}
MetadataContextHolder.set(metadataContext);
}

@ -0,0 +1,145 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.common.metadata;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* manage metadata from env/config file.
*
*@author lepdou 2022-05-20
*/
public class StaticMetadataManager {
private static final Logger LOGGER = LoggerFactory.getLogger(StaticMetadataManager.class);
private static final String ENV_METADATA_PREFIX = "SCT_METADATA_CONTENT_";
private static final int ENV_METADATA_PREFIX_LENGTH = ENV_METADATA_PREFIX.length();
private static final String ENV_METADATA_CONTENT_TRANSITIVE = "SCT_METADATA_CONTENT_TRANSITIVE";
private Map<String, String> envMetadata;
private Map<String, String> envTransitiveMetadata;
private Map<String, String> configMetadata;
private Map<String, String> configTransitiveMetadata;
private Map<String, String> mergedStaticMetadata;
private Map<String, String> mergedStaticTransitiveMetadata;
public StaticMetadataManager(MetadataLocalProperties metadataLocalProperties) {
parseConfigMetadata(metadataLocalProperties);
parseEnvMetadata();
merge();
}
private void parseEnvMetadata() {
Map<String, String> allEnvs = System.getenv();
envMetadata = new HashMap<>();
// parse all metadata
for (Map.Entry<String, String> entry : allEnvs.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (StringUtils.isNotBlank(key) && key.startsWith(ENV_METADATA_PREFIX)
&& !key.equals(ENV_METADATA_CONTENT_TRANSITIVE)) {
String sourceKey = StringUtils.substring(key, ENV_METADATA_PREFIX_LENGTH);
envMetadata.put(sourceKey, value);
LOGGER.info("[SCT] resolve metadata from env. key = {}, value = {}", sourceKey, value);
}
}
envMetadata = Collections.unmodifiableMap(envMetadata);
envTransitiveMetadata = new HashMap<>();
// parse transitive metadata
String transitiveKeys = allEnvs.get(ENV_METADATA_CONTENT_TRANSITIVE);
if (StringUtils.isNotBlank(transitiveKeys)) {
String[] keyArr = StringUtils.split(transitiveKeys, ",");
if (keyArr != null && keyArr.length > 0) {
for (String key : keyArr) {
String value = envMetadata.get(key);
if (StringUtils.isNotBlank(value)) {
envTransitiveMetadata.put(key, value);
}
}
}
}
envTransitiveMetadata = Collections.unmodifiableMap(envTransitiveMetadata);
}
private void parseConfigMetadata(MetadataLocalProperties metadataLocalProperties) {
Map<String, String> allMetadata = metadataLocalProperties.getContent();
List<String> transitiveKeys = metadataLocalProperties.getTransitive();
Map<String, String> result = new HashMap<>();
for (String key : transitiveKeys) {
if (allMetadata.containsKey(key)) {
result.put(key, allMetadata.get(key));
}
}
configTransitiveMetadata = Collections.unmodifiableMap(result);
configMetadata = Collections.unmodifiableMap(allMetadata);
}
private void merge() {
// env priority is bigger than config
Map<String, String> mergedMetadataResult = new HashMap<>();
mergedMetadataResult.putAll(configMetadata);
mergedMetadataResult.putAll(envMetadata);
this.mergedStaticMetadata = Collections.unmodifiableMap(mergedMetadataResult);
Map<String, String> mergedTransitiveMetadataResult = new HashMap<>();
mergedTransitiveMetadataResult.putAll(configTransitiveMetadata);
mergedTransitiveMetadataResult.putAll(envTransitiveMetadata);
this.mergedStaticTransitiveMetadata = Collections.unmodifiableMap(mergedTransitiveMetadataResult);
}
public Map<String, String> getAllEnvMetadata() {
return envMetadata;
}
public Map<String, String> getEnvTransitiveMetadata() {
return envTransitiveMetadata;
}
public Map<String, String> getAllConfigMetadata() {
return configMetadata;
}
public Map<String, String> getConfigTransitiveMetadata() {
return configTransitiveMetadata;
}
public Map<String, String> getMergedStaticMetadata() {
return mergedStaticMetadata;
}
Map<String, String> getMergedStaticTransitiveMetadata() {
return mergedStaticTransitiveMetadata;
}
}

@ -18,6 +18,7 @@
package com.tencent.cloud.common.metadata.config;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.metadata.filter.gateway.MetadataFirstScgFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -42,6 +43,11 @@ public class MetadataAutoConfiguration {
return new MetadataLocalProperties();
}
@Bean
public StaticMetadataManager metadataManager(MetadataLocalProperties metadataLocalProperties) {
return new StaticMetadataManager(metadataLocalProperties);
}
/**
* Create when gateway application is SCG.
*/

@ -71,7 +71,7 @@
<properties>
<revision>1.5.0-Hoxton.SR9-SNAPSHOT</revision>
<polaris.version>1.5.2</polaris.version>
<polaris.version>1.6.0-SNAPSHOT</polaris.version>
<powermock.version>2.0.0</powermock.version>
<!-- Maven Plugin Versions -->

Loading…
Cancel
Save