feature: support metadata router

pull/192/head
lepdou 2 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.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.constant.MetadataConstant;
@ -58,6 +59,28 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
WebFilterChain webFilterChain) { WebFilterChain webFilterChain) {
// Get metadata string from http header. // Get metadata string from http header.
ServerHttpRequest serverHttpRequest = serverWebExchange.getRequest(); 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(); HttpHeaders httpHeaders = serverHttpRequest.getHeaders();
String customMetadataStr = httpHeaders String customMetadataStr = httpHeaders
.getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA); .getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA);
@ -71,20 +94,8 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
} }
LOG.debug("Get upstream metadata string: {}", customMetadataStr); LOG.debug("Get upstream metadata string: {}", customMetadataStr);
// create custom metadata. return JacksonUtils.deserialize2Map(customMetadataStr);
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());
} }
} }

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

@ -56,32 +56,18 @@ public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor,
public void apply(RequestTemplate requestTemplate) { public void apply(RequestTemplate requestTemplate) {
// get metadata of current thread // get metadata of current thread
MetadataContext metadataContext = MetadataContextHolder.get(); 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); Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
if (!CollectionUtils.isEmpty(customMetadata)) { if (!CollectionUtils.isEmpty(customMetadata)) {
String metadataStr = JacksonUtils.serialize2Json(customMetadata); String encodedTransitiveMetadata = JacksonUtils.serialize2Json(customMetadata);
requestTemplate.removeHeader(CUSTOM_METADATA); requestTemplate.removeHeader(CUSTOM_METADATA);
try { try {
requestTemplate.header(CUSTOM_METADATA, requestTemplate.header(CUSTOM_METADATA,
URLEncoder.encode(metadataStr, "UTF-8")); URLEncoder.encode(encodedTransitiveMetadata, "UTF-8"));
} }
catch (UnsupportedEncodingException e) { catch (UnsupportedEncodingException e) {
LOG.error("Set header failed.", 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.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/** /**
* Interceptor used for adding the metadata in http headers from context when web client * 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 { ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
// get metadata of current thread // get metadata of current thread
MetadataContext metadataContext = MetadataContextHolder.get(); 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); Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
if (!CollectionUtils.isEmpty(customMetadata)) { if (!CollectionUtils.isEmpty(customMetadata)) {
metadataStr = JacksonUtils.serialize2Json(customMetadata); String encodedTransitiveMetadata = JacksonUtils.serialize2Json(customMetadata);
try { try {
httpRequest.getHeaders().set(MetadataConstant.HeaderName.CUSTOM_METADATA, httpRequest.getHeaders().set(MetadataConstant.HeaderName.CUSTOM_METADATA,
URLEncoder.encode(metadataStr, "UTF-8")); URLEncoder.encode(encodedTransitiveMetadata, "UTF-8"));
} }
catch (UnsupportedEncodingException e) { catch (UnsupportedEncodingException e) {
httpRequest.getHeaders().set(MetadataConstant.HeaderName.CUSTOM_METADATA, httpRequest.getHeaders().set(MetadataConstant.HeaderName.CUSTOM_METADATA,
metadataStr); encodedTransitiveMetadata);
} }
} }
return clientHttpRequestExecution.execute(httpRequest, bytes); return clientHttpRequestExecution.execute(httpRequest, bytes);
} }

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

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

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

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

@ -26,6 +26,10 @@
<groupId>com.tencent.polaris</groupId> <groupId>com.tencent.polaris</groupId>
<artifactId>router-rule</artifactId> <artifactId>router-rule</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-metadata</artifactId>
</dependency>
<!-- Polaris dependencies end --> <!-- Polaris dependencies end -->
<dependency> <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.Instance;
import com.tencent.polaris.api.pojo.ServiceInfo; import com.tencent.polaris.api.pojo.ServiceInfo;
import com.tencent.polaris.api.pojo.ServiceInstances; 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.core.RouterAPI;
import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest; import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse; import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse;
@ -133,13 +134,8 @@ public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule {
ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest(); ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest();
processRoutersRequest.setDstInstances(serviceInstances); processRoutersRequest.setDstInstances(serviceInstances);
Map<String, String> routerMetadata; Map<String, String> transitiveLabels = getRouterLabels(key, PolarisRouterContext.TRANSITIVE_LABELS);
if (key instanceof PolarisRouterContext) { processRoutersRequest.putRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA, transitiveLabels);
routerMetadata = ((PolarisRouterContext) key).getLabels();
}
else {
routerMetadata = Collections.emptyMap();
}
String srcNamespace = MetadataContext.LOCAL_NAMESPACE; String srcNamespace = MetadataContext.LOCAL_NAMESPACE;
String srcService = MetadataContext.LOCAL_SERVICE; String srcService = MetadataContext.LOCAL_SERVICE;
@ -148,13 +144,21 @@ public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule {
ServiceInfo serviceInfo = new ServiceInfo(); ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.setNamespace(srcNamespace); serviceInfo.setNamespace(srcNamespace);
serviceInfo.setService(srcService); serviceInfo.setService(srcService);
serviceInfo.setMetadata(routerMetadata); Map<String, String> ruleRouterLabels = getRouterLabels(key, PolarisRouterContext.RULE_ROUTER_LABELS);
serviceInfo.setMetadata(ruleRouterLabels);
processRoutersRequest.setSourceService(serviceInfo); processRoutersRequest.setSourceService(serviceInfo);
} }
return processRoutersRequest; 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() { public AbstractLoadBalancerRule getRule() {
String loadBalanceStrategy = loadBalancerProperties.getStrategy(); String loadBalanceStrategy = loadBalancerProperties.getStrategy();
if (org.springframework.util.StringUtils.isEmpty(loadBalanceStrategy)) { if (org.springframework.util.StringUtils.isEmpty(loadBalanceStrategy)) {

@ -31,23 +31,28 @@ import org.springframework.util.CollectionUtils;
*/ */
public class PolarisRouterContext { public class PolarisRouterContext {
private Map<String, String> labels; /**
* the label for rule router.
public Map<String, String> getLabels() { */
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)) { if (CollectionUtils.isEmpty(labels)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
return Collections.unmodifiableMap(labels); return Collections.unmodifiableMap(labels.get(labelType));
}
public void setLabels(Map<String, String> labels) {
this.labels = labels;
} }
public void putLabel(String key, String value) { public void setLabels(String labelType, Map<String, String> subLabels) {
if (labels == null) { if (this.labels == null) {
labels = new HashMap<>(); 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.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.reactive.LoadBalancerCommand; 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.ExpressionLabelUtils;
import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.router.PolarisRouterContext; import com.tencent.cloud.polaris.router.PolarisRouterContext;
@ -58,6 +60,9 @@ public class PolarisFeignLoadBalancer extends FeignLoadBalancer {
PolarisRouterContext routerContext = new PolarisRouterContext(); PolarisRouterContext routerContext = new PolarisRouterContext();
routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, MetadataContextHolder.get()
.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE));
labelHeaderValues.forEach(labelHeaderValue -> { labelHeaderValues.forEach(labelHeaderValue -> {
Map<String, String> labels = JacksonUtils.deserialize2Map(labelHeaderValue); Map<String, String> labels = JacksonUtils.deserialize2Map(labelHeaderValue);
if (!CollectionUtils.isEmpty(labels)) { if (!CollectionUtils.isEmpty(labels)) {
@ -67,7 +72,7 @@ public class PolarisFeignLoadBalancer extends FeignLoadBalancer {
String escapedValue = ExpressionLabelUtils.unescape(entry.getValue()); String escapedValue = ExpressionLabelUtils.unescape(entry.getValue());
unescapeLabels.put(escapedKey, escapedValue); 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); Map<String, String> ruleExpressionLabels = getExpressionLabels(request, peerServiceName);
if (!CollectionUtils.isEmpty(ruleExpressionLabels)) { if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
labels.putAll(ruleExpressionLabels); labels.putAll(ruleExpressionLabels);
} }
//local service labels // local service labels
labels.putAll(metadataLocalProperties.getContent()); labels.putAll(metadataLocalProperties.getContent());
PolarisRouterContext routerContext = new PolarisRouterContext(); PolarisRouterContext routerContext = new PolarisRouterContext();
routerContext.setLabels(labels);
routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labels);
routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, transitiveLabels);
return routerContext; return routerContext;
} }

@ -18,8 +18,8 @@
package com.tencent.cloud.common.metadata; package com.tencent.cloud.common.metadata;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; 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 final ThreadLocal<MetadataContext> METADATA_CONTEXT = new InheritableThreadLocal<>();
private static MetadataLocalProperties metadataLocalProperties; private static MetadataLocalProperties metadataLocalProperties;
private static StaticMetadataManager staticMetadataManager;
private MetadataContextHolder() { private MetadataContextHolder() {
} }
/** /**
@ -47,39 +47,27 @@ public final class MetadataContextHolder {
* @return METADATA_CONTEXT * @return METADATA_CONTEXT
*/ */
public static MetadataContext get() { public static MetadataContext get() {
if (null == METADATA_CONTEXT.get()) { if (METADATA_CONTEXT.get() != null) {
MetadataContext metadataContext = new MetadataContext(); return METADATA_CONTEXT.get();
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);
} }
return METADATA_CONTEXT.get();
}
/** if (metadataLocalProperties == null) {
* Filter and store the transitive metadata to transitive metadata context. metadataLocalProperties = (MetadataLocalProperties) ApplicationContextAwareUtils
* @param source all metadata content .getApplicationContext().getBean("metadataLocalProperties");
* @param transitiveMetadataKeyList transitive metadata name list }
* @return result if (staticMetadataManager == null) {
*/ staticMetadataManager = (StaticMetadataManager) ApplicationContextAwareUtils
private static Map<String, String> getTransitiveMetadataMap( .getApplicationContext().getBean("metadataManager");
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));
}
} }
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. * 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. // Init ThreadLocal.
MetadataContextHolder.remove(); MetadataContextHolder.remove();
MetadataContext metadataContext = MetadataContextHolder.get(); MetadataContext metadataContext = MetadataContextHolder.get();
// Save to ThreadLocal. // Save transitive metadata to ThreadLocal.
if (!CollectionUtils.isEmpty(customMetadataMap)) { if (!CollectionUtils.isEmpty(dynamicTransitiveMetadata)) {
metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, customMetadataMap); 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); 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; package com.tencent.cloud.common.metadata.config;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.metadata.filter.gateway.MetadataFirstScgFilter; import com.tencent.cloud.common.metadata.filter.gateway.MetadataFirstScgFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -42,6 +43,11 @@ public class MetadataAutoConfiguration {
return new MetadataLocalProperties(); return new MetadataLocalProperties();
} }
@Bean
public StaticMetadataManager metadataManager(MetadataLocalProperties metadataLocalProperties) {
return new StaticMetadataManager(metadataLocalProperties);
}
/** /**
* Create when gateway application is SCG. * Create when gateway application is SCG.
*/ */

@ -71,7 +71,7 @@
<properties> <properties>
<revision>1.5.0-Hoxton.SR9-SNAPSHOT</revision> <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> <powermock.version>2.0.0</powermock.version>
<!-- Maven Plugin Versions --> <!-- Maven Plugin Versions -->

Loading…
Cancel
Save