Merge pull request #2 from Tencent/main

mcs
pull/193/head
andrew shan 3 years ago committed by GitHub
commit b00879c640
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,3 +5,4 @@
- [Feature: Support parse ratelimit rule expression labels.](https://github.com/Tencent/spring-cloud-tencent/pull/183) - [Feature: Support parse ratelimit rule expression labels.](https://github.com/Tencent/spring-cloud-tencent/pull/183)
- [Feature: Router support request label.](https://github.com/Tencent/spring-cloud-tencent/pull/165) - [Feature: Router support request label.](https://github.com/Tencent/spring-cloud-tencent/pull/165)
- [Feature: Support router expression label](https://github.com/Tencent/spring-cloud-tencent/pull/190) - [Feature: Support router expression label](https://github.com/Tencent/spring-cloud-tencent/pull/190)
- [Add metadata transfer example.](https://github.com/Tencent/spring-cloud-tencent/pull/184)

@ -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 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() { 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) { if (metadataLocalProperties == null) {
metadataLocalProperties = (MetadataLocalProperties) ApplicationContextAwareUtils metadataLocalProperties = (MetadataLocalProperties) ApplicationContextAwareUtils
.getApplicationContext().getBean("metadataLocalProperties"); .getApplicationContext().getBean("metadataLocalProperties");
} }
if (staticMetadataManager == null) {
staticMetadataManager = (StaticMetadataManager) ApplicationContextAwareUtils
.getApplicationContext().getBean("metadataManager");
}
// init custom metadata and load local metadata // init static transitive metadata
Map<String, String> transitiveMetadataMap = getTransitiveMetadataMap( MetadataContext metadataContext = new MetadataContext();
metadataLocalProperties.getContent(), metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE,
metadataLocalProperties.getTransitive()); staticMetadataManager.getMergedStaticTransitiveMetadata());
metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, transitiveMetadataMap);
METADATA_CONTEXT.set(metadataContext); METADATA_CONTEXT.set(metadataContext);
}
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));
}
}
return result;
} }
/** /**
@ -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 -->

@ -0,0 +1,118 @@
# Spring Cloud Tencent Metadata Transfer example
## 样例简介
本样例将介绍如何在Spring Cloud项目中使用```spring-cloud-starter-tencent-metadata-transfer```以使用其各项功能。
本样例包括```metadata-callee-service```、```metadata-caller-service```。
## 使用说明
### 修改配置
配置如下所示。其中,${ip}和${port}为Polaris后端服务的IP地址与端口号。
```yaml
spring:
application:
name: ${application.name}
cloud:
polaris:
address: ${ip}:${port}
```
### Maven依赖
```xml
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId>
</dependency>
```
### 启动样例
#### 启动Polaris后端服务
参考[Polaris Getting Started](https://github.com/PolarisMesh/polaris#getting-started)。
#### 启动应用
##### IDEA启动
分别启动
- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service```的```MetadataCalleeService```
- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service```的```MetadataCallerService```
##### Maven打包启动
在```spring-cloud-tencent-examples/metadata-transfer-example```下执行
```sh
mvn clean package
```
然后在```metadata-callee-service```、```metadata-caller-service```下找到生成的jar包运行
```
java -jar ${app.jar}
```
启动应用,其中${app.jar}替换为对应的jar包名。
### 元数据配置
在```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service```项目的```bootstrap.yml```配置文件中
```yaml
spring:
cloud:
tencent:
metadata:
# 定义元数据的键值对
content:
# 示例:本地元数据,默认不在链路中传递
CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL
# 示例:可传递元数据
CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE
# 指定哪个元数据的键值将沿着链接传递
transitive:
- CUSTOM-METADATA-KEY-TRANSITIVE
```
### 验证
#### 请求调用
```shell
curl -L -X GET 'http://127.0.0.1:48080/metadata/service/caller/feign/info'
```
预期返回值
```
{
"caller-metadata-contents": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE",
"CUSTOM-METADATA-KEY-LOCAL": "CUSTOM-VALUE-LOCAL"
},
"callee-transitive-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE"
},
"caller-transitive-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE"
}
}
```
返回值解析
- Key `caller-metadata-contents` 表示 `metadata-caller-service` 项目中默认配置的所有的元数据。
- Key `caller-transitive-metadata` 表示 `metadata-caller-service` 项目中指定的可以在链路中传递的元数据列表。
- Key `callee-transitive-metadata` 表示 `metadata-callee-service` 项目被 `metadata-caller-service` 调用时传递过来的上游的元数据列表。
### Wiki参考
查看 [Spring Cloud Tencent Metadata Transfer 使用指南](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Metadata-Transfer-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97) .

@ -0,0 +1,117 @@
# Spring Cloud Tencent Metadata Transfer example
## Example Introduction
This example shows how to use ```spring-cloud-starter-tencent-metadata-transfer`` in Spring Cloud project for its features.
This example contains ```metadata-callee-service```、```metadata-caller-service```.
## Instruction
### Configuration
The configuration is as the following shows. ${ip} and ${port} are Polaris backend IP address and port number.
```yaml
spring:
application:
name: ${application.name}
cloud:
polaris:
address: ${ip}:${port}
```
### Maven Dependency
```xml
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId>
</dependency>
```
### Launching Example
#### Launching Polaris Backend Service
Reference to [Polaris Getting Started](https://github.com/PolarisMesh/polaris#getting-started)
#### Launching Application
- IDEA Launching
- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service```s```MetadataCalleeService```
- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service```'s```MetadataCallerService```
- Maven Package Launching
Execute under ```spring-cloud-tencent-examples/metadata-transfer-example```
```sh
mvn clean package
```
Then find the jars under ```metadata-callee-service```、```metadata-caller-service```, and run it:
```
java -jar ${app.jar}
```
Launch application, change ${app.jar} to jar's package name.
### Metadata Configuration
In the ```bootstrap.yml``` configuration file of the ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service``` project
```yaml
spring:
cloud:
tencent:
metadata:
# Defined your metadata keys & values
content:
# Example: intransitive
CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL
# Example: transitive
CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE
# Assigned which metadata key-value will be passed along the link
transitive:
- CUSTOM-METADATA-KEY-TRANSITIVE
```
### Verify
#### Request Invoke
```shell
curl -L -X GET 'http://127.0.0.1:48080/metadata/service/caller/feign/info'
```
Expected return rate
```
{
"caller-metadata-contents": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE",
"CUSTOM-METADATA-KEY-LOCAL": "CUSTOM-VALUE-LOCAL"
},
"callee-transitive-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE"
},
"caller-transitive-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE"
}
}
```
Response value description
- Key `caller-metadata-contents` represents all metadata configured by default in the `metadata-caller-service` project.
- Key `caller-transitive-metadata` represents the list of metadata that can be passed in the link specified in the `metadata-caller-service` item.
- Key `callee-transitive-metadata` represents the list of upstream metadata passed when the `metadata-callee-service` project is called by `metadata-caller-service`.
### Wiki Reference
See [Spring Cloud Tencent Metadata Transfer Usage Document](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Metadata-Transfer-Usage-Document) for more reference .

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>metadata-transfer-example</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>metadata-callee-service</artifactId>
<name>Spring Cloud Tencent Metadata Transfer Callee Service</name>
<dependencies>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,65 @@
/*
* 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.service.callee;
import java.util.Map;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Metadata callee controller.
*
* @author Palmer Xu
*/
@RestController
@RequestMapping("/metadata/service/callee")
public class MetadataCalleeController {
private static final Logger LOG = LoggerFactory.getLogger(MetadataCalleeController.class);
@Value("${server.port:0}")
private int port;
/**
* Get information of callee.
* @return information of callee
*/
@GetMapping("/info")
public Map<String, String> info() {
LOG.info("Metadata Service Callee [{}] is called.", port);
// Get Custom Metadata From Context
MetadataContext context = MetadataContextHolder.get();
Map<String, String> customMetadataMap = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
customMetadataMap.forEach((key, value) -> {
LOG.info("Custom Metadata (Key-Value): {} : {}", key, value);
});
return customMetadataMap;
}
}

@ -0,0 +1,35 @@
/*
* 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.service.callee;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Metadata callee application.
*
* @author Palmer Xu
*/
@SpringBootApplication
public class MetadataCalleeService {
public static void main(String[] args) {
SpringApplication.run(MetadataCalleeService.class, args);
}
}

@ -0,0 +1,13 @@
server:
port: 48084
spring:
application:
name: MetadataCalleeService
cloud:
polaris:
address: grpc://127.0.0.1:8091
namespace: default
enabled: true
discovery:
enabled: true
register: true

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>metadata-transfer-example</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>metadata-caller-service</artifactId>
<name>Spring Cloud Tencent Metadata Transfer Caller Service</name>
<dependencies>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,41 @@
/*
* 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.service.caller;
import java.util.Map;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* Metadata callee feign client.
*
* @author Palmer Xu
*/
@FeignClient(value = "MetadataCalleeService",
fallback = MetadataCalleeServiceFallback.class)
public interface MetadataCalleeService {
/**
* Get information of callee.
* @return information of callee
*/
@GetMapping("/metadata/service/callee/info")
Map<String, String> info();
}

@ -0,0 +1,39 @@
/*
* 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.service.caller;
import java.util.Map;
import com.google.common.collect.Maps;
import org.springframework.stereotype.Component;
/**
* Metadata callee feign client fallback.
*
* @author Palmer Xu
*/
@Component
public class MetadataCalleeServiceFallback implements MetadataCalleeService {
@Override
public Map<String, String> info() {
return Maps.newHashMap();
}
}

@ -0,0 +1,109 @@
/*
* 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.service.caller;
import java.util.Map;
import com.google.common.collect.Maps;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* Metadata caller controller.
*
* @author Palmer Xu
*/
@RestController
@RequestMapping("/metadata/service/caller")
public class MetadataCallerController {
private final RestTemplate restTemplate;
private final MetadataCalleeService metadataCalleeService;
private final MetadataLocalProperties metadataLocalProperties;
public MetadataCallerController(RestTemplate restTemplate,
MetadataCalleeService metadataCalleeService,
MetadataLocalProperties metadataLocalProperties) {
this.restTemplate = restTemplate;
this.metadataCalleeService = metadataCalleeService;
this.metadataLocalProperties = metadataLocalProperties;
}
/**
* Get metadata info from remote service.
* @return metadata map
*/
@GetMapping("/feign/info")
public Map<String, Map<String, String>> feign() {
Map<String, Map<String, String>> ret = Maps.newHashMap();
// Call remote service with feign client
Map<String, String> calleeMetadata = metadataCalleeService.info();
ret.put("callee-transitive-metadata", calleeMetadata);
// Get Custom Metadata From Context
MetadataContext context = MetadataContextHolder.get();
Map<String, String> callerTransitiveMetadata = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
ret.put("caller-transitive-metadata", callerTransitiveMetadata);
ret.put("caller-metadata-contents", metadataLocalProperties.getContent());
return ret;
}
/**
* Get metadata information of callee.
* @return information of callee
*/
@SuppressWarnings("unchecked")
@GetMapping("/rest/info")
public Map<String, Map<String, String>> rest() {
Map<String, Map<String, String>> ret = Maps.newHashMap();
// Call remote service with RestTemplate
Map<String, String> calleeMetadata = restTemplate.getForObject(
"http://MetadataCalleeService/metadata/service/callee/info",
Map.class);
ret.put("callee-transitive-metadata", calleeMetadata);
// Get Custom Metadata From Context
MetadataContext context = MetadataContextHolder.get();
Map<String, String> callerTransitiveMetadata = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
ret.put("caller-transitive-metadata", callerTransitiveMetadata);
ret.put("caller-metadata-contents", metadataLocalProperties.getContent());
return ret;
}
/**
* health check.
* @return health check info
*/
@GetMapping("/healthCheck")
public String healthCheck() {
return "pk ok";
}
}

@ -0,0 +1,48 @@
/*
* 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.service.caller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* Metadata caller application.
*
* @author Palmer Xu
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class MetadataCallerService {
public static void main(String[] args) {
SpringApplication.run(MetadataCallerService.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

@ -0,0 +1,26 @@
server:
port: 48080
spring:
application:
name: MetadataCallerService
cloud:
polaris:
address: grpc://127.0.0.1:8091
namespace: default
enabled: true
discovery:
enabled: true
register: true
heartbeat-enabled: true
health-check-url: /metadata/service/caller/healthCheck
tencent:
metadata:
# Defined your metadata keys & values
content:
# Example: intransitive
CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL
# Example: transitive
CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE
# Assigned which metadata key-value will be passed along the link
transitive:
- CUSTOM-METADATA-KEY-TRANSITIVE

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-tencent-examples</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>metadata-transfer-example</artifactId>
<packaging>pom</packaging>
<name>Spring Cloud Starter Tencent Metadata Transfer Example</name>
<modules>
<module>metadata-callee-service</module>
<module>metadata-caller-service</module>
</modules>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId>
</dependency>
</dependencies>
</project>

@ -22,6 +22,7 @@
<module>polaris-gateway-example</module> <module>polaris-gateway-example</module>
<module>polaris-config-example</module> <module>polaris-config-example</module>
<module>polaris-router-example</module> <module>polaris-router-example</module>
<module>metadata-transfer-example</module>
</modules> </modules>
<properties> <properties>

Loading…
Cancel
Save