feature: support router expression label

pull/190/head
lepdou 3 years ago
parent cb103080fa
commit d72cd884ad

@ -3,4 +3,5 @@
- [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)
- [fix: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)

@ -33,6 +33,13 @@
<artifactId>spring-cloud-starter-openfeign</artifactId> <artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
</project> </project>

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

@ -19,6 +19,8 @@
package com.tencent.cloud.polaris.router.config; package com.tencent.cloud.polaris.router.config;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.polaris.context.ServiceRuleManager;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.feign.PolarisCachingSpringLoadBalanceFactory; import com.tencent.cloud.polaris.router.feign.PolarisCachingSpringLoadBalanceFactory;
import com.tencent.cloud.polaris.router.feign.RouterLabelInterceptor; import com.tencent.cloud.polaris.router.feign.RouterLabelInterceptor;
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerBeanPostProcessor; import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerBeanPostProcessor;
@ -44,8 +46,9 @@ public class RouterAutoConfiguration {
@Bean @Bean
public RouterLabelInterceptor routerLabelInterceptor(@Nullable RouterLabelResolver resolver, public RouterLabelInterceptor routerLabelInterceptor(@Nullable RouterLabelResolver resolver,
MetadataLocalProperties metadataLocalProperties) { MetadataLocalProperties metadataLocalProperties,
return new RouterLabelInterceptor(resolver, metadataLocalProperties); RouterRuleLabelResolver routerRuleLabelResolver) {
return new RouterLabelInterceptor(resolver, metadataLocalProperties, routerRuleLabelResolver);
} }
@Bean @Bean
@ -58,4 +61,9 @@ public class RouterAutoConfiguration {
public PolarisLoadBalancerBeanPostProcessor polarisLoadBalancerBeanPostProcessor() { public PolarisLoadBalancerBeanPostProcessor polarisLoadBalancerBeanPostProcessor() {
return new PolarisLoadBalancerBeanPostProcessor(); return new PolarisLoadBalancerBeanPostProcessor();
} }
@Bean
public RouterRuleLabelResolver routerRuleLabelResolver(ServiceRuleManager serviceRuleManager) {
return new RouterRuleLabelResolver(serviceRuleManager);
}
} }

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

@ -19,11 +19,13 @@
package com.tencent.cloud.polaris.router.feign; package com.tencent.cloud.polaris.router.feign;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.Map; 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.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;
import com.tencent.cloud.polaris.router.RouterConstants; import com.tencent.cloud.polaris.router.RouterConstants;
@ -59,7 +61,13 @@ public class PolarisFeignLoadBalancer extends FeignLoadBalancer {
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)) {
routerContext.setLabels(labels); Map<String, String> unescapeLabels = new HashMap<>(labels.size());
for (Map.Entry<String, String> entry : labels.entrySet()) {
String escapedKey = ExpressionLabelUtils.unescape(entry.getKey());
String escapedValue = ExpressionLabelUtils.unescape(entry.getValue());
unescapeLabels.put(escapedKey, escapedValue);
}
routerContext.setLabels(unescapeLabels);
} }
}); });

@ -18,14 +18,18 @@
package com.tencent.cloud.polaris.router.feign; package com.tencent.cloud.polaris.router.feign;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder; 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.common.util.ExpressionLabelUtils;
import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.router.RouterConstants; import com.tencent.cloud.polaris.router.RouterConstants;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
import feign.RequestInterceptor; import feign.RequestInterceptor;
import feign.RequestTemplate; import feign.RequestTemplate;
@ -45,11 +49,14 @@ public class RouterLabelInterceptor implements RequestInterceptor, Ordered {
private final RouterLabelResolver resolver; private final RouterLabelResolver resolver;
private final MetadataLocalProperties metadataLocalProperties; private final MetadataLocalProperties metadataLocalProperties;
private final RouterRuleLabelResolver routerRuleLabelResolver;
public RouterLabelInterceptor(RouterLabelResolver resolver, public RouterLabelInterceptor(RouterLabelResolver resolver,
MetadataLocalProperties metadataLocalProperties) { MetadataLocalProperties metadataLocalProperties,
RouterRuleLabelResolver routerRuleLabelResolver) {
this.resolver = resolver; this.resolver = resolver;
this.metadataLocalProperties = metadataLocalProperties; this.metadataLocalProperties = metadataLocalProperties;
this.routerRuleLabelResolver = routerRuleLabelResolver;
} }
@Override @Override
@ -79,10 +86,38 @@ public class RouterLabelInterceptor implements RequestInterceptor, Ordered {
} }
} }
// labels from rule expression
String peerServiceName = requestTemplate.feignTarget().name();
Map<String, String> ruleExpressionLabels = getRuleExpressionLabels(requestTemplate, peerServiceName);
labels.putAll(ruleExpressionLabels);
//local service labels //local service labels
labels.putAll(metadataLocalProperties.getContent()); labels.putAll(metadataLocalProperties.getContent());
// Because when the label is placed in RequestTemplate.header,
// RequestTemplate will parse the header according to the regular, which conflicts with the expression.
// Avoid conflicts by escaping.
Map<String, String> escapeLabels = new HashMap<>(labels.size());
for (Map.Entry<String, String> entry : labels.entrySet()) {
String escapedKey = ExpressionLabelUtils.escape(entry.getKey());
String escapedValue = ExpressionLabelUtils.escape(entry.getValue());
escapeLabels.put(escapedKey, escapedValue);
}
// pass label by header // pass label by header
requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER, JacksonUtils.serialize2Json(labels)); requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER, JacksonUtils.serialize2Json(escapeLabels));
}
private Map<String, String> getRuleExpressionLabels(RequestTemplate requestTemplate, String peerService) {
Set<String> labelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
MetadataContext.LOCAL_SERVICE, peerService);
if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap();
}
return FeignExpressionLabelUtils.resolve(requestTemplate, labelKeys);
} }
} }

@ -19,6 +19,7 @@
package com.tencent.cloud.polaris.router.resttemplate; package com.tencent.cloud.polaris.router.resttemplate;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
@ -51,9 +52,10 @@ public class PolarisLoadBalancerBeanPostProcessor implements BeanPostProcessor,
LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class); LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class);
RouterLabelResolver routerLabelResolver = this.factory.getBean(RouterLabelResolver.class); RouterLabelResolver routerLabelResolver = this.factory.getBean(RouterLabelResolver.class);
MetadataLocalProperties metadataLocalProperties = this.factory.getBean(MetadataLocalProperties.class); MetadataLocalProperties metadataLocalProperties = this.factory.getBean(MetadataLocalProperties.class);
RouterRuleLabelResolver routerRuleLabelResolver = this.factory.getBean(RouterRuleLabelResolver.class);
return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory, return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory,
routerLabelResolver, metadataLocalProperties); routerLabelResolver, metadataLocalProperties, routerRuleLabelResolver);
} }
return bean; return bean;
} }

@ -20,13 +20,17 @@ package com.tencent.cloud.polaris.router.resttemplate;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder; 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.common.util.ExpressionLabelUtils;
import com.tencent.cloud.polaris.router.PolarisRouterContext; import com.tencent.cloud.polaris.router.PolarisRouterContext;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -54,18 +58,21 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
private final LoadBalancerRequestFactory requestFactory; private final LoadBalancerRequestFactory requestFactory;
private final RouterLabelResolver resolver; private final RouterLabelResolver resolver;
private final MetadataLocalProperties metadataLocalProperties; private final MetadataLocalProperties metadataLocalProperties;
private final RouterRuleLabelResolver routerRuleLabelResolver;
private final boolean isRibbonLoadBalanceClient; private final boolean isRibbonLoadBalanceClient;
public PolarisLoadBalancerInterceptor(LoadBalancerClient loadBalancer, public PolarisLoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerRequestFactory requestFactory, LoadBalancerRequestFactory requestFactory,
RouterLabelResolver resolver, RouterLabelResolver resolver,
MetadataLocalProperties metadataLocalProperties) { MetadataLocalProperties metadataLocalProperties,
RouterRuleLabelResolver routerRuleLabelResolver) {
super(loadBalancer, requestFactory); super(loadBalancer, requestFactory);
this.loadBalancer = loadBalancer; this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory; this.requestFactory = requestFactory;
this.resolver = resolver; this.resolver = resolver;
this.metadataLocalProperties = metadataLocalProperties; this.metadataLocalProperties = metadataLocalProperties;
this.routerRuleLabelResolver = routerRuleLabelResolver;
this.isRibbonLoadBalanceClient = loadBalancer instanceof RibbonLoadBalancerClient; this.isRibbonLoadBalanceClient = loadBalancer instanceof RibbonLoadBalancerClient;
} }
@ -73,22 +80,22 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
@Override @Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI(); final URI originalUri = request.getURI();
String serviceName = originalUri.getHost(); String peerServiceName = originalUri.getHost();
Assert.state(serviceName != null, Assert.state(peerServiceName != null,
"Request URI does not contain a valid hostname: " + originalUri); "Request URI does not contain a valid hostname: " + originalUri);
if (isRibbonLoadBalanceClient) { if (isRibbonLoadBalanceClient) {
PolarisRouterContext routerContext = genRouterContext(request, body); PolarisRouterContext routerContext = genRouterContext(request, body, peerServiceName);
return ((RibbonLoadBalancerClient) loadBalancer).execute(serviceName, return ((RibbonLoadBalancerClient) loadBalancer).execute(peerServiceName,
this.requestFactory.createRequest(request, body, execution), routerContext); this.requestFactory.createRequest(request, body, execution), routerContext);
} }
return this.loadBalancer.execute(serviceName, return this.loadBalancer.execute(peerServiceName,
this.requestFactory.createRequest(request, body, execution)); this.requestFactory.createRequest(request, body, execution));
} }
private PolarisRouterContext genRouterContext(HttpRequest request, byte[] body) { private PolarisRouterContext genRouterContext(HttpRequest request, byte[] body, String peerServiceName) {
Map<String, String> labels = new HashMap<>(); Map<String, String> labels = new HashMap<>();
// labels from downstream // labels from downstream
@ -109,6 +116,11 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
} }
} }
Map<String, String> ruleExpressionLabels = getExpressionLabels(request, peerServiceName);
if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
labels.putAll(ruleExpressionLabels);
}
//local service labels //local service labels
labels.putAll(metadataLocalProperties.getContent()); labels.putAll(metadataLocalProperties.getContent());
@ -117,4 +129,15 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
return routerContext; return routerContext;
} }
private Map<String, String> getExpressionLabels(HttpRequest request, String peerServiceName) {
Set<String> labelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
MetadataContext.LOCAL_SERVICE, peerServiceName);
if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap();
}
return ExpressionLabelUtils.resolve(request, labelKeys);
}
} }

@ -83,6 +83,18 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>

@ -18,6 +18,7 @@
package com.tencent.cloud.common.util; package com.tencent.cloud.common.util;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -29,6 +30,8 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpCookie; import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -41,16 +44,50 @@ import org.springframework.web.server.ServerWebExchange;
*/ */
public class ExpressionLabelUtils { public class ExpressionLabelUtils {
private static final String LABEL_HEADER_PREFIX = "${http.header."; /**
private static final int LABEL_HEADER_PREFIX_LEN = LABEL_HEADER_PREFIX.length(); * the expression prefix of header label.
private static final String LABEL_QUERY_PREFIX = "${http.query."; */
private static final int LABEL_QUERY_PREFIX_LEN = LABEL_QUERY_PREFIX.length(); public static final String LABEL_HEADER_PREFIX = "${http.header.";
private static final String LABEL_COOKIE_PREFIX = "${http.cookie."; /**
private static final int LABEL_COOKIE_PREFIX_LEN = LABEL_COOKIE_PREFIX.length(); * the length of expression header label prefix.
private static final String LABEL_METHOD = "${http.method}"; */
private static final String LABEL_URI = "${http.uri}"; public static final int LABEL_HEADER_PREFIX_LEN = LABEL_HEADER_PREFIX.length();
/**
private static final String LABEL_SUFFIX = "}"; * the expression prefix of query.
*/
public static final String LABEL_QUERY_PREFIX = "${http.query.";
/**
* the length of expression query label prefix.
*/
public static final int LABEL_QUERY_PREFIX_LEN = LABEL_QUERY_PREFIX.length();
/**
* the expression prefix of cookie.
*/
public static final String LABEL_COOKIE_PREFIX = "${http.cookie.";
/**
* the length of expression cookie label prefix.
*/
public static final int LABEL_COOKIE_PREFIX_LEN = LABEL_COOKIE_PREFIX.length();
/**
* the expression of method.
*/
public static final String LABEL_METHOD = "${http.method}";
/**
* the expression of uri.
*/
public static final String LABEL_URI = "${http.uri}";
/**
* the prefix of expression.
*/
public static final String LABEL_PREFIX = "${";
/**
* the suffix of expression.
*/
public static final String LABEL_SUFFIX = "}";
/**
* the escape prefix of label.
*/
public static final String LABEL_ESCAPE_PREFIX = "$$$$";
public static boolean isExpressionLabel(String labelKey) { public static boolean isExpressionLabel(String labelKey) {
if (StringUtils.isEmpty(labelKey)) { if (StringUtils.isEmpty(labelKey)) {
@ -66,6 +103,14 @@ public class ExpressionLabelUtils {
&& StringUtils.endsWith(labelKey, LABEL_SUFFIX); && StringUtils.endsWith(labelKey, LABEL_SUFFIX);
} }
public static String escape(String str) {
return StringUtils.replace(str, LABEL_PREFIX, LABEL_ESCAPE_PREFIX);
}
public static String unescape(String str) {
return StringUtils.replace(str, LABEL_ESCAPE_PREFIX, LABEL_PREFIX);
}
public static Map<String, String> resolve(HttpServletRequest request, Set<String> labelKeys) { public static Map<String, String> resolve(HttpServletRequest request, Set<String> labelKeys) {
if (CollectionUtils.isEmpty(labelKeys)) { if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap(); return Collections.emptyMap();
@ -75,15 +120,24 @@ public class ExpressionLabelUtils {
for (String labelKey : labelKeys) { for (String labelKey : labelKeys) {
if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX)) { if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX)) {
String headerKey = labelKey.substring(LABEL_HEADER_PREFIX_LEN, labelKey.length() - 1); String headerKey = parseHeaderKey(labelKey);
if (StringUtils.isBlank(headerKey)) {
continue;
}
labels.put(labelKey, request.getHeader(headerKey)); labels.put(labelKey, request.getHeader(headerKey));
} }
else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX)) { else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX)) {
String queryKey = labelKey.substring(LABEL_QUERY_PREFIX_LEN, labelKey.length() - 1); String queryKey = parseQueryKey(labelKey);
if (StringUtils.isBlank(queryKey)) {
continue;
}
labels.put(labelKey, getQueryValue(request.getQueryString(), queryKey)); labels.put(labelKey, getQueryValue(request.getQueryString(), queryKey));
} }
else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_COOKIE_PREFIX)) { else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_COOKIE_PREFIX)) {
String cookieKey = labelKey.substring(LABEL_COOKIE_PREFIX_LEN, labelKey.length() - 1); String cookieKey = parseCookieKey(labelKey);
if (StringUtils.isBlank(cookieKey)) {
continue;
}
labels.put(labelKey, getCookieValue(request.getCookies(), cookieKey)); labels.put(labelKey, getCookieValue(request.getCookies(), cookieKey));
} }
else if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey)) { else if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey)) {
@ -106,15 +160,24 @@ public class ExpressionLabelUtils {
for (String labelKey : labelKeys) { for (String labelKey : labelKeys) {
if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX)) { if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX)) {
String headerKey = labelKey.substring(LABEL_HEADER_PREFIX_LEN, labelKey.length() - 1); String headerKey = parseHeaderKey(labelKey);
if (StringUtils.isBlank(headerKey)) {
continue;
}
labels.put(labelKey, getHeaderValue(exchange.getRequest(), headerKey)); labels.put(labelKey, getHeaderValue(exchange.getRequest(), headerKey));
} }
else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX)) { else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX)) {
String queryKey = labelKey.substring(LABEL_QUERY_PREFIX_LEN, labelKey.length() - 1); String queryKey = parseQueryKey(labelKey);
if (StringUtils.isBlank(queryKey)) {
continue;
}
labels.put(labelKey, getQueryValue(exchange.getRequest(), queryKey)); labels.put(labelKey, getQueryValue(exchange.getRequest(), queryKey));
} }
else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_COOKIE_PREFIX)) { else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_COOKIE_PREFIX)) {
String cookieKey = labelKey.substring(LABEL_COOKIE_PREFIX_LEN, labelKey.length() - 1); String cookieKey = parseCookieKey(labelKey);
if (StringUtils.isBlank(cookieKey)) {
continue;
}
labels.put(labelKey, getCookieValue(exchange.getRequest(), cookieKey)); labels.put(labelKey, getCookieValue(exchange.getRequest(), cookieKey));
} }
else if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey)) { else if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey)) {
@ -128,7 +191,52 @@ public class ExpressionLabelUtils {
return labels; return labels;
} }
private static String getQueryValue(String queryString, String queryKey) { public static Map<String, String> resolve(HttpRequest request, Set<String> labelKeys) {
if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap();
}
Map<String, String> labels = new HashMap<>();
for (String labelKey : labelKeys) {
if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX)) {
String headerKey = parseHeaderKey(labelKey);
if (StringUtils.isBlank(headerKey)) {
continue;
}
labels.put(labelKey, getHeaderValue(request, headerKey));
}
else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX)) {
String queryKey = parseQueryKey(labelKey);
if (StringUtils.isBlank(queryKey)) {
continue;
}
labels.put(labelKey, getQueryValue(request, queryKey));
}
else if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey)) {
labels.put(labelKey, request.getMethodValue());
}
else if (StringUtils.equalsIgnoreCase(LABEL_URI, labelKey)) {
labels.put(labelKey, request.getURI().getPath());
}
}
return labels;
}
public static String parseHeaderKey(String expression) {
return expression.substring(LABEL_HEADER_PREFIX_LEN, expression.length() - 1);
}
public static String parseQueryKey(String expression) {
return expression.substring(LABEL_QUERY_PREFIX_LEN, expression.length() - 1);
}
public static String parseCookieKey(String expression) {
return expression.substring(LABEL_COOKIE_PREFIX_LEN, expression.length() - 1);
}
public static String getQueryValue(String queryString, String queryKey) {
if (StringUtils.isBlank(queryString)) { if (StringUtils.isBlank(queryString)) {
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
@ -145,7 +253,7 @@ public class ExpressionLabelUtils {
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
private static String getCookieValue(Cookie[] cookies, String key) { public static String getCookieValue(Cookie[] cookies, String key) {
if (cookies == null || cookies.length == 0) { if (cookies == null || cookies.length == 0) {
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
@ -157,7 +265,7 @@ public class ExpressionLabelUtils {
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
private static String getHeaderValue(ServerHttpRequest request, String key) { public static String getHeaderValue(ServerHttpRequest request, String key) {
String value = request.getHeaders().getFirst(key); String value = request.getHeaders().getFirst(key);
if (value == null) { if (value == null) {
return StringUtils.EMPTY; return StringUtils.EMPTY;
@ -165,7 +273,7 @@ public class ExpressionLabelUtils {
return value; return value;
} }
private static String getQueryValue(ServerHttpRequest request, String key) { public static String getQueryValue(ServerHttpRequest request, String key) {
MultiValueMap<String, String> queries = request.getQueryParams(); MultiValueMap<String, String> queries = request.getQueryParams();
if (CollectionUtils.isEmpty(queries)) { if (CollectionUtils.isEmpty(queries)) {
return StringUtils.EMPTY; return StringUtils.EMPTY;
@ -177,11 +285,39 @@ public class ExpressionLabelUtils {
return value; return value;
} }
private static String getCookieValue(ServerHttpRequest request, String key) { public static String getCookieValue(ServerHttpRequest request, String key) {
HttpCookie cookie = request.getCookies().getFirst(key); HttpCookie cookie = request.getCookies().getFirst(key);
if (cookie == null) { if (cookie == null) {
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
return cookie.getValue(); return cookie.getValue();
} }
public static String getHeaderValue(HttpRequest request, String key) {
HttpHeaders headers = request.getHeaders();
return headers.getFirst(key);
}
public static String getQueryValue(HttpRequest request, String key) {
String query = request.getURI().getQuery();
return getQueryValue(query, key);
}
public static String getFirstValue(Map<String, Collection<String>> valueMaps, String key) {
if (CollectionUtils.isEmpty(valueMaps)) {
return StringUtils.EMPTY;
}
Collection<String> values = valueMaps.get(key);
if (CollectionUtils.isEmpty(values)) {
return StringUtils.EMPTY;
}
for (String value : values) {
return value;
}
return StringUtils.EMPTY;
}
} }

@ -75,7 +75,7 @@ public class ServiceRuleManager {
return null; return null;
} }
public List<RoutingProto.Routing> getServiceRouterRule(String namespace, String sourceService, String dstService) { public List<RoutingProto.Route> getServiceRouterRule(String namespace, String sourceService, String dstService) {
Set<ServiceEventKey> routerKeys = new HashSet<>(); Set<ServiceEventKey> routerKeys = new HashSet<>();
ServiceEventKey dstSvcEventKey = new ServiceEventKey(new ServiceKey(namespace, dstService), ServiceEventKey dstSvcEventKey = new ServiceEventKey(new ServiceKey(namespace, dstService),
@ -92,20 +92,23 @@ public class ServiceRuleManager {
ResourcesResponse resourcesResponse = BaseFlow ResourcesResponse resourcesResponse = BaseFlow
.syncGetResources(sdkContext.getExtensions(), true, svcKeysProvider, controlParam); .syncGetResources(sdkContext.getExtensions(), true, svcKeysProvider, controlParam);
List<RoutingProto.Routing> rules = new ArrayList<>(); List<RoutingProto.Route> rules = new ArrayList<>();
//get source service outbound rules.
ServiceRule sourceServiceRule = resourcesResponse.getServiceRule(srcSvcEventKey); ServiceRule sourceServiceRule = resourcesResponse.getServiceRule(srcSvcEventKey);
if (sourceServiceRule != null) { if (sourceServiceRule != null) {
Object rule = sourceServiceRule.getRule(); Object rule = sourceServiceRule.getRule();
if (rule instanceof RoutingProto.Routing) { if (rule instanceof RoutingProto.Routing) {
rules.add((RoutingProto.Routing) rule); rules.addAll(((RoutingProto.Routing) rule).getOutboundsList());
} }
} }
//get peer service inbound rules.
ServiceRule dstServiceRule = resourcesResponse.getServiceRule(dstSvcEventKey); ServiceRule dstServiceRule = resourcesResponse.getServiceRule(dstSvcEventKey);
if (dstServiceRule != null) { if (dstServiceRule != null) {
Object rule = dstServiceRule.getRule(); Object rule = dstServiceRule.getRule();
if (rule instanceof RoutingProto.Routing) { if (rule instanceof RoutingProto.Routing) {
rules.add((RoutingProto.Routing) rule); rules.addAll(((RoutingProto.Routing) rule).getInboundsList());
} }
} }

Loading…
Cancel
Save