Add disposable metadata transfer support . (#459)

pull/474/head
VOPEN.XYZ 2 years ago committed by GitHub
parent 2155b3bd9d
commit 0f32fb47e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,3 +7,4 @@
- [Add configurable heartbeat interval support](https://github.com/Tencent/spring-cloud-tencent/pull/444) - [Add configurable heartbeat interval support](https://github.com/Tencent/spring-cloud-tencent/pull/444)
- [feat:enhance Feign and RestTemplate and support Polaris monitor.](https://github.com/Tencent/spring-cloud-tencent/pull/447) - [feat:enhance Feign and RestTemplate and support Polaris monitor.](https://github.com/Tencent/spring-cloud-tencent/pull/447)
- [Optimize feign & rest-template circuit-breaker logic](https://github.com/Tencent/spring-cloud-tencent/pull/454) - [Optimize feign & rest-template circuit-breaker logic](https://github.com/Tencent/spring-cloud-tencent/pull/454)
- [Feature: Add disposable metadata transfer support](https://github.com/Tencent/spring-cloud-tencent/pull/459)

@ -93,10 +93,10 @@
<spring.cloud.version>2021.0.3</spring.cloud.version> <spring.cloud.version>2021.0.3</spring.cloud.version>
<!-- Spring Boot --> <!-- Spring Boot -->
<spring.boot.version>2.6.9</spring.boot.version> <spring.boot.version>2.6.10</spring.boot.version>
<!-- Spring Framework --> <!-- Spring Framework -->
<spring.framework.version>5.3.21</spring.framework.version> <spring.framework.version>5.3.22</spring.framework.version>
<!-- Maven Plugin Versions --> <!-- Maven Plugin Versions -->
<jacoco.version>0.8.8</jacoco.version> <jacoco.version>0.8.8</jacoco.version>

@ -39,6 +39,8 @@ import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.WebFilterChain;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
/** /**
* Filter used for storing the metadata from upstream temporarily when web application is * Filter used for storing the metadata from upstream temporarily when web application is
@ -60,27 +62,32 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
// 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> internalTransitiveMetadata = getIntervalMetadata(serverHttpRequest, CUSTOM_METADATA);
Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(serverWebExchange); Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(serverWebExchange);
Map<String, String> mergedTransitiveMetadata = new HashMap<>(); Map<String, String> mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(internalTransitiveMetadata); mergedTransitiveMetadata.putAll(internalTransitiveMetadata);
mergedTransitiveMetadata.putAll(customTransitiveMetadata); mergedTransitiveMetadata.putAll(customTransitiveMetadata);
MetadataContextHolder.init(mergedTransitiveMetadata); Map<String, String> internalDisposableMetadata = getIntervalMetadata(serverHttpRequest, CUSTOM_DISPOSABLE_METADATA);
Map<String, String> mergedDisposableMetadata = new HashMap<>(internalDisposableMetadata);
MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata);
// Save to ServerWebExchange. // Save to ServerWebExchange.
serverWebExchange.getAttributes() serverWebExchange.getAttributes().put(
.put(MetadataConstant.HeaderName.METADATA_CONTEXT, MetadataContextHolder.get()); MetadataConstant.HeaderName.METADATA_CONTEXT,
MetadataContextHolder.get());
return webFilterChain.filter(serverWebExchange) return webFilterChain.filter(serverWebExchange)
.doOnError(throwable -> LOG.error("handle metadata[{}] error.", MetadataContextHolder.get(), throwable)) .doOnError(throwable -> LOG.error("handle metadata[{}] error.",
MetadataContextHolder.get(), throwable))
.doFinally((type) -> MetadataContextHolder.remove()); .doFinally((type) -> MetadataContextHolder.remove());
} }
private Map<String, String> getIntervalTransitiveMetadata(ServerHttpRequest serverHttpRequest) { private Map<String, String> getIntervalMetadata(ServerHttpRequest serverHttpRequest, String headerName) {
HttpHeaders httpHeaders = serverHttpRequest.getHeaders(); HttpHeaders httpHeaders = serverHttpRequest.getHeaders();
String customMetadataStr = httpHeaders.getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA); String customMetadataStr = httpHeaders.getFirst(headerName);
try { try {
if (StringUtils.hasText(customMetadataStr)) { if (StringUtils.hasText(customMetadataStr)) {
customMetadataStr = URLDecoder.decode(customMetadataStr, UTF_8); customMetadataStr = URLDecoder.decode(customMetadataStr, UTF_8);

@ -36,10 +36,13 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
/** /**
* Filter used for storing the metadata from upstream temporarily when web application is * Filter used for storing the metadata from upstream temporarily when web application is
@ -53,28 +56,27 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataServletFilter.class); private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataServletFilter.class);
@Override @Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, protected void doFilterInternal(@NonNull HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { @NonNull HttpServletResponse httpServletResponse, FilterChain filterChain)
Map<String, String> internalTransitiveMetadata = getInternalTransitiveMetadata(httpServletRequest); throws ServletException, IOException {
Map<String, String> internalTransitiveMetadata = getInternalMetadata(httpServletRequest, CUSTOM_METADATA);
Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(httpServletRequest); Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(httpServletRequest);
Map<String, String> mergedTransitiveMetadata = new HashMap<>(); Map<String, String> mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(internalTransitiveMetadata); mergedTransitiveMetadata.putAll(internalTransitiveMetadata);
mergedTransitiveMetadata.putAll(customTransitiveMetadata); mergedTransitiveMetadata.putAll(customTransitiveMetadata);
try { Map<String, String> internalDisposableMetadata = getInternalMetadata(httpServletRequest, CUSTOM_DISPOSABLE_METADATA);
MetadataContextHolder.init(mergedTransitiveMetadata); Map<String, String> mergedDisposableMetadata = new HashMap<>(internalDisposableMetadata);
filterChain.doFilter(httpServletRequest, httpServletResponse); MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata);
}
catch (IOException | ServletException | RuntimeException e) { filterChain.doFilter(httpServletRequest, httpServletResponse);
throw e;
}
} }
private Map<String, String> getInternalTransitiveMetadata(HttpServletRequest httpServletRequest) { private Map<String, String> getInternalMetadata(HttpServletRequest httpServletRequest, String headerName) {
// Get custom metadata string from http header. // Get custom metadata string from http header.
String customMetadataStr = httpServletRequest.getHeader(MetadataConstant.HeaderName.CUSTOM_METADATA); String customMetadataStr = httpServletRequest.getHeader(headerName);
try { try {
if (StringUtils.hasText(customMetadataStr)) { if (StringUtils.hasText(customMetadataStr)) {
customMetadataStr = URLDecoder.decode(customMetadataStr, UTF_8); customMetadataStr = URLDecoder.decode(customMetadataStr, UTF_8);

@ -19,7 +19,7 @@
package com.tencent.cloud.metadata.core; package com.tencent.cloud.metadata.core;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; 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;
@ -33,9 +33,12 @@ import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA; import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
import static java.net.URLEncoder.encode;
/** /**
* 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
@ -57,16 +60,37 @@ public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor,
// get metadata of current thread // get metadata of current thread
MetadataContext metadataContext = MetadataContextHolder.get(); MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
Map<String, String> disposableMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_DISPOSABLE);
if (!CollectionUtils.isEmpty(customMetadata)) { // Clean up one-time metadata coming from upstream .
String encodedTransitiveMetadata = JacksonUtils.serialize2Json(customMetadata); Map<String, String> newestCustomMetadata = new HashMap<>();
requestTemplate.removeHeader(CUSTOM_METADATA); customMetadata.forEach((key, value) -> {
if (!disposableMetadata.containsKey(key)) {
newestCustomMetadata.put(key, value);
}
});
this.buildMetadataHeader(requestTemplate, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
// process custom metadata finally
this.buildMetadataHeader(requestTemplate, newestCustomMetadata, CUSTOM_METADATA);
}
/**
* Set metadata into the request header for {@link RestTemplate} .
* @param requestTemplate instance of {@link RestTemplate}
* @param metadata metadata map .
* @param headerName target metadata http header name .
*/
private void buildMetadataHeader(RequestTemplate requestTemplate, Map<String, String> metadata, String headerName) {
if (!CollectionUtils.isEmpty(metadata)) {
String encodedMetadata = JacksonUtils.serialize2Json(metadata);
requestTemplate.removeHeader(headerName);
try { try {
requestTemplate.header(CUSTOM_METADATA, URLEncoder.encode(encodedTransitiveMetadata, UTF_8)); requestTemplate.header(headerName, encode(encodedMetadata, UTF_8));
} }
catch (UnsupportedEncodingException e) { catch (UnsupportedEncodingException e) {
LOG.error("Set header failed.", e); LOG.error("Set header failed.", e);
requestTemplate.header(CUSTOM_METADATA, encodedTransitiveMetadata); requestTemplate.header(headerName, encodedMetadata);
} }
} }
} }

@ -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.URLEncoder; import java.net.URLEncoder;
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;
@ -33,9 +34,12 @@ import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution; 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.lang.NonNull;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
/** /**
* 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
@ -51,22 +55,44 @@ public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRe
} }
@Override @Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, public ClientHttpResponse intercept(@NonNull HttpRequest httpRequest, @NonNull byte[] bytes,
ClientHttpRequestExecution clientHttpRequestExecution) throws IOException { @NonNull ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
// get metadata of current thread // get metadata of current thread
MetadataContext metadataContext = MetadataContextHolder.get(); MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
Map<String, String> disposableMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_DISPOSABLE);
if (!CollectionUtils.isEmpty(customMetadata)) { Map<String, String> newestCustomMetadata = new HashMap<>();
String encodedTransitiveMetadata = JacksonUtils.serialize2Json(customMetadata); customMetadata.forEach((key, value) -> {
if (!disposableMetadata.containsKey(key)) {
newestCustomMetadata.put(key, value);
}
});
// build custom disposable metadata request header
this.buildMetadataHeader(httpRequest, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
// build custom metadata request header
this.buildMetadataHeader(httpRequest, newestCustomMetadata, CUSTOM_METADATA);
return clientHttpRequestExecution.execute(httpRequest, bytes);
}
/**
* Set metadata into the request header for {@link HttpRequest} .
*
* @param request instance of {@link HttpRequest}
* @param metadata metadata map .
* @param headerName target metadata http header name .
*/
private void buildMetadataHeader(HttpRequest request, Map<String, String> metadata, String headerName) {
if (!CollectionUtils.isEmpty(metadata)) {
String encodedMetadata = JacksonUtils.serialize2Json(metadata);
try { try {
httpRequest.getHeaders().set(MetadataConstant.HeaderName.CUSTOM_METADATA, request.getHeaders().set(headerName, URLEncoder.encode(encodedMetadata, UTF_8));
URLEncoder.encode(encodedTransitiveMetadata, UTF_8));
} }
catch (UnsupportedEncodingException e) { catch (UnsupportedEncodingException e) {
httpRequest.getHeaders().set(MetadataConstant.HeaderName.CUSTOM_METADATA, encodedTransitiveMetadata); request.getHeaders().set(headerName, encodedMetadata);
} }
} }
return clientHttpRequestExecution.execute(httpRequest, bytes);
} }
} }

@ -20,6 +20,7 @@ package com.tencent.cloud.metadata.core;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
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;
@ -36,6 +37,8 @@ import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
import static org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER; import static org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER;
/** /**
@ -59,22 +62,42 @@ public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered {
// get metadata of current thread // get metadata of current thread
MetadataContext metadataContext = exchange.getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT); MetadataContext metadataContext = exchange.getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT);
// add new metadata and cover old
if (metadataContext == null) { if (metadataContext == null) {
metadataContext = MetadataContextHolder.get(); metadataContext = MetadataContextHolder.get();
} }
Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Map<String, String> customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
if (!CollectionUtils.isEmpty(customMetadata)) { Map<String, String> disposableMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_DISPOSABLE);
String metadataStr = JacksonUtils.serialize2Json(customMetadata);
// Clean upstream disposable metadata.
Map<String, String> newestCustomMetadata = new HashMap<>();
customMetadata.forEach((key, value) -> {
if (!disposableMetadata.containsKey(key)) {
newestCustomMetadata.put(key, value);
}
});
this.buildMetadataHeader(builder, newestCustomMetadata, CUSTOM_METADATA);
this.buildMetadataHeader(builder, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
return chain.filter(exchange.mutate().request(builder.build()).build());
}
/**
* Set metadata into the request header for {@link ServerHttpRequest.Builder} .
* @param builder instance of {@link ServerHttpRequest.Builder}
* @param metadata metadata map .
* @param headerName target metadata http header name .
*/
private void buildMetadataHeader(ServerHttpRequest.Builder builder, Map<String, String> metadata, String headerName) {
if (!CollectionUtils.isEmpty(metadata)) {
String encodedMetadata = JacksonUtils.serialize2Json(metadata);
try { try {
builder.header(MetadataConstant.HeaderName.CUSTOM_METADATA, URLEncoder.encode(metadataStr, UTF_8)); builder.header(headerName, URLEncoder.encode(encodedMetadata, UTF_8));
} }
catch (UnsupportedEncodingException e) { catch (UnsupportedEncodingException e) {
builder.header(MetadataConstant.HeaderName.CUSTOM_METADATA, metadataStr); builder.header(headerName, encodedMetadata);
} }
} }
return chain.filter(exchange.mutate().request(builder.build()).build());
} }
} }

@ -26,6 +26,12 @@ import org.springframework.core.Ordered;
*/ */
public final class MetadataConstant { public final class MetadataConstant {
/**
* Default Private Constructor.
*/
private MetadataConstant() {
}
/** /**
* Order of filter, interceptor, ... * Order of filter, interceptor, ...
*/ */
@ -57,6 +63,11 @@ public final class MetadataConstant {
*/ */
public static final String CUSTOM_METADATA = "SCT-CUSTOM-METADATA"; public static final String CUSTOM_METADATA = "SCT-CUSTOM-METADATA";
/**
* Custom Disposable Metadata.
*/
public static final String CUSTOM_DISPOSABLE_METADATA = "SCT-CUSTOM-DISPOSABLE-METADATA";
/** /**
* System Metadata. * System Metadata.
*/ */

@ -40,6 +40,17 @@ public class MetadataContext {
* transitive context. * transitive context.
*/ */
public static final String FRAGMENT_TRANSITIVE = "transitive"; public static final String FRAGMENT_TRANSITIVE = "transitive";
/**
* disposable Context.
*/
public static final String FRAGMENT_DISPOSABLE = "disposable";
/**
* upstream disposable Context.
*/
public static final String FRAGMENT_UPSTREAM_DISPOSABLE = "upstream-disposable";
private static final Logger LOG = LoggerFactory.getLogger(MetadataContext.class); private static final Logger LOG = LoggerFactory.getLogger(MetadataContext.class);
/** /**
* Namespace of local instance. * Namespace of local instance.

@ -20,12 +20,17 @@ package com.tencent.cloud.common.metadata;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils; import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_DISPOSABLE;
import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_TRANSITIVE;
import static com.tencent.cloud.common.metadata.MetadataContext.FRAGMENT_UPSTREAM_DISPOSABLE;
/** /**
* Metadata Context Holder. * Metadata Context Holder.
* *
@ -36,6 +41,7 @@ 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 static StaticMetadataManager staticMetadataManager;
private MetadataContextHolder() { private MetadataContextHolder() {
@ -51,24 +57,55 @@ public final class MetadataContextHolder {
} }
if (metadataLocalProperties == null) { if (metadataLocalProperties == null) {
metadataLocalProperties = (MetadataLocalProperties) ApplicationContextAwareUtils metadataLocalProperties = ApplicationContextAwareUtils.getApplicationContext().getBean(MetadataLocalProperties.class);
.getApplicationContext().getBean("metadataLocalProperties");
} }
if (staticMetadataManager == null) { if (staticMetadataManager == null) {
staticMetadataManager = (StaticMetadataManager) ApplicationContextAwareUtils staticMetadataManager = ApplicationContextAwareUtils.getApplicationContext().getBean(StaticMetadataManager.class);
.getApplicationContext().getBean("metadataManager");
} }
// init static transitive metadata // init static transitive metadata
MetadataContext metadataContext = new MetadataContext(); MetadataContext metadataContext = new MetadataContext();
metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, metadataContext.putFragmentContext(FRAGMENT_TRANSITIVE, staticMetadataManager.getMergedStaticTransitiveMetadata());
staticMetadataManager.getMergedStaticTransitiveMetadata()); metadataContext.putFragmentContext(FRAGMENT_DISPOSABLE, staticMetadataManager.getMergedStaticDisposableMetadata());
METADATA_CONTEXT.set(metadataContext); METADATA_CONTEXT.set(metadataContext);
return METADATA_CONTEXT.get(); return METADATA_CONTEXT.get();
} }
/**
* Get disposable metadata value from thread local .
* @param key metadata key .
* @param upstream upstream disposable , otherwise will return local static disposable metadata .
* @return target disposable metadata value .
*/
public static Optional<String> getDisposableMetadata(String key, boolean upstream) {
MetadataContext context = get();
if (upstream) {
return Optional.ofNullable(context.getContext(FRAGMENT_UPSTREAM_DISPOSABLE, key));
}
else {
return Optional.ofNullable(context.getContext(FRAGMENT_DISPOSABLE, key));
}
}
/**
* Get all disposable metadata value from thread local .
* @param upstream upstream disposable , otherwise will return local static disposable metadata .
* @return target disposable metadata value .
*/
public static Map<String, String> getAllDisposableMetadata(boolean upstream) {
Map<String, String> disposables = new HashMap<>();
MetadataContext context = get();
if (upstream) {
disposables.putAll(context.getFragmentContext(FRAGMENT_UPSTREAM_DISPOSABLE));
}
else {
disposables.putAll(context.getFragmentContext(FRAGMENT_DISPOSABLE));
}
return Collections.unmodifiableMap(disposables);
}
/** /**
* Set metadata context. * Set metadata context.
* @param metadataContext metadata context * @param metadataContext metadata context
@ -80,21 +117,26 @@ public final class MetadataContextHolder {
/** /**
* Save metadata map to thread local. * Save metadata map to thread local.
* @param dynamicTransitiveMetadata custom metadata collection * @param dynamicTransitiveMetadata custom metadata collection
* @param dynamicDisposableMetadata custom disposable metadata connection
*/ */
public static void init(Map<String, String> dynamicTransitiveMetadata) { public static void init(Map<String, String> dynamicTransitiveMetadata, Map<String, String> dynamicDisposableMetadata) {
// Init ThreadLocal. // Init ThreadLocal.
MetadataContextHolder.remove(); MetadataContextHolder.remove();
MetadataContext metadataContext = MetadataContextHolder.get(); MetadataContext metadataContext = MetadataContextHolder.get();
// Save transitive metadata to ThreadLocal. // Save transitive metadata to ThreadLocal.
if (!CollectionUtils.isEmpty(dynamicTransitiveMetadata)) { if (!CollectionUtils.isEmpty(dynamicTransitiveMetadata)) {
Map<String, String> staticTransitiveMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Map<String, String> staticTransitiveMetadata = metadataContext.getFragmentContext(FRAGMENT_TRANSITIVE);
Map<String, String> mergedTransitiveMetadata = new HashMap<>(); Map<String, String> mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(staticTransitiveMetadata); mergedTransitiveMetadata.putAll(staticTransitiveMetadata);
mergedTransitiveMetadata.putAll(dynamicTransitiveMetadata); mergedTransitiveMetadata.putAll(dynamicTransitiveMetadata);
metadataContext.putFragmentContext(FRAGMENT_TRANSITIVE, Collections.unmodifiableMap(mergedTransitiveMetadata));
Map<String, String> mergedDisposableMetadata = new HashMap<>(dynamicDisposableMetadata);
metadataContext.putFragmentContext(FRAGMENT_UPSTREAM_DISPOSABLE, Collections.unmodifiableMap(mergedDisposableMetadata));
metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, Map<String, String> staticDisposableMetadata = metadataContext.getFragmentContext(FRAGMENT_DISPOSABLE);
Collections.unmodifiableMap(mergedTransitiveMetadata)); metadataContext.putFragmentContext(FRAGMENT_DISPOSABLE, Collections.unmodifiableMap(staticDisposableMetadata));
} }
MetadataContextHolder.set(metadataContext); MetadataContextHolder.set(metadataContext);
} }

@ -54,18 +54,22 @@ public class StaticMetadataManager {
private static final String ENV_METADATA_PREFIX = "SCT_METADATA_CONTENT_"; 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 int ENV_METADATA_PREFIX_LENGTH = ENV_METADATA_PREFIX.length();
private static final String ENV_METADATA_CONTENT_TRANSITIVE = "SCT_METADATA_CONTENT_TRANSITIVE"; private static final String ENV_METADATA_CONTENT_TRANSITIVE = "SCT_METADATA_CONTENT_TRANSITIVE";
private static final String ENV_METADATA_CONTENT_DISPOSABLE = "SCT_METADATA_CONTENT_DISPOSABLE";
private static final String ENV_METADATA_ZONE = "SCT_METADATA_ZONE"; private static final String ENV_METADATA_ZONE = "SCT_METADATA_ZONE";
private static final String ENV_METADATA_REGION = "SCT_METADATA_REGION"; private static final String ENV_METADATA_REGION = "SCT_METADATA_REGION";
private static final String ENV_METADATA_CAMPUS = "SCT_METADATA_CAMPUS"; private static final String ENV_METADATA_CAMPUS = "SCT_METADATA_CAMPUS";
private Map<String, String> envMetadata; private Map<String, String> envMetadata;
private Map<String, String> envTransitiveMetadata; private Map<String, String> envTransitiveMetadata;
private Map<String, String> envDisposableMetadata;
private Map<String, String> configMetadata; private Map<String, String> configMetadata;
private Map<String, String> configTransitiveMetadata; private Map<String, String> configTransitiveMetadata;
private Map<String, String> configDisposableMetadata;
private Map<String, String> customSPIMetadata; private Map<String, String> customSPIMetadata;
private Map<String, String> customSPITransitiveMetadata; private Map<String, String> customSPITransitiveMetadata;
private Map<String, String> customSPIDisposableMetadata;
private Map<String, String> mergedStaticMetadata; private Map<String, String> mergedStaticMetadata;
private Map<String, String> mergedStaticTransitiveMetadata; private Map<String, String> mergedStaticTransitiveMetadata;
private Map<String, String> mergedStaticDisposableMetadata;
private String zone; private String zone;
private String region; private String region;
private String campus; private String campus;
@ -85,6 +89,7 @@ public class StaticMetadataManager {
LOGGER.info("[SCT] Loaded static metadata info. {}", this); LOGGER.info("[SCT] Loaded static metadata info. {}", this);
} }
@SuppressWarnings("DuplicatedCode")
private void parseEnvMetadata() { private void parseEnvMetadata() {
Map<String, String> allEnvs = System.getenv(); Map<String, String> allEnvs = System.getenv();
@ -118,27 +123,54 @@ public class StaticMetadataManager {
} }
} }
envTransitiveMetadata = Collections.unmodifiableMap(envTransitiveMetadata); envTransitiveMetadata = Collections.unmodifiableMap(envTransitiveMetadata);
envDisposableMetadata = new HashMap<>();
// parse disposable metadata
String disposableKeys = allEnvs.get(ENV_METADATA_CONTENT_DISPOSABLE);
if (StringUtils.isNotBlank(disposableKeys)) {
String[] keyArr = StringUtils.split(disposableKeys, ",");
if (keyArr != null && keyArr.length > 0) {
for (String key : keyArr) {
String value = envMetadata.get(key);
if (StringUtils.isNotBlank(value)) {
envDisposableMetadata.put(key, value);
}
}
}
}
envDisposableMetadata = Collections.unmodifiableMap(envDisposableMetadata);
} }
private void parseConfigMetadata(MetadataLocalProperties metadataLocalProperties) { private void parseConfigMetadata(MetadataLocalProperties metadataLocalProperties) {
Map<String, String> allMetadata = metadataLocalProperties.getContent(); Map<String, String> allMetadata = metadataLocalProperties.getContent();
List<String> transitiveKeys = metadataLocalProperties.getTransitive(); List<String> transitiveKeys = metadataLocalProperties.getTransitive();
List<String> disposableKeys = metadataLocalProperties.getDisposable();
Map<String, String> result = new HashMap<>(); Map<String, String> transitiveResult = new HashMap<>();
for (String key : transitiveKeys) { for (String key : transitiveKeys) {
if (allMetadata.containsKey(key)) { if (allMetadata.containsKey(key)) {
result.put(key, allMetadata.get(key)); transitiveResult.put(key, allMetadata.get(key));
} }
} }
configTransitiveMetadata = Collections.unmodifiableMap(result); Map<String, String> disposableResult = new HashMap<>();
for (String key : disposableKeys) {
if (allMetadata.containsKey(key)) {
disposableResult.put(key, allMetadata.get(key));
}
}
configTransitiveMetadata = Collections.unmodifiableMap(transitiveResult);
configDisposableMetadata = Collections.unmodifiableMap(disposableResult);
configMetadata = Collections.unmodifiableMap(allMetadata); configMetadata = Collections.unmodifiableMap(allMetadata);
} }
@SuppressWarnings("DuplicatedCode")
private void parseCustomMetadata(InstanceMetadataProvider instanceMetadataProvider) { private void parseCustomMetadata(InstanceMetadataProvider instanceMetadataProvider) {
if (instanceMetadataProvider == null) { if (instanceMetadataProvider == null) {
customSPIMetadata = Collections.emptyMap(); customSPIMetadata = Collections.emptyMap();
customSPITransitiveMetadata = Collections.emptyMap(); customSPITransitiveMetadata = Collections.emptyMap();
customSPIDisposableMetadata = Collections.emptyMap();
return; return;
} }
@ -162,6 +194,17 @@ public class StaticMetadataManager {
} }
} }
customSPITransitiveMetadata = Collections.unmodifiableMap(transitiveMetadata); customSPITransitiveMetadata = Collections.unmodifiableMap(transitiveMetadata);
Set<String> disposableKeys = instanceMetadataProvider.getDisposableMetadataKeys();
Map<String, String> disposableMetadata = new HashMap<>();
if (!CollectionUtils.isEmpty(disposableKeys)) {
for (String key : disposableKeys) {
if (customSPIMetadata.containsKey(key)) {
disposableMetadata.put(key, customSPIMetadata.get(key));
}
}
}
customSPIDisposableMetadata = Collections.unmodifiableMap(disposableMetadata);
} }
private void merge() { private void merge() {
@ -173,15 +216,19 @@ public class StaticMetadataManager {
mergedMetadataResult.putAll(customSPIMetadata); mergedMetadataResult.putAll(customSPIMetadata);
// set location info as metadata // set location info as metadata
mergedMetadataResult.putAll(getLocationMetadata()); mergedMetadataResult.putAll(getLocationMetadata());
this.mergedStaticMetadata = Collections.unmodifiableMap(mergedMetadataResult); this.mergedStaticMetadata = Collections.unmodifiableMap(mergedMetadataResult);
Map<String, String> mergedTransitiveMetadataResult = new HashMap<>(); Map<String, String> mergedTransitiveMetadataResult = new HashMap<>();
mergedTransitiveMetadataResult.putAll(configTransitiveMetadata); mergedTransitiveMetadataResult.putAll(configTransitiveMetadata);
mergedTransitiveMetadataResult.putAll(envTransitiveMetadata); mergedTransitiveMetadataResult.putAll(envTransitiveMetadata);
mergedTransitiveMetadataResult.putAll(customSPITransitiveMetadata); mergedTransitiveMetadataResult.putAll(customSPITransitiveMetadata);
this.mergedStaticTransitiveMetadata = Collections.unmodifiableMap(mergedTransitiveMetadataResult); this.mergedStaticTransitiveMetadata = Collections.unmodifiableMap(mergedTransitiveMetadataResult);
Map<String, String> mergedDisposableMetadataResult = new HashMap<>();
mergedDisposableMetadataResult.putAll(configDisposableMetadata);
mergedDisposableMetadataResult.putAll(envDisposableMetadata);
mergedDisposableMetadataResult.putAll(customSPIDisposableMetadata);
this.mergedStaticDisposableMetadata = Collections.unmodifiableMap(mergedDisposableMetadataResult);
} }
private void parseLocationMetadata(MetadataLocalProperties metadataLocalProperties, private void parseLocationMetadata(MetadataLocalProperties metadataLocalProperties,
@ -228,6 +275,10 @@ public class StaticMetadataManager {
return envTransitiveMetadata; return envTransitiveMetadata;
} }
public Map<String, String> getEnvDisposableMetadata() {
return envDisposableMetadata;
}
public Map<String, String> getAllConfigMetadata() { public Map<String, String> getAllConfigMetadata() {
return configMetadata; return configMetadata;
} }
@ -236,6 +287,10 @@ public class StaticMetadataManager {
return configTransitiveMetadata; return configTransitiveMetadata;
} }
public Map<String, String> getConfigDisposableMetadata() {
return configDisposableMetadata;
}
public Map<String, String> getAllCustomMetadata() { public Map<String, String> getAllCustomMetadata() {
return customSPIMetadata; return customSPIMetadata;
} }
@ -244,6 +299,10 @@ public class StaticMetadataManager {
return customSPITransitiveMetadata; return customSPITransitiveMetadata;
} }
public Map<String, String> getCustomSPIDisposableMetadata() {
return customSPIDisposableMetadata;
}
public Map<String, String> getMergedStaticMetadata() { public Map<String, String> getMergedStaticMetadata() {
return mergedStaticMetadata; return mergedStaticMetadata;
} }
@ -252,6 +311,10 @@ public class StaticMetadataManager {
return mergedStaticTransitiveMetadata; return mergedStaticTransitiveMetadata;
} }
public Map<String, String> getMergedStaticDisposableMetadata() {
return mergedStaticDisposableMetadata;
}
public String getZone() { public String getZone() {
return zone; return zone;
} }

@ -43,6 +43,11 @@ public class MetadataLocalProperties {
*/ */
private List<String> transitive; private List<String> transitive;
/**
* A disposable metadata key list .
*/
private List<String> disposable;
public Map<String, String> getContent() { public Map<String, String> getContent() {
if (CollectionUtils.isEmpty(content)) { if (CollectionUtils.isEmpty(content)) {
content = new HashMap<>(); content = new HashMap<>();
@ -64,4 +69,15 @@ public class MetadataLocalProperties {
public void setTransitive(List<String> transitive) { public void setTransitive(List<String> transitive) {
this.transitive = transitive; this.transitive = transitive;
} }
public List<String> getDisposable() {
if (CollectionUtils.isEmpty(disposable)) {
disposable = new ArrayList<>();
}
return disposable;
}
public void setDisposable(List<String> disposable) {
this.disposable = disposable;
}
} }

@ -45,9 +45,14 @@ public class PolarisMetadataEndpoint {
result.put("Env", staticMetadataManager.getAllEnvMetadata()); result.put("Env", staticMetadataManager.getAllEnvMetadata());
result.put("EnvTransitive", staticMetadataManager.getEnvTransitiveMetadata()); result.put("EnvTransitive", staticMetadataManager.getEnvTransitiveMetadata());
result.put("ConfigTransitive", staticMetadataManager.getConfigTransitiveMetadata()); result.put("ConfigTransitive", staticMetadataManager.getConfigTransitiveMetadata());
result.put("ConfigDisposable", staticMetadataManager.getConfigDisposableMetadata());
result.put("Config", staticMetadataManager.getAllConfigMetadata()); result.put("Config", staticMetadataManager.getAllConfigMetadata());
result.put("MergeStatic", staticMetadataManager.getMergedStaticMetadata()); result.put("MergeStatic", staticMetadataManager.getMergedStaticMetadata());
result.put("CustomSPI", staticMetadataManager.getCustomSPITransitiveMetadata()); result.put("MergeStaticTransitive", staticMetadataManager.getMergedStaticTransitiveMetadata());
result.put("MergeStaticDisposable", staticMetadataManager.getMergedStaticDisposableMetadata());
result.put("CustomSPI", staticMetadataManager.getAllCustomMetadata());
result.put("CustomSPITransitive", staticMetadataManager.getCustomSPITransitiveMetadata());
result.put("CustomSPIDisposable", staticMetadataManager.getCustomSPIDisposableMetadata());
result.put("zone", staticMetadataManager.getZone()); result.put("zone", staticMetadataManager.getZone());
result.put("region", staticMetadataManager.getRegion()); result.put("region", staticMetadataManager.getRegion());
result.put("campus", staticMetadataManager.getCampus()); result.put("campus", staticMetadataManager.getCampus());

@ -43,6 +43,13 @@ public interface InstanceMetadataProvider {
return Collections.emptySet(); return Collections.emptySet();
} }
/**
* @return the keys of disposable metadata.
*/
default Set<String> getDisposableMetadataKeys() {
return Collections.emptySet();
}
/** /**
* The region of current instance. * The region of current instance.
* *

@ -22,6 +22,7 @@ import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -51,8 +52,25 @@ public final class JacksonUtils {
* @return Json String * @return Json String
*/ */
public static <T> String serialize2Json(T object) { public static <T> String serialize2Json(T object) {
return serialize2Json(object, false);
}
/**
* Object to Json.
* @param object object to be serialized
* @param pretty pretty print
* @param <T> type of object
* @return Json String
*/
public static <T> String serialize2Json(T object, boolean pretty) {
try { try {
return OM.writeValueAsString(object); if (pretty) {
ObjectWriter objectWriter = OM.writerWithDefaultPrettyPrinter();
return objectWriter.writeValueAsString(object);
}
else {
return OM.writeValueAsString(object);
}
} }
catch (JsonProcessingException e) { catch (JsonProcessingException e) {
LOG.error("Object to Json failed. {}", object, e); LOG.error("Object to Json failed. {}", object, e);

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

@ -4,7 +4,7 @@
本样例将介绍如何在Spring Cloud项目中使用```spring-cloud-starter-tencent-metadata-transfer```以使用其各项功能。 本样例将介绍如何在Spring Cloud项目中使用```spring-cloud-starter-tencent-metadata-transfer```以使用其各项功能。
本样例包括```metadata-callee-service```、```metadata-caller-service```。 本样例包括```metadata-frontend```、```metadata-middle```、```metadata-backend```。
## 使用说明 ## 使用说明
@ -41,8 +41,9 @@ spring:
##### IDEA启动 ##### IDEA启动
分别启动 分别启动
- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service```的```MetadataCalleeService``` - ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-frontend```的```MetadataFrontendService```
- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service```的```MetadataCallerService``` - ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-middle```的```MetadataMiddleService```
- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-backend```的```MetadataBackendService```
##### Maven打包启动 ##### Maven打包启动
@ -53,7 +54,7 @@ spring:
mvn clean package mvn clean package
``` ```
然后在```metadata-callee-service```、```metadata-caller-service```下找到生成的jar包运行 然后在```metadata-frontend```、```metadata-middle```、```metadata-backend```下找到生成的jar包运行
``` ```
java -jar ${app.jar} java -jar ${app.jar}
@ -63,7 +64,7 @@ java -jar ${app.jar}
### 元数据配置 ### 元数据配置
在```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service```项目的```bootstrap.yml```配置文件中 - 在```spring-cloud-tencent-examples/metadata-transfer-example/metadata-frontend```项目的```bootstrap.yml```配置文件中
```yaml ```yaml
spring: spring:
@ -76,10 +77,37 @@ spring:
CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL
# 示例:可传递元数据 # 示例:可传递元数据
CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE
# 示例:一次性元数据
CUSTOM-METADATA-KEY-DISPOSABLE: CUSTOM-VALUE-DISPOSABLE-FRONTEND
# 指定哪个元数据的键值将沿着链接传递 # 指定哪个元数据的键值将沿着链接传递
transitive: transitive:
- CUSTOM-METADATA-KEY-TRANSITIVE - CUSTOM-METADATA-KEY-TRANSITIVE
# 指定哪个元数据的键值只进行一次性传递(一跳)
disposable:
- CUSTOM-METADATA-KEY-DISPOSABLE
```
- 在```spring-cloud-tencent-examples/metadata-transfer-example/metadata-frontend```项目的```bootstrap.yml```配置文件中
```yaml
spring:
cloud:
tencent:
metadata:
# 定义元数据的键值对
content:
# 示例:本地元数据,默认不在链路中传递
CUSTOM-METADATA-KEY-LOCAL-2: CUSTOM-VALUE-LOCAL-2
# 示例:可传递元数据
CUSTOM-METADATA-KEY-TRANSITIVE-2: CUSTOM-VALUE-TRANSITIVE-2
# 示例:一次性元数据
CUSTOM-METADATA-KEY-DISPOSABLE: CUSTOM-VALUE-DISPOSABLE-MIDDLE
# 指定哪个元数据的键值将沿着链接传递
transitive:
- CUSTOM-METADATA-KEY-TRANSITIVE-2
# 指定哪个元数据的键值只进行一次性传递(一跳)
disposable:
- CUSTOM-METADATA-KEY-DISPOSABLE
``` ```
### 验证 ### 验证
@ -87,31 +115,85 @@ spring:
#### 请求调用 #### 请求调用
```shell ```shell
curl -L -X GET 'http://127.0.0.1:48080/metadata/service/caller/feign/info' curl -L -X GET 'http://127.0.0.1:48080/metadata/service/frontend/feign/info'
``` ```
预期返回值 预期返回值
``` ```json
{ {
"caller-metadata-contents": { "frontend-transitive-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE"
},
"frontend-upstream-disposable-metadata": {
},
"frontend-local-disposable-metadata": {
"CUSTOM-METADATA-KEY-DISPOSABLE": "CUSTOM-VALUE-DISPOSABLE-FRONTEND"
},
"middle-transitive-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE", "CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE",
"CUSTOM-METADATA-KEY-LOCAL": "CUSTOM-VALUE-LOCAL" "CUSTOM-METADATA-KEY-TRANSITIVE-2": "CUSTOM-VALUE-TRANSITIVE-2"
}, },
"callee-transitive-metadata": { "middle-upstream-disposable-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE" "CUSTOM-METADATA-KEY-DISPOSABLE": "CUSTOM-VALUE-DISPOSABLE-FRONTEND"
}, },
"caller-transitive-metadata": { "middle-local-disposable-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE" "CUSTOM-METADATA-KEY-DISPOSABLE": "CUSTOM-VALUE-DISPOSABLE-MIDDLE"
},
"backend-transitive-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE",
"CUSTOM-METADATA-KEY-TRANSITIVE-2": "CUSTOM-VALUE-TRANSITIVE-2"
},
"backend-upstream-disposable-metadata": {
"CUSTOM-METADATA-KEY-DISPOSABLE": "CUSTOM-VALUE-DISPOSABLE-MIDDLE"
},
"backend-local-disposable-metadata": {
} }
} }
``` ```
返回值解析 返回值解析
- Key `caller-metadata-contents` 表示 `metadata-caller-service` 项目中默认配置的所有的元数据。 > `*`(星号),代表示例中的`frontend`、`middle`、`backend`。
- Key `caller-transitive-metadata` 表示 `metadata-caller-service` 项目中指定的可以在链路中传递的元数据列表。
- Key `callee-transitive-metadata` 表示 `metadata-callee-service` 项目被 `metadata-caller-service` 调用时传递过来的上游的元数据列表。 - Key `*-transitive-metadata` 表示服务中默认配置的所有的可传递(全链路)的元数据。
- Key `*-upstream-disposable-metadata` 表示服务中从上游请求中获取到的一次性传递的元数据。
- Key `*-local-disposable-metadata` 表示当前服务配置的往下游传递的一次性的元数据。
### 如何通过Api获取传递的元数据
- 获取全局传递的元数据
```java
MetadataContext context = MetadataContextHolder.get();
Map<String, String> customMetadataMap = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
customMetadataMap.forEach((key, value) -> {
// ...
});
```
- 获取上游传递过来的一次性元数据
```java
Map<String, String> upstreamDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(true);
upstreamDisposableMetadatas.forEach((key, value) -> {
// ...
});
```
- 获取本地配置的一次性元数据
```java
Map<String, String> localDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(false);
localDisposableMetadatas.forEach((key, value) -> {
// ...
});
```
### Wiki参考 ### Wiki参考

@ -2,9 +2,10 @@
## Example Introduction ## Example Introduction
This example shows how to use ```spring-cloud-starter-tencent-metadata-transfer``` in Spring Cloud project for its features. 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```. This example contains ```metadata-frontend```、```metadata-middle```、```metadata-backend```.
## Instruction ## Instruction
@ -40,8 +41,10 @@ Reference to [Polaris Getting Started](https://github.com/PolarisMesh/polaris#ge
- IDEA Launching - IDEA Launching
- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service```s```MetadataCalleeService``` - ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-frontend```'s ```MetadataFrontendService```
- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service```'s```MetadataCallerService``` - ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-middle```'s ```MetadataMiddleService```
- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-backend```'s ```MetadataBackendService```
- Maven Package Launching - Maven Package Launching
@ -51,7 +54,7 @@ Execute under ```spring-cloud-tencent-examples/metadata-transfer-example```
mvn clean package mvn clean package
``` ```
Then find the jars under ```metadata-callee-service```、```metadata-caller-service```, and run it: Then find the jars under ```metadata-frontend```、```metadata-middle```、```metadata-backend```, and run it:
``` ```
java -jar ${app.jar} java -jar ${app.jar}
@ -62,7 +65,7 @@ Launch application, change ${app.jar} to jar's package name.
### Metadata Configuration ### Metadata Configuration
In the ```bootstrap.yml``` configuration file of the ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service``` project - In the ```bootstrap.yml``` configuration file of the ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-frontend``` project
```yaml ```yaml
spring: spring:
@ -75,9 +78,38 @@ spring:
CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL
# Example: transitive # Example: transitive
CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE
# Example: disposable
CUSTOM-METADATA-KEY-DISPOSABLE: CUSTOM-VALUE-DISPOSABLE-FRONTEND
# Assigned which metadata key-value will be passed along the link # Assigned which metadata key-value will be passed along the link
transitive: transitive:
- CUSTOM-METADATA-KEY-TRANSITIVE - CUSTOM-METADATA-KEY-TRANSITIVE
# Specify which metadata key value will be passed only once (one-step)
disposable:
- CUSTOM-METADATA-KEY-DISPOSABLE
```
- In the ```bootstrap.yml``` configuration file of the ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-middle``` project
```yaml
spring:
cloud:
tencent:
metadata:
# Defined your metadata keys & values
content:
# Example: intransitive
CUSTOM-METADATA-KEY-LOCAL-2: CUSTOM-VALUE-LOCAL-2
# Example: transitive
CUSTOM-METADATA-KEY-TRANSITIVE-2: CUSTOM-VALUE-TRANSITIVE-2
# Example: disposable
CUSTOM-METADATA-KEY-DISPOSABLE: CUSTOM-VALUE-DISPOSABLE-MIDDLE
# Assigned which metadata key-value will be passed along the link
transitive:
- CUSTOM-METADATA-KEY-TRANSITIVE-2
# Specify which metadata key value will be passed only once (one-step)
disposable:
- CUSTOM-METADATA-KEY-DISPOSABLE
``` ```
@ -93,24 +125,77 @@ Expected return rate
``` ```
{ {
"caller-metadata-contents": { "frontend-transitive-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE"
},
"frontend-upstream-disposable-metadata": {
},
"frontend-local-disposable-metadata": {
"CUSTOM-METADATA-KEY-DISPOSABLE": "CUSTOM-VALUE-DISPOSABLE-FRONTEND"
},
"middle-transitive-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE", "CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE",
"CUSTOM-METADATA-KEY-LOCAL": "CUSTOM-VALUE-LOCAL" "CUSTOM-METADATA-KEY-TRANSITIVE-2": "CUSTOM-VALUE-TRANSITIVE-2"
}, },
"callee-transitive-metadata": { "middle-upstream-disposable-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE" "CUSTOM-METADATA-KEY-DISPOSABLE": "CUSTOM-VALUE-DISPOSABLE-FRONTEND"
}, },
"caller-transitive-metadata": { "middle-local-disposable-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE" "CUSTOM-METADATA-KEY-DISPOSABLE": "CUSTOM-VALUE-DISPOSABLE-MIDDLE"
},
"backend-transitive-metadata": {
"CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE",
"CUSTOM-METADATA-KEY-TRANSITIVE-2": "CUSTOM-VALUE-TRANSITIVE-2"
},
"backend-upstream-disposable-metadata": {
"CUSTOM-METADATA-KEY-DISPOSABLE": "CUSTOM-VALUE-DISPOSABLE-MIDDLE"
},
"backend-local-disposable-metadata": {
} }
} }
``` ```
Response value description Response value description
- Key `caller-metadata-contents` represents all metadata configured by default in the `metadata-caller-service` project. > `*` (asterisk), representing `frontend`, `middle`, `backend` in the example.
- 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`. - Key `*-transitive-metadata` represents all the passable (fully linked) metadata configured by default in the service.
- Key `*-upstream-disposable-metadata` indicates the one-time transmissible metadata obtained from upstream requests in the service.
- Key `*-local-disposable-metadata` indicates the one-time metadata passed downstream as configured by the current service.
### How to get the passed metadata via Api
- Get the metadata passed globally
```java
MetadataContext context = MetadataContextHolder.get();
Map<String, String> customMetadataMap = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
customMetadataMap.forEach((key, value) -> {
// ...
});
```
- Get disposable(one-time) metadata passed from upstream
```java
Map<String, String> upstreamDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(true);
upstreamDisposableMetadatas.forEach((key, value) -> {
// ...
});
```
- Get disposable(one-time) metadata for local configuration
```java
Map<String, String> localDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(false);
localDisposableMetadatas.forEach((key, value) -> {
// ...
});
```
### Wiki Reference ### Wiki Reference

@ -10,8 +10,8 @@
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>metadata-caller-service</artifactId> <artifactId>metadata-backend</artifactId>
<name>Spring Cloud Tencent Metadata Transfer Caller Service</name> <name>Spring Cloud Tencent Metadata Transfer Backend Service</name>
<dependencies> <dependencies>
<dependency> <dependency>

@ -15,8 +15,9 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.metadata.service.callee; package com.tencent.cloud.metadata.service.backend;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
@ -35,10 +36,10 @@ import org.springframework.web.bind.annotation.RestController;
* @author Palmer Xu * @author Palmer Xu
*/ */
@RestController @RestController
@RequestMapping("/metadata/service/callee") @RequestMapping("/metadata/service/backend")
public class MetadataCalleeController { public class MetadataBackendController {
private static final Logger LOG = LoggerFactory.getLogger(MetadataCalleeController.class); private static final Logger LOG = LoggerFactory.getLogger(MetadataBackendController.class);
@Value("${server.port:0}") @Value("${server.port:0}")
private int port; private int port;
@ -48,18 +49,36 @@ public class MetadataCalleeController {
* @return information of callee * @return information of callee
*/ */
@GetMapping("/info") @GetMapping("/info")
public Map<String, String> info() { public Map<String, Map<String, String>> info() {
LOG.info("Metadata Service Callee [{}] is called.", port); LOG.info("Metadata Backend Service [{}] is called.", port);
Map<String, Map<String, String>> ret = new HashMap<>();
// Get Custom Metadata From Context // Get Custom Metadata From Context
MetadataContext context = MetadataContextHolder.get(); MetadataContext context = MetadataContextHolder.get();
Map<String, String> customMetadataMap = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Map<String, String> customMetadataMap = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
customMetadataMap.forEach((key, value) -> { customMetadataMap.forEach((key, value) -> {
LOG.info("Custom Metadata (Key-Value): {} : {}", key, value); LOG.info("Metadata Backend Custom Metadata (Key-Value): {} : {}", key, value);
}); });
return customMetadataMap; ret.put("backend-transitive-metadata", customMetadataMap);
}
// Get All Disposable metadata from upstream service
Map<String, String> upstreamDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(true);
upstreamDisposableMetadatas.forEach((key, value) -> {
LOG.info("Upstream Disposable Metadata (Key-Value): {} : {}", key, value);
});
ret.put("backend-upstream-disposable-metadata", upstreamDisposableMetadatas);
// Get All Disposable metadata from upstream service
Map<String, String> localDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(false);
localDisposableMetadatas.forEach((key, value) -> {
LOG.info("Local Custom Disposable Metadata (Key-Value): {} : {}", key, value);
});
ret.put("backend-local-disposable-metadata", localDisposableMetadatas);
return ret;
}
} }

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.metadata.service.callee; package com.tencent.cloud.metadata.service.backend;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -26,10 +26,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* @author Palmer Xu * @author Palmer Xu
*/ */
@SpringBootApplication @SpringBootApplication
public class MetadataCalleeService { public class MetadataBackendService {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(MetadataCalleeService.class, args); SpringApplication.run(MetadataBackendService.class, args);
} }
} }

@ -1,8 +1,8 @@
server: server:
port: 48084 port: 48088
spring: spring:
application: application:
name: MetadataCalleeService name: MetadataBackendService
cloud: cloud:
polaris: polaris:
address: grpc://183.47.111.80:8091 address: grpc://183.47.111.80:8091
@ -11,3 +11,10 @@ spring:
discovery: discovery:
enabled: true enabled: true
register: true register: true
management:
endpoints:
web:
exposure:
include:
- polaris-metadata

@ -1,109 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.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,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-frontend</artifactId>
<name>Spring Cloud Tencent Metadata Transfer Frontent 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,155 @@
/*
* 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.frontend;
import java.util.HashMap;
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.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/frontend")
public class MetadataFrontendController {
private static final Logger LOG = LoggerFactory.getLogger(MetadataFrontendController.class);
private final RestTemplate restTemplate;
private final MetadataMiddleService metadataMiddleService;
public MetadataFrontendController(RestTemplate restTemplate,
MetadataMiddleService metadataMiddleService) {
this.restTemplate = restTemplate;
this.metadataMiddleService = metadataMiddleService;
}
/**
* 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 = new HashMap<>();
// Call remote service with feign client
Map<String, Map<String, String>> middleResult = metadataMiddleService.info();
if (middleResult != null) {
ret.putAll(middleResult);
}
// Get Custom Metadata From Context
MetadataContext context = MetadataContextHolder.get();
Map<String, String> customMetadataMap = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
customMetadataMap.forEach((key, value) -> {
LOG.info("Metadata Middle Custom Metadata (Key-Value): {} : {}", key, value);
});
ret.put("frontend-transitive-metadata", customMetadataMap);
// Get All Disposable metadata from upstream service
Map<String, String> upstreamDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(true);
upstreamDisposableMetadatas.forEach((key, value) -> {
LOG.info("Upstream Custom Disposable Metadata (Key-Value): {} : {}", key, value);
});
ret.put("frontend-upstream-disposable-metadata", upstreamDisposableMetadatas);
// Get All Disposable metadata from upstream service
Map<String, String> localDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(false);
localDisposableMetadatas.forEach((key, value) -> {
LOG.info("Local Custom Disposable Metadata (Key-Value): {} : {}", key, value);
});
ret.put("frontend-local-disposable-metadata", localDisposableMetadatas);
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 = new HashMap<>();
// Call remote service with RestTemplate
Map<String, Map<String, String>> middleResult = restTemplate.getForObject(
"http://MetadataMiddleService/metadata/service/middle/info", Map.class);
if (middleResult != null) {
ret.putAll(middleResult);
}
// Get Custom Metadata From Context
MetadataContext context = MetadataContextHolder.get();
Map<String, String> customMetadataMap = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
customMetadataMap.forEach((key, value) -> {
LOG.info("Metadata Middle Custom Metadata (Key-Value): {} : {}", key, value);
});
ret.put("frontend-transitive-metadata", customMetadataMap);
// Get All Disposable metadata from upstream service
Map<String, String> upstreamDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(true);
upstreamDisposableMetadatas.forEach((key, value) -> {
LOG.info("Upstream Custom Disposable Metadata (Key-Value): {} : {}", key, value);
});
ret.put("frontend-upstream-disposable-metadata", upstreamDisposableMetadatas);
// Get All Disposable metadata from upstream service
Map<String, String> localDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(false);
localDisposableMetadatas.forEach((key, value) -> {
LOG.info("Local Custom Disposable Metadata (Key-Value): {} : {}", key, value);
});
ret.put("frontend-local-disposable-metadata", localDisposableMetadatas);
return ret;
}
/**
* health check.
*
* @return health check info
*/
@GetMapping("/healthCheck")
public String healthCheck() {
return "pk ok";
}
}

@ -15,11 +15,10 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.metadata.service.caller; package com.tencent.cloud.metadata.service.frontend;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -31,12 +30,11 @@ import org.springframework.web.client.RestTemplate;
* @author Palmer Xu * @author Palmer Xu
*/ */
@SpringBootApplication @SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients @EnableFeignClients
public class MetadataCallerService { public class MetadataFrontendService {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(MetadataCallerService.class, args); SpringApplication.run(MetadataFrontendService.class, args);
} }
@Bean @Bean
@ -44,5 +42,4 @@ public class MetadataCallerService {
public RestTemplate restTemplate() { public RestTemplate restTemplate() {
return new RestTemplate(); return new RestTemplate();
} }
} }

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.metadata.service.caller; package com.tencent.cloud.metadata.service.frontend;
import java.util.Map; import java.util.Map;
@ -27,15 +27,14 @@ import org.springframework.web.bind.annotation.GetMapping;
* *
* @author Palmer Xu * @author Palmer Xu
*/ */
@FeignClient(value = "MetadataCalleeService", @FeignClient(value = "MetadataMiddleService",
fallback = MetadataCalleeServiceFallback.class) fallback = MetadataMiddleServiceFallback.class)
public interface MetadataCalleeService { public interface MetadataMiddleService {
/** /**
* Get information of callee. * Get information of callee.
* @return information of callee * @return information of callee
*/ */
@GetMapping("/metadata/service/callee/info") @GetMapping("/metadata/service/middle/info")
Map<String, String> info(); Map<String, Map<String, String>> info();
} }

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.metadata.service.caller; package com.tencent.cloud.metadata.service.frontend;
import java.util.Map; import java.util.Map;
@ -29,11 +29,10 @@ import org.springframework.stereotype.Component;
* @author Palmer Xu * @author Palmer Xu
*/ */
@Component @Component
public class MetadataCalleeServiceFallback implements MetadataCalleeService { public class MetadataMiddleServiceFallback implements MetadataMiddleService {
@Override @Override
public Map<String, String> info() { public Map<String, Map<String, String>> info() {
return Maps.newHashMap(); return Maps.newHashMap();
} }
} }

@ -2,7 +2,7 @@ server:
port: 48080 port: 48080
spring: spring:
application: application:
name: MetadataCallerService name: MetadataFrontendService
cloud: cloud:
polaris: polaris:
address: grpc://183.47.111.80:8091 address: grpc://183.47.111.80:8091
@ -12,18 +12,22 @@ spring:
enabled: true enabled: true
register: true register: true
heartbeat-enabled: true heartbeat-enabled: true
health-check-url: /metadata/service/caller/healthCheck health-check-url: /metadata/service/frontend/healthCheck
tencent: tencent:
metadata: metadata:
# Defined your metadata keys & values # Defined your metadata keys & values
content: content:
# Example: intransitive # Example: intransitive
CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL
# Example: disposable
CUSTOM-METADATA-KEY-DISPOSABLE: CUSTOM-VALUE-DISPOSABLE
# Example: transitive # Example: transitive
CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE-FRONTEND
# Assigned which metadata key-value will be passed along the link # Assigned which metadata key-value will be passed along the link
transitive: transitive:
- CUSTOM-METADATA-KEY-TRANSITIVE - CUSTOM-METADATA-KEY-TRANSITIVE
disposable:
- CUSTOM-METADATA-KEY-DISPOSABLE
management: management:
endpoints: endpoints:
web: web:

@ -10,8 +10,8 @@
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>metadata-callee-service</artifactId> <artifactId>metadata-middle</artifactId>
<name>Spring Cloud Tencent Metadata Transfer Callee Service</name> <name>Spring Cloud Tencent Metadata Transfer Middle Service</name>
<dependencies> <dependencies>
<dependency> <dependency>

@ -0,0 +1,40 @@
/*
* 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.middle;
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 = "MetadataBackendService",
fallback = MetadataBackendServiceFallback.class)
public interface MetadataBackendService {
/**
* Get information of callee.
* @return information of callee
*/
@GetMapping("/metadata/service/backend/info")
Map<String, Map<String, String>> info();
}

@ -0,0 +1,38 @@
/*
* 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.middle;
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 MetadataBackendServiceFallback implements MetadataBackendService {
@Override
public Map<String, Map<String, String>> info() {
return Maps.newHashMap();
}
}

@ -0,0 +1,122 @@
/*
* 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.middle;
import java.util.HashMap;
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;
import org.springframework.web.client.RestTemplate;
import static com.tencent.cloud.common.util.JacksonUtils.serialize2Json;
/**
* Metadata callee controller.
*
* @author Palmer Xu
*/
@RestController
@RequestMapping("/metadata/service/middle")
public class MetadataMiddleController {
private static final Logger LOG = LoggerFactory.getLogger(MetadataMiddleController.class);
@Value("${server.port:0}")
private int port;
private final MetadataBackendService metadataBackendService;
private final RestTemplate restTemplate;
public MetadataMiddleController(MetadataBackendService metadataBackendService, RestTemplate restTemplate) {
this.metadataBackendService = metadataBackendService;
this.restTemplate = restTemplate;
}
/**
* Get information of callee.
*
* @return information of callee
*/
@GetMapping("/info")
public Map<String, Map<String, String>> info() {
// Build result
Map<String, Map<String, String>> ret = new HashMap<>();
LOG.info("Metadata Middle Service [{}] is called.", port);
// Call remote service with RestTemplate
Map<String, Map<String, String>> backendResult = restTemplate.getForObject(
"http://MetadataBackendService/metadata/service/backend/info", Map.class);
if (backendResult != null) {
LOG.info("RestTemplate Backend Metadata");
LOG.info("\r{}", serialize2Json(backendResult, true));
backendResult.clear();
}
// Call remote service with Feign
backendResult = metadataBackendService.info();
if (backendResult != null) {
LOG.info("Feign Backend Metadata");
LOG.info("\r{}", serialize2Json(backendResult, true));
}
if (backendResult != null) {
ret.putAll(backendResult);
}
// Get Custom Metadata From Context
MetadataContext context = MetadataContextHolder.get();
Map<String, String> customMetadataMap = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
customMetadataMap.forEach((key, value) -> {
LOG.info("Metadata Middle Custom Metadata (Key-Value): {} : {}", key, value);
});
ret.put("middle-transitive-metadata", customMetadataMap);
// Get All Disposable metadata from upstream service
Map<String, String> upstreamDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(true);
upstreamDisposableMetadatas.forEach((key, value) -> {
LOG.info("Upstream Custom Disposable Metadata (Key-Value): {} : {}", key, value);
});
ret.put("middle-upstream-disposable-metadata", upstreamDisposableMetadatas);
// Get All Disposable metadata from upstream service
Map<String, String> localDisposableMetadatas = MetadataContextHolder.getAllDisposableMetadata(false);
localDisposableMetadatas.forEach((key, value) -> {
LOG.info("Local Custom Disposable Metadata (Key-Value): {} : {}", key, value);
});
ret.put("middle-local-disposable-metadata", localDisposableMetadatas);
return ret;
}
}

@ -0,0 +1,46 @@
/*
* 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.middle;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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 callee application.
*
* @author Palmer Xu
*/
@SpringBootApplication
@EnableFeignClients
public class MetadataMiddleService {
public static void main(String[] args) {
SpringApplication.run(MetadataMiddleService.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

@ -0,0 +1,35 @@
server:
port: 48084
spring:
application:
name: MetadataMiddleService
cloud:
polaris:
address: grpc://183.47.111.80:8091
namespace: default
enabled: true
discovery:
enabled: true
register: true
tencent:
metadata:
# Defined your metadata keys & values
content:
# Example: intransitive
CUSTOM-METADATA-KEY-LOCAL-2: CUSTOM-VALUE-LOCAL-2
# Example: transitive
CUSTOM-METADATA-KEY-TRANSITIVE-2: CUSTOM-VALUE-TRANSITIVE-2
# Example: disposable
CUSTOM-METADATA-KEY-DISPOSABLE: CUSTOM-VALUE-DISPOSABLE-MIDDLE
# Assigned which metadata key-value will be passed along the link
transitive:
- CUSTOM-METADATA-KEY-TRANSITIVE-2
disposable:
- CUSTOM-METADATA-KEY-DISPOSABLE
management:
endpoints:
web:
exposure:
include:
- polaris-metadata

@ -15,8 +15,9 @@
<name>Spring Cloud Starter Tencent Metadata Transfer Example</name> <name>Spring Cloud Starter Tencent Metadata Transfer Example</name>
<modules> <modules>
<module>metadata-callee-service</module> <module>metadata-frontend</module>
<module>metadata-caller-service</module> <module>metadata-middle</module>
<module>metadata-backend</module>
</modules> </modules>
<dependencies> <dependencies>

Loading…
Cancel
Save