org.springframework.cloud
spring-cloud-netflix-zuul
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java
index eeb84afd3..7581ca469 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java
@@ -32,8 +32,10 @@ import com.netflix.loadbalancer.RoundRobinRule;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.WeightedResponseTimeRule;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
+import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.pojo.PolarisServer;
+import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.loadbalancer.LoadBalancerUtils;
import com.tencent.cloud.polaris.loadbalancer.PolarisWeightedRule;
import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties;
@@ -45,19 +47,20 @@ import com.tencent.polaris.api.pojo.ServiceInstances;
import com.tencent.polaris.router.api.core.RouterAPI;
import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse;
+import org.yaml.snakeyaml.util.UriEncoder;
+import org.springframework.http.HttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
- *
* Service routing entrance.
- *
+ *
* Rule routing needs to rely on request parameters for server filtering,
* and {@link com.netflix.loadbalancer.ServerListFilter#getFilteredListOfServers(List)}
* The interface cannot obtain the context object of the request granularity,
* so the routing capability cannot be achieved through ServerListFilter.
- *
+ *
* And {@link com.netflix.loadbalancer.IRule#choose(Object)} provides the ability to pass in context parameters,
* so routing capabilities are implemented through IRule.
*
@@ -117,6 +120,7 @@ public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule {
ILoadBalancer loadBalancer = new SimpleLoadBalancer();
// 2. filter by router
if (key instanceof PolarisRouterContext) {
+ // router implement for Feign and scg
PolarisRouterContext routerContext = (PolarisRouterContext) key;
List serversAfterRouter = doRouter(allServers, routerContext);
// 3. filter by load balance.
@@ -124,9 +128,26 @@ public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule {
// because the list of servers may be different after filtered by router
loadBalancer.addServers(serversAfterRouter);
}
+ else if (key instanceof HttpRequest) {
+ // router implement for rest template
+ HttpRequest request = (HttpRequest) key;
+
+ String routerContextStr = request.getHeaders().getFirst(RouterConstant.HEADER_ROUTER_CONTEXT);
+
+ if (StringUtils.isEmpty(routerContextStr)) {
+ loadBalancer.addServers(allServers);
+ }
+ else {
+ PolarisRouterContext routerContext = JacksonUtils.deserialize(UriEncoder.decode(routerContextStr),
+ PolarisRouterContext.class);
+ List serversAfterRouter = doRouter(allServers, routerContext);
+ loadBalancer.addServers(serversAfterRouter);
+ }
+ }
else {
loadBalancer.addServers(allServers);
}
+
delegateRule.setLoadBalancer(loadBalancer);
return delegateRule.choose(key);
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java
index 38cda88ad..6d380f5bb 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java
@@ -102,4 +102,12 @@ public class PolarisRouterContext {
}
labels.put(labelType, subLabels);
}
+
+ public Map> getLabels() {
+ return labels;
+ }
+
+ public void setLabels(Map> labels) {
+ this.labels = labels;
+ }
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java
index a65f6f06f..9912d1731 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java
@@ -18,13 +18,8 @@
package com.tencent.cloud.polaris.router.beanprocessor;
-import java.util.List;
-
-import com.tencent.cloud.common.metadata.StaticMetadataManager;
-import com.tencent.cloud.common.util.BeanFactoryUtils;
-import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerInterceptor;
-import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
+import com.tencent.cloud.polaris.router.resttemplate.RouterContextFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
@@ -36,7 +31,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
import org.springframework.lang.NonNull;
/**
- * Replace LoadBalancerInterceptor with PolarisLoadBalancerInterceptor.
+ * Replace {@link LoadBalancerInterceptor} with {@link PolarisLoadBalancerInterceptor}.
* PolarisLoadBalancerInterceptor can pass routing context information.
*
* @author lepdou 2022-05-18
@@ -57,12 +52,9 @@ public class LoadBalancerInterceptorBeanPostProcessor implements BeanPostProcess
// Replaces the default LoadBalancerInterceptor implementation and returns a custom PolarisLoadBalancerInterceptor
LoadBalancerRequestFactory requestFactory = this.factory.getBean(LoadBalancerRequestFactory.class);
LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class);
- List routerLabelResolvers = BeanFactoryUtils.getBeans(factory, SpringWebRouterLabelResolver.class);
- StaticMetadataManager staticMetadataManager = this.factory.getBean(StaticMetadataManager.class);
- RouterRuleLabelResolver routerRuleLabelResolver = this.factory.getBean(RouterRuleLabelResolver.class);
+ RouterContextFactory routerContextFactory = this.factory.getBean(RouterContextFactory.class);
- return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory,
- routerLabelResolvers, staticMetadataManager, routerRuleLabelResolver);
+ return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory, routerContextFactory);
}
return bean;
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/RetryLoadBalancerInterceptorBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/RetryLoadBalancerInterceptorBeanPostProcessor.java
new file mode 100644
index 000000000..6325b874a
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/RetryLoadBalancerInterceptorBeanPostProcessor.java
@@ -0,0 +1,68 @@
+/*
+ * 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.beanprocessor;
+
+import com.tencent.cloud.polaris.router.resttemplate.PolarisRetryLoadBalancerInterceptor;
+import com.tencent.cloud.polaris.router.resttemplate.RouterContextFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerRetryProperties;
+import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
+import org.springframework.lang.NonNull;
+
+/**
+ * Replace {@link RetryLoadBalancerInterceptor}RetryLoadBalancerInterceptor with {@link PolarisRetryLoadBalancerInterceptor}.
+ * PolarisRetryLoadBalancerInterceptor can pass routing context information.
+ *
+ * @author lepdou 2022-10-09
+ */
+public class RetryLoadBalancerInterceptorBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
+
+ private BeanFactory factory;
+
+ @Override
+ public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
+ this.factory = beanFactory;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
+ if (bean instanceof RetryLoadBalancerInterceptor) {
+ // Support rest template router.
+ // Replaces the default RetryLoadBalancerInterceptor implementation
+ // and returns a custom PolarisRetryLoadBalancerInterceptor
+
+ LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class);
+ LoadBalancerRetryProperties lbProperties = this.factory.getBean(LoadBalancerRetryProperties.class);
+ LoadBalancerRequestFactory requestFactory = this.factory.getBean(LoadBalancerRequestFactory.class);
+ LoadBalancedRetryFactory lbRetryFactory = this.factory.getBean(LoadBalancedRetryFactory.class);
+ RouterContextFactory routerContextFactory = this.factory.getBean(RouterContextFactory.class);
+
+ return new PolarisRetryLoadBalancerInterceptor(loadBalancerClient, lbProperties, requestFactory, lbRetryFactory,
+ routerContextFactory);
+ }
+ return bean;
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java
index 551c3b956..ed9766109 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java
@@ -25,13 +25,16 @@ import com.tencent.cloud.polaris.context.ServiceRuleManager;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerClientFilterBeanPostProcessor;
import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerInterceptorBeanPostProcessor;
+import com.tencent.cloud.polaris.router.beanprocessor.RetryLoadBalancerInterceptorBeanPostProcessor;
import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties;
import com.tencent.cloud.polaris.router.interceptor.MetadataRouterRequestInterceptor;
import com.tencent.cloud.polaris.router.interceptor.NearbyRouterRequestInterceptor;
import com.tencent.cloud.polaris.router.interceptor.RuleBasedRouterRequestInterceptor;
+import com.tencent.cloud.polaris.router.resttemplate.RouterContextFactory;
import com.tencent.cloud.polaris.router.spi.ServletRouterLabelResolver;
+import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import com.tencent.cloud.polaris.router.zuul.PolarisRibbonRoutingFilter;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -59,13 +62,6 @@ import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
@Import({PolarisNearByRouterProperties.class, PolarisMetadataRouterProperties.class, PolarisRuleBasedRouterProperties.class})
public class RouterAutoConfiguration {
- @Bean
- @Order(HIGHEST_PRECEDENCE)
- @ConditionalOnClass(name = "org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor")
- public LoadBalancerInterceptorBeanPostProcessor loadBalancerInterceptorBeanPostProcessor() {
- return new LoadBalancerInterceptorBeanPostProcessor();
- }
-
@Bean
@Order(HIGHEST_PRECEDENCE)
@ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.LoadBalancerClientFilter")
@@ -96,6 +92,32 @@ public class RouterAutoConfiguration {
return new RuleBasedRouterRequestInterceptor(polarisRuleBasedRouterProperties);
}
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
+ public static class RestTemplateAutoConfiguration {
+ @Bean
+ @Order(HIGHEST_PRECEDENCE)
+ @ConditionalOnClass(name = "org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor")
+ public LoadBalancerInterceptorBeanPostProcessor loadBalancerInterceptorBeanPostProcessor() {
+ return new LoadBalancerInterceptorBeanPostProcessor();
+ }
+
+ @Bean
+ @Order(HIGHEST_PRECEDENCE)
+ @ConditionalOnClass(name = "org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor")
+ public RetryLoadBalancerInterceptorBeanPostProcessor retryLoadBalancerInterceptorBeanPostProcessor() {
+ return new RetryLoadBalancerInterceptorBeanPostProcessor();
+ }
+
+ @Bean
+ @Order(HIGHEST_PRECEDENCE)
+ public RouterContextFactory routerContextFactory(List routerLabelResolvers,
+ StaticMetadataManager staticMetadataManager,
+ RouterRuleLabelResolver routerRuleLabelResolver) {
+ return new RouterContextFactory(routerLabelResolvers, staticMetadataManager, routerRuleLabelResolver);
+ }
+ }
+
/**
* AutoConfiguration for router module integrate for zuul.
*/
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisInterceptorRetryPolicy.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisInterceptorRetryPolicy.java
new file mode 100644
index 000000000..7da66766a
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisInterceptorRetryPolicy.java
@@ -0,0 +1,85 @@
+/*
+ * 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.resttemplate;
+
+import com.tencent.cloud.polaris.router.PolarisRouterContext;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
+import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
+import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
+import org.springframework.http.HttpRequest;
+import org.springframework.retry.RetryContext;
+
+/**
+ * Override InterceptorRetryPolicy for passing router context.
+ *
+ * @author lepdou 2022-10-09
+ */
+public class PolarisInterceptorRetryPolicy extends InterceptorRetryPolicy {
+
+ private final HttpRequest request;
+
+ private final LoadBalancedRetryPolicy policy;
+
+ private final ServiceInstanceChooser serviceInstanceChooser;
+
+ private final String serviceName;
+
+ private final PolarisRouterContext routerContext;
+
+
+ public PolarisInterceptorRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy,
+ ServiceInstanceChooser serviceInstanceChooser, String serviceName,
+ PolarisRouterContext routerContext) {
+ super(request, policy, serviceInstanceChooser, serviceName);
+
+ this.request = request;
+ this.policy = policy;
+ this.serviceInstanceChooser = serviceInstanceChooser;
+ this.serviceName = serviceName;
+ this.routerContext = routerContext;
+ }
+
+ @Override
+ public boolean canRetry(RetryContext context) {
+ if (serviceInstanceChooser instanceof RibbonLoadBalancerClient) {
+ LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
+ if (lbContext.getRetryCount() == 0 && lbContext.getServiceInstance() == null) {
+ loadbalancerWithRouterContext(lbContext, routerContext);
+ return true;
+ }
+ return policy.canRetryNextServer(lbContext);
+ }
+ else {
+ return super.canRetry(context);
+ }
+ }
+
+ private void loadbalancerWithRouterContext(LoadBalancedRetryContext lbContext, PolarisRouterContext routerContext) {
+ RibbonLoadBalancerClient ribbonLoadBalancerClient = (RibbonLoadBalancerClient) serviceInstanceChooser;
+
+ // set router context to request
+ RouterContextHelper.setRouterContextToRequest(request, routerContext);
+
+ ServiceInstance serviceInstance = ribbonLoadBalancerClient.choose(serviceName, request);
+ lbContext.setServiceInstance(serviceInstance);
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java
index cce678a79..c62569bfa 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java
@@ -19,40 +19,18 @@
package com.tencent.cloud.polaris.router.resttemplate;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
import java.net.URI;
-import java.net.URLEncoder;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import com.tencent.cloud.common.constant.RouterConstant;
-import com.tencent.cloud.common.metadata.MetadataContext;
-import com.tencent.cloud.common.metadata.MetadataContextHolder;
-import com.tencent.cloud.common.metadata.StaticMetadataManager;
-import com.tencent.cloud.common.util.JacksonUtils;
-import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils;
import com.tencent.cloud.polaris.router.PolarisRouterContext;
-import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
-import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
-import org.springframework.core.Ordered;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
-import org.springframework.util.CollectionUtils;
-
-import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
/**
* PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor capabilities.
@@ -61,34 +39,19 @@ import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
* @author lepdou 2022-05-18
*/
public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
- private static final Logger LOGGER = LoggerFactory.getLogger(PolarisLoadBalancerInterceptor.class);
private final LoadBalancerClient loadBalancer;
private final LoadBalancerRequestFactory requestFactory;
- private final List routerLabelResolvers;
- private final StaticMetadataManager staticMetadataManager;
- private final RouterRuleLabelResolver routerRuleLabelResolver;
-
+ private final RouterContextFactory routerContextFactory;
private final boolean isRibbonLoadBalanceClient;
public PolarisLoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerRequestFactory requestFactory,
- List routerLabelResolvers,
- StaticMetadataManager staticMetadataManager,
- RouterRuleLabelResolver routerRuleLabelResolver) {
+ RouterContextFactory routerContextFactory) {
super(loadBalancer, requestFactory);
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
- this.staticMetadataManager = staticMetadataManager;
- this.routerRuleLabelResolver = routerRuleLabelResolver;
-
- if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
- routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder));
- this.routerLabelResolvers = routerLabelResolvers;
- }
- else {
- this.routerLabelResolvers = null;
- }
+ this.routerContextFactory = routerContextFactory;
this.isRibbonLoadBalanceClient = loadBalancer instanceof RibbonLoadBalancerClient;
}
@@ -101,72 +64,21 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
"Request URI does not contain a valid hostname: " + originalUri);
if (isRibbonLoadBalanceClient) {
- PolarisRouterContext routerContext = genRouterContext(request, body, peerServiceName);
+ //1. create router context
+ PolarisRouterContext routerContext = routerContextFactory.create(request, body, peerServiceName);
+
+ //2. set router context to request
+ RouterContextHelper.setRouterContextToRequest(request, routerContext);
+
+ //3. do loadbalancer and execute request
ClientHttpResponse response = ((RibbonLoadBalancerClient) loadBalancer).execute(peerServiceName,
this.requestFactory.createRequest(request, body, execution), routerContext);
- Map labels = routerContext.getLabels(RouterConstant.ROUTER_LABELS);
- // put labels in header
- String encodedLabelsContent;
- try {
- encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(labels), UTF_8);
- }
- catch (UnsupportedEncodingException e) {
- throw new RuntimeException("unsupported charset exception " + UTF_8);
- }
- response.getHeaders().add(RouterConstant.ROUTER_LABELS, encodedLabelsContent);
+ //4. set router context to response
+ RouterContextHelper.setRouterContextToResponse(routerContext, response);
return response;
}
- return this.loadBalancer.execute(peerServiceName,
- this.requestFactory.createRequest(request, body, execution));
- }
-
- PolarisRouterContext genRouterContext(HttpRequest request, byte[] body, String peerServiceName) {
- // local service labels
- Map labels = new HashMap<>(staticMetadataManager.getMergedStaticMetadata());
-
- // labels from rule expression
- Set expressionLabelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
- MetadataContext.LOCAL_SERVICE, peerServiceName);
- Map ruleExpressionLabels = getExpressionLabels(request, expressionLabelKeys);
- if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
- labels.putAll(ruleExpressionLabels);
- }
-
- // labels from request
- if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
- routerLabelResolvers.forEach(resolver -> {
- try {
- Map customResolvedLabels = resolver.resolve(request, body, expressionLabelKeys);
- if (!CollectionUtils.isEmpty(customResolvedLabels)) {
- labels.putAll(customResolvedLabels);
- }
- }
- catch (Throwable t) {
- LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t);
- }
- });
- }
-
- // labels from downstream
- Map transitiveLabels = MetadataContextHolder.get()
- .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
- labels.putAll(transitiveLabels);
-
- PolarisRouterContext routerContext = new PolarisRouterContext();
-
- routerContext.putLabels(RouterConstant.ROUTER_LABELS, labels);
- routerContext.putLabels(RouterConstant.TRANSITIVE_LABELS, transitiveLabels);
-
- return routerContext;
- }
-
- private Map getExpressionLabels(HttpRequest request, Set labelKeys) {
- if (CollectionUtils.isEmpty(labelKeys)) {
- return Collections.emptyMap();
- }
-
- return SpringWebExpressionLabelUtils.resolve(request, labelKeys);
+ return this.loadBalancer.execute(peerServiceName, this.requestFactory.createRequest(request, body, execution));
}
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisRetryLoadBalancerInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisRetryLoadBalancerInterceptor.java
new file mode 100644
index 000000000..6b19966f3
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisRetryLoadBalancerInterceptor.java
@@ -0,0 +1,158 @@
+/*
+ * 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.resttemplate;
+
+import java.io.IOException;
+import java.net.URI;
+
+import com.tencent.cloud.polaris.router.PolarisRouterContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.ClientHttpResponseStatusCodeException;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRecoveryCallback;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerRetryProperties;
+import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.retry.RetryListener;
+import org.springframework.retry.backoff.BackOffPolicy;
+import org.springframework.retry.backoff.NoBackOffPolicy;
+import org.springframework.retry.policy.NeverRetryPolicy;
+import org.springframework.retry.support.RetryTemplate;
+import org.springframework.util.Assert;
+import org.springframework.util.StreamUtils;
+
+/**
+ * Override {@link RetryLoadBalancerInterceptor} for passing router context.
+ *
+ * @author lepdou 2022-10-09
+ */
+public class PolarisRetryLoadBalancerInterceptor extends RetryLoadBalancerInterceptor {
+
+ private static final Log LOG = LogFactory.getLog(PolarisRetryLoadBalancerInterceptor.class);
+
+ private final LoadBalancerClient loadBalancer;
+
+ private final LoadBalancerRetryProperties lbProperties;
+
+ private final LoadBalancerRequestFactory requestFactory;
+
+ private final LoadBalancedRetryFactory lbRetryFactory;
+
+ private final RouterContextFactory routerContextFactory;
+
+ public PolarisRetryLoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRetryProperties lbProperties,
+ LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory lbRetryFactory, RouterContextFactory routerContextFactory) {
+ super(loadBalancer, lbProperties, requestFactory, lbRetryFactory);
+ this.loadBalancer = loadBalancer;
+ this.lbProperties = lbProperties;
+ this.requestFactory = requestFactory;
+ this.lbRetryFactory = lbRetryFactory;
+ this.routerContextFactory = routerContextFactory;
+ }
+
+ @Override
+ public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
+ final ClientHttpRequestExecution execution) throws IOException {
+
+ final URI originalUri = request.getURI();
+ final String serviceName = originalUri.getHost();
+ Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
+
+ final LoadBalancedRetryPolicy retryPolicy = lbRetryFactory.createRetryPolicy(serviceName, loadBalancer);
+
+ //1. create router context
+ PolarisRouterContext routerContext = routerContextFactory.create(request, body, serviceName);
+
+ RetryTemplate template = createRetryTemplate(serviceName, request, routerContext, retryPolicy);
+
+ //2. do loadbalancer is template.execute
+ return template.execute(context -> {
+ ServiceInstance serviceInstance = null; if (context instanceof LoadBalancedRetryContext) {
+ LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
+ serviceInstance = lbContext.getServiceInstance();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieved service instance from LoadBalancedRetryContext: %s", serviceInstance));
+ }
+ }
+
+ //retry loadbalancer if LoadBalancedRetryContext does not has instance
+ if (serviceInstance == null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. " + "Reattempting service instance selection");
+ }
+ serviceInstance = loadBalancer.choose(serviceName); if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Selected service instance: %s", serviceInstance));
+ }
+ }
+
+ //3. execute request
+ ClientHttpResponse response = loadBalancer.execute(serviceName, serviceInstance,
+ requestFactory.createRequest(request, body, execution));
+
+ //4. set router context to response
+ RouterContextHelper.setRouterContextToResponse(routerContext, response);
+
+ //5. handle response whether retry execute request
+ int statusCode = response.getRawStatusCode();
+ if (retryPolicy != null && retryPolicy.retryableStatusCode(statusCode)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrying on status code: %d", statusCode));
+ }
+ byte[] bodyCopy = StreamUtils.copyToByteArray(response.getBody());
+ response.close();
+ throw new ClientHttpResponseStatusCodeException(serviceName, response, bodyCopy);
+ }
+ return response;
+ }, new LoadBalancedRecoveryCallback() {
+ // This is a special case, where both parameters to
+ // LoadBalancedRecoveryCallback are
+ // the same. In most cases they would be different.
+ @Override
+ protected ClientHttpResponse createResponse(ClientHttpResponse response, URI uri) {
+ return response;
+ }
+ });
+ }
+
+ private RetryTemplate createRetryTemplate(String serviceName, HttpRequest request, PolarisRouterContext routerContext,
+ LoadBalancedRetryPolicy retryPolicy) {
+ RetryTemplate template = new RetryTemplate();
+ BackOffPolicy backOffPolicy = lbRetryFactory.createBackOffPolicy(serviceName);
+ template.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
+ template.setThrowLastExceptionOnExhausted(true);
+ RetryListener[] retryListeners = lbRetryFactory.createRetryListeners(serviceName);
+ if (retryListeners != null && retryListeners.length != 0) {
+ template.setListeners(retryListeners);
+ }
+ //Override: use PolarisInterceptorRetryPolicy for passing router context
+ template.setRetryPolicy(!lbProperties.isEnabled() || retryPolicy == null ? new NeverRetryPolicy() :
+ new PolarisInterceptorRetryPolicy(request, retryPolicy, loadBalancer, serviceName, routerContext));
+ return template;
+ }
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterContextFactory.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterContextFactory.java
new file mode 100644
index 000000000..76aa4c407
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterContextFactory.java
@@ -0,0 +1,117 @@
+/*
+ * 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.resttemplate;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.tencent.cloud.common.constant.RouterConstant;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.metadata.StaticMetadataManager;
+import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils;
+import com.tencent.cloud.polaris.router.PolarisRouterContext;
+import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
+import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpRequest;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * Polaris router context factory.
+ *
+ * @author lepdou 2022-10-09
+ */
+public class RouterContextFactory {
+ private static final Logger LOGGER = LoggerFactory.getLogger(RouterContextFactory.class);
+
+ private final List routerLabelResolvers;
+ private final StaticMetadataManager staticMetadataManager;
+ private final RouterRuleLabelResolver routerRuleLabelResolver;
+
+ public RouterContextFactory(List routerLabelResolvers,
+ StaticMetadataManager staticMetadataManager,
+ RouterRuleLabelResolver routerRuleLabelResolver) {
+ this.staticMetadataManager = staticMetadataManager;
+ this.routerRuleLabelResolver = routerRuleLabelResolver;
+
+ if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
+ routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder));
+ this.routerLabelResolvers = routerLabelResolvers;
+ }
+ else {
+ this.routerLabelResolvers = null;
+ }
+ }
+
+
+ public PolarisRouterContext create(HttpRequest request, byte[] body, String peerServiceName) {
+ // local service labels
+ Map labels = new HashMap<>(staticMetadataManager.getMergedStaticMetadata());
+
+ // labels from rule expression
+ Set expressionLabelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
+ MetadataContext.LOCAL_SERVICE, peerServiceName);
+ Map ruleExpressionLabels = getExpressionLabels(request, expressionLabelKeys);
+ if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
+ labels.putAll(ruleExpressionLabels);
+ }
+
+ // labels from request
+ if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
+ routerLabelResolvers.forEach(resolver -> {
+ try {
+ Map customResolvedLabels = resolver.resolve(request, body, expressionLabelKeys);
+ if (!CollectionUtils.isEmpty(customResolvedLabels)) {
+ labels.putAll(customResolvedLabels);
+ }
+ }
+ catch (Throwable t) {
+ LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t);
+ }
+ });
+ }
+
+ // labels from downstream
+ Map transitiveLabels = MetadataContextHolder.get()
+ .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
+ labels.putAll(transitiveLabels);
+
+ PolarisRouterContext routerContext = new PolarisRouterContext();
+
+ routerContext.putLabels(RouterConstant.ROUTER_LABELS, labels);
+ routerContext.putLabels(RouterConstant.TRANSITIVE_LABELS, transitiveLabels);
+
+ return routerContext;
+ }
+
+ private Map getExpressionLabels(HttpRequest request, Set labelKeys) {
+ if (CollectionUtils.isEmpty(labelKeys)) {
+ return Collections.emptyMap();
+ }
+
+ return SpringWebExpressionLabelUtils.resolve(request, labelKeys);
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterContextHelper.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterContextHelper.java
new file mode 100644
index 000000000..7a0f9eb8f
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterContextHelper.java
@@ -0,0 +1,68 @@
+/*
+ * 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.resttemplate;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Map;
+
+import com.tencent.cloud.common.constant.RouterConstant;
+import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.cloud.polaris.router.PolarisRouterContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+
+import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
+
+/**
+ * Set router context to request and response.
+ *
+ * @author lepdou 2022-10-09
+ */
+public final class RouterContextHelper {
+ private static final Logger LOGGER = LoggerFactory.getLogger(RouterContextHelper.class);
+
+ private RouterContextHelper() {
+
+ }
+
+ public static void setRouterContextToRequest(HttpRequest request, PolarisRouterContext routerContext) {
+ try {
+ request.getHeaders().add(RouterConstant.HEADER_ROUTER_CONTEXT,
+ URLEncoder.encode(JacksonUtils.serialize2Json(routerContext), UTF_8));
+ }
+ catch (Exception e) {
+ LOGGER.error("[SCT] serialize router context error.", e);
+ }
+ }
+
+ public static void setRouterContextToResponse(PolarisRouterContext routerContext, ClientHttpResponse response) {
+ Map labels = routerContext.getLabels(RouterConstant.ROUTER_LABELS);
+
+ try {
+ response.getHeaders().add(RouterConstant.ROUTER_LABELS,
+ URLEncoder.encode(JacksonUtils.serialize2Json(labels), UTF_8));
+ }
+ catch (UnsupportedEncodingException e) {
+ LOGGER.error("[SCT] add router label to response header error.", e);
+ }
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerBeanPostProcessorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerBeanPostProcessorTest.java
index afbb71883..9efdbb6b3 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerBeanPostProcessorTest.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerBeanPostProcessorTest.java
@@ -18,9 +18,7 @@
package com.tencent.cloud.polaris.router.resttemplate;
-import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.BeanFactoryUtils;
-import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerInterceptorBeanPostProcessor;
import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import org.junit.Assert;
@@ -51,18 +49,12 @@ public class PolarisLoadBalancerBeanPostProcessorTest {
@Mock
private LoadBalancerRequestFactory loadBalancerRequestFactory;
@Mock
- private StaticMetadataManager staticMetadataManager;
- @Mock
- private RouterRuleLabelResolver routerRuleLabelResolver;
- @Mock
private BeanFactory beanFactory;
@Test
public void testWrapperLoadBalancerInterceptor() {
when(beanFactory.getBean(LoadBalancerRequestFactory.class)).thenReturn(loadBalancerRequestFactory);
when(beanFactory.getBean(LoadBalancerClient.class)).thenReturn(loadBalancerClient);
- when(beanFactory.getBean(StaticMetadataManager.class)).thenReturn(staticMetadataManager);
- when(beanFactory.getBean(RouterRuleLabelResolver.class)).thenReturn(routerRuleLabelResolver);
try (MockedStatic mockedBeanFactoryUtils = Mockito.mockStatic(BeanFactoryUtils.class)) {
mockedBeanFactoryUtils.when(() -> BeanFactoryUtils.getBeans(beanFactory, SpringWebRouterLabelResolver.class))
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java
index 81984fe71..d853a0c5d 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java
@@ -22,22 +22,15 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
-import java.util.Set;
import com.tencent.cloud.common.constant.RouterConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
-import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.JacksonUtils;
-import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils;
import com.tencent.cloud.polaris.router.PolarisRouterContext;
-import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
-import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
import org.assertj.core.api.Assertions;
import org.junit.Assert;
import org.junit.Test;
@@ -79,11 +72,7 @@ public class PolarisLoadBalancerInterceptorTest {
@Mock
private LoadBalancerRequestFactory loadBalancerRequestFactory;
@Mock
- private SpringWebRouterLabelResolver routerLabelResolver;
- @Mock
- private StaticMetadataManager staticMetadataManager;
- @Mock
- private RouterRuleLabelResolver routerRuleLabelResolver;
+ private RouterContextFactory routerContextFactory;
@Test
public void testProxyRibbonLoadBalance() throws Exception {
@@ -91,28 +80,13 @@ public class PolarisLoadBalancerInterceptorTest {
String calleeService = "calleeService";
HttpRequest request = new MockedHttpRequest("http://" + calleeService + "/user/get");
+ PolarisRouterContext routerContext = new PolarisRouterContext();
Map routerLabels = new HashMap<>();
- // mock local metadata
- Map localMetadata = new HashMap<>();
- localMetadata.put("k1", "v1");
- localMetadata.put("k2", "v2");
- when(staticMetadataManager.getMergedStaticMetadata()).thenReturn(localMetadata);
- routerLabels.putAll(localMetadata);
-
- // mock expression rule labels
- Set expressionKeys = new HashSet<>();
- expressionKeys.add("${http.method}");
- expressionKeys.add("${http.uri}");
- when(routerRuleLabelResolver.getExpressionLabelKeys(callerService, callerService, calleeService)).thenReturn(expressionKeys);
- routerLabels.putAll(SpringWebExpressionLabelUtils.resolve(request, expressionKeys));
-
- // mock custom resolved from request
- Map customResolvedLabels = new HashMap<>();
- customResolvedLabels.put("k3", "v3");
- customResolvedLabels.put("k4", "v4");
- when(routerLabelResolver.resolve(request, null, expressionKeys)).thenReturn(customResolvedLabels);
- routerLabels.putAll(customResolvedLabels);
+ routerLabels.put("k1", "v1");
+ routerLabels.put("k2", "v12");
+ routerContext.putLabels(RouterConstant.ROUTER_LABELS, routerLabels);
+ when(routerContextFactory.create(request, null, calleeService)).thenReturn(routerContext);
try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
@@ -120,30 +94,21 @@ public class PolarisLoadBalancerInterceptorTest {
MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
- // mock transitive metadata
- Map transitiveLabels = new HashMap<>();
- transitiveLabels.put("k1", "v1");
- transitiveLabels.put("k2", "v22");
- when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels);
- routerLabels.putAll(transitiveLabels);
-
try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) {
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
+
LoadBalancerRequest loadBalancerRequest = new MockedLoadBalancerRequest<>();
when(loadBalancerRequestFactory.createRequest(request, null, null)).thenReturn(loadBalancerRequest);
PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor = new PolarisLoadBalancerInterceptor(loadBalancerClient,
- loadBalancerRequestFactory, Collections.singletonList(routerLabelResolver), staticMetadataManager, routerRuleLabelResolver);
+ loadBalancerRequestFactory, routerContextFactory);
ClientHttpResponse mockedResponse = new MockClientHttpResponse(new byte[] {}, HttpStatus.OK);
when(loadBalancerClient.execute(eq(calleeService), eq(loadBalancerRequest), any(PolarisRouterContext.class))).thenReturn(mockedResponse);
polarisLoadBalancerInterceptor.intercept(request, null, null);
- verify(staticMetadataManager).getMergedStaticMetadata();
- verify(routerRuleLabelResolver).getExpressionLabelKeys(callerService, callerService, calleeService);
- verify(routerLabelResolver).resolve(request, null, expressionKeys);
String encodedLabelsContent;
try {
encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(routerLabels), UTF_8);
@@ -170,9 +135,7 @@ public class PolarisLoadBalancerInterceptorTest {
when(notRibbonLoadBalancerClient.execute(calleeService, loadBalancerRequest)).thenReturn(mockedResponse);
PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor = new PolarisLoadBalancerInterceptor(
- notRibbonLoadBalancerClient, loadBalancerRequestFactory,
- Collections.singletonList(routerLabelResolver), staticMetadataManager,
- routerRuleLabelResolver);
+ notRibbonLoadBalancerClient, loadBalancerRequestFactory, routerContextFactory);
ClientHttpResponse response = polarisLoadBalancerInterceptor.intercept(request, null, null);
@@ -182,69 +145,6 @@ public class PolarisLoadBalancerInterceptorTest {
}
- @Test
- public void testRouterContext() {
- String callerService = "callerService";
- String calleeService = "calleeService";
- HttpRequest request = new MockedHttpRequest("http://" + calleeService + "/user/get");
-
- // mock local metadata
- Map localMetadata = new HashMap<>();
- localMetadata.put("k1", "v1");
- localMetadata.put("k2", "v2");
- when(staticMetadataManager.getMergedStaticMetadata()).thenReturn(localMetadata);
-
- // mock expression rule labels
- Set expressionKeys = new HashSet<>();
- expressionKeys.add("${http.method}");
- expressionKeys.add("${http.uri}");
- when(routerRuleLabelResolver.getExpressionLabelKeys(callerService, callerService, calleeService)).thenReturn(expressionKeys);
-
- // mock custom resolved from request
- Map customResolvedLabels = new HashMap<>();
- customResolvedLabels.put("k2", "v22");
- customResolvedLabels.put("k4", "v4");
- when(routerLabelResolver.resolve(request, null, expressionKeys)).thenReturn(customResolvedLabels);
-
- try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
- mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
- .thenReturn(callerService);
-
- MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
-
- // mock transitive metadata
- Map transitiveLabels = new HashMap<>();
- transitiveLabels.put("k1", "v1");
- transitiveLabels.put("k2", "v22");
- when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels);
-
- try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) {
- mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
-
- PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor =
- new PolarisLoadBalancerInterceptor(loadBalancerClient, loadBalancerRequestFactory,
- Collections.singletonList(routerLabelResolver), staticMetadataManager,
- routerRuleLabelResolver);
-
- PolarisRouterContext routerContext = polarisLoadBalancerInterceptor.genRouterContext(request, null, calleeService);
-
- verify(staticMetadataManager).getMergedStaticMetadata();
- verify(routerRuleLabelResolver).getExpressionLabelKeys(callerService, callerService, calleeService);
- verify(routerLabelResolver).resolve(request, null, expressionKeys);
-
- Assert.assertEquals("v1", routerContext.getLabels(RouterConstant.TRANSITIVE_LABELS).get("k1"));
- Assert.assertEquals("v22", routerContext.getLabels(RouterConstant.TRANSITIVE_LABELS).get("k2"));
- Assert.assertEquals("v1", routerContext.getLabels(RouterConstant.ROUTER_LABELS).get("k1"));
- Assert.assertEquals("v22", routerContext.getLabels(RouterConstant.ROUTER_LABELS).get("k2"));
- Assert.assertEquals("v4", routerContext.getLabels(RouterConstant.ROUTER_LABELS).get("k4"));
- Assert.assertEquals("GET", routerContext.getLabels(RouterConstant.ROUTER_LABELS)
- .get("${http.method}"));
- Assert.assertEquals("/user/get", routerContext.getLabels(RouterConstant.ROUTER_LABELS)
- .get("${http.uri}"));
- }
- }
- }
-
static class MockedLoadBalancerRequest implements LoadBalancerRequest {
@Override
@@ -273,7 +173,7 @@ public class PolarisLoadBalancerInterceptorTest {
@Override
public HttpHeaders getHeaders() {
- return null;
+ return new HttpHeaders();
}
}
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/RouterContextFactoryTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/RouterContextFactoryTest.java
new file mode 100644
index 000000000..6152db7ee
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/RouterContextFactoryTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.resttemplate;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.tencent.cloud.common.constant.RouterConstant;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.metadata.StaticMetadataManager;
+import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
+import com.tencent.cloud.polaris.router.PolarisRouterContext;
+import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
+import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpRequest;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test for {@link RouterContextFactory}
+ *
+ * @author lepdou 2022-10-09
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class RouterContextFactoryTest {
+
+ @Mock
+ private SpringWebRouterLabelResolver springWebRouterLabelResolver;
+ @Mock
+ private StaticMetadataManager staticMetadataManager;
+ @Mock
+ private RouterRuleLabelResolver routerRuleLabelResolver;
+
+ @Test
+ public void testRouterContext() {
+ String callerService = "callerService";
+ String calleeService = "calleeService";
+ HttpRequest request = new MockedHttpRequest("http://" + calleeService + "/user/get");
+
+ // mock local metadata
+ Map localMetadata = new HashMap<>();
+ localMetadata.put("k1", "v1");
+ localMetadata.put("k2", "v2");
+ when(staticMetadataManager.getMergedStaticMetadata()).thenReturn(localMetadata);
+
+ // mock expression rule labels
+ Set expressionKeys = new HashSet<>();
+ expressionKeys.add("${http.method}");
+ expressionKeys.add("${http.uri}");
+ when(routerRuleLabelResolver.getExpressionLabelKeys(callerService, callerService, calleeService)).thenReturn(expressionKeys);
+
+ // mock custom resolved from request
+ Map customResolvedLabels = new HashMap<>();
+ customResolvedLabels.put("k2", "v22");
+ customResolvedLabels.put("k4", "v4");
+ when(springWebRouterLabelResolver.resolve(request, null, expressionKeys)).thenReturn(customResolvedLabels);
+
+ try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
+ .thenReturn(callerService);
+
+ MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
+
+ // mock transitive metadata
+ Map transitiveLabels = new HashMap<>();
+ transitiveLabels.put("k1", "v1");
+ transitiveLabels.put("k2", "v22");
+ when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels);
+
+ try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) {
+ mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
+
+ RouterContextFactory routerContextFactory = new RouterContextFactory(Arrays.asList(springWebRouterLabelResolver),
+ staticMetadataManager, routerRuleLabelResolver);
+
+ PolarisRouterContext routerContext = routerContextFactory.create(request, null, calleeService);
+
+ verify(staticMetadataManager).getMergedStaticMetadata();
+ verify(routerRuleLabelResolver).getExpressionLabelKeys(callerService, callerService, calleeService);
+ verify(springWebRouterLabelResolver).resolve(request, null, expressionKeys);
+
+ Assert.assertEquals("v1", routerContext.getLabels(RouterConstant.TRANSITIVE_LABELS).get("k1"));
+ Assert.assertEquals("v22", routerContext.getLabels(RouterConstant.TRANSITIVE_LABELS).get("k2"));
+ Assert.assertEquals("v1", routerContext.getLabels(RouterConstant.ROUTER_LABELS).get("k1"));
+ Assert.assertEquals("v22", routerContext.getLabels(RouterConstant.ROUTER_LABELS).get("k2"));
+ Assert.assertEquals("v4", routerContext.getLabels(RouterConstant.ROUTER_LABELS).get("k4"));
+ Assert.assertEquals("GET", routerContext.getLabels(RouterConstant.ROUTER_LABELS)
+ .get("${http.method}"));
+ Assert.assertEquals("/user/get", routerContext.getLabels(RouterConstant.ROUTER_LABELS)
+ .get("${http.uri}"));
+ }
+ }
+ }
+
+ static class MockedHttpRequest implements HttpRequest {
+
+ private URI uri;
+
+ MockedHttpRequest(String url) {
+ this.uri = URI.create(url);
+ }
+
+ @Override
+ public String getMethodValue() {
+ return HttpMethod.GET.name();
+ }
+
+ @Override
+ public URI getURI() {
+ return uri;
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ return new HttpHeaders();
+ }
+ }
+}
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/RouterConstant.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/RouterConstant.java
index 68317b99e..00d9633a7 100644
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/RouterConstant.java
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/RouterConstant.java
@@ -38,6 +38,11 @@ public final class RouterConstant {
*/
public static final String ROUTER_LABEL_HEADER = "internal-router-label";
+ /**
+ *
+ */
+ public static final String HEADER_ROUTER_CONTEXT = "routerContext";
+
/**
* Default Private Constructor.
*/
diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml
index 574fea7a1..2c4a25100 100644
--- a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml
+++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml
@@ -23,6 +23,12 @@
spring-cloud-starter-tencent-polaris-router
+