diff --git a/CHANGELOG.md b/CHANGELOG.md
index 491208e4c..cc24833ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,3 +6,4 @@
- [Feature: delete implement ServiceInstance](https://github.com/Tencent/spring-cloud-tencent/pull/481)
- [Upgrade owasp esapi's configuration](https://github.com/Tencent/spring-cloud-tencent/pull/492)
- [Bugfix: update byte-buddy scope test to compile](https://github.com/Tencent/spring-cloud-tencent/pull/495)
+- [Feature: zuul supports polaris router](https://github.com/Tencent/spring-cloud-tencent/pull/502)
\ No newline at end of file
diff --git a/spring-cloud-starter-tencent-polaris-router/pom.xml b/spring-cloud-starter-tencent-polaris-router/pom.xml
index 1f68017ef..104dd433e 100644
--- a/spring-cloud-starter-tencent-polaris-router/pom.xml
+++ b/spring-cloud-starter-tencent-polaris-router/pom.xml
@@ -62,6 +62,18 @@
true
+
+ org.springframework.cloud
+ spring-cloud-netflix-zuul
+ true
+
+
+
+ com.netflix.zuul
+ zuul-core
+ true
+
+
org.springframework.boot
spring-boot-actuator
@@ -73,7 +85,7 @@
spring-boot-actuator-autoconfigure
true
-
+
org.springframework.boot
spring-boot-starter-test
@@ -92,6 +104,12 @@
test
+
+ com.squareup.okhttp3
+ okhttp
+ test
+
+
net.bytebuddy
byte-buddy
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 0ac1b7021..ae9a37e17 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
@@ -18,6 +18,9 @@
package com.tencent.cloud.polaris.router.config;
+import java.util.List;
+
+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.beanprocessor.LoadBalancerClientFilterBeanPostProcessor;
@@ -28,10 +31,14 @@ import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouter
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.spi.ServletRouterLabelResolver;
+import com.tencent.cloud.polaris.router.zuul.PolarisRibbonRoutingFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
+import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
+import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -85,4 +92,15 @@ public class RouterAutoConfiguration {
public RuleBasedRouterRequestInterceptor ruleBasedRouterRequestInterceptor(PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties) {
return new RuleBasedRouterRequestInterceptor(polarisRuleBasedRouterProperties);
}
+
+ @Bean(initMethod = "init")
+ @ConditionalOnClass(name = "org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter")
+ public PolarisRibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
+ RibbonCommandFactory> ribbonCommandFactory,
+ MetadataLocalProperties metadataLocalProperties,
+ RouterRuleLabelResolver routerRuleLabelResolver,
+ List routerLabelResolvers) {
+ return new PolarisRibbonRoutingFilter(helper, ribbonCommandFactory, metadataLocalProperties,
+ routerRuleLabelResolver, routerLabelResolvers);
+ }
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/ServletRouterLabelResolver.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/ServletRouterLabelResolver.java
new file mode 100644
index 000000000..db3347378
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/ServletRouterLabelResolver.java
@@ -0,0 +1,45 @@
+/*
+ * 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.spi;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.core.Ordered;
+
+/**
+ * Router label resolver for spring web http request.
+ *
+ * @author jarvisxiong 2022-08-04
+ */
+public interface ServletRouterLabelResolver extends Ordered {
+
+ /**
+ * resolve labels from servlet http request. User can customize expression parser to extract labels.
+ *
+ * @param request the servlet http request.
+ * @param expressionLabelKeys the expression labels which are configured in router rule.
+ * @return resolved labels
+ */
+ default Map resolve(HttpServletRequest request, Set expressionLabelKeys) {
+ return Collections.emptyMap();
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilter.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilter.java
new file mode 100644
index 000000000..e9e737629
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilter.java
@@ -0,0 +1,195 @@
+/*
+ * 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.zuul;
+
+import java.io.InputStream;
+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 javax.servlet.http.HttpServletRequest;
+
+import com.netflix.zuul.context.RequestContext;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
+import com.tencent.cloud.common.util.BeanFactoryUtils;
+import com.tencent.cloud.common.util.expresstion.ServletExpressionLabelUtils;
+import com.tencent.cloud.polaris.router.PolarisRouterContext;
+import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
+import com.tencent.cloud.polaris.router.spi.ServletRouterLabelResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.cloud.netflix.ribbon.support.RibbonCommandContext;
+import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
+import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
+import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
+import org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter;
+import org.springframework.core.Ordered;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.MultiValueMap;
+
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.LOAD_BALANCER_KEY;
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.RETRYABLE_KEY;
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVICE_ID_KEY;
+
+/**
+ * Replaces the default RibbonRoutingFilter implementation.
+ *
+ * @author jarvisxiong 2022-08-04
+ */
+public class PolarisRibbonRoutingFilter extends RibbonRoutingFilter implements BeanFactoryAware {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PolarisRibbonRoutingFilter.class);
+
+ private BeanFactory factory;
+
+ private final MetadataLocalProperties metadataLocalProperties;
+
+ private final RouterRuleLabelResolver routerRuleLabelResolver;
+
+ private final List routerLabelResolvers;
+
+ private boolean useServlet31 = true;
+
+ public PolarisRibbonRoutingFilter(ProxyRequestHelper helper,
+ RibbonCommandFactory> ribbonCommandFactory,
+ MetadataLocalProperties metadataLocalProperties,
+ RouterRuleLabelResolver routerRuleLabelResolver,
+ List routerLabelResolvers) {
+ super(helper, ribbonCommandFactory, Collections.emptyList());
+ this.metadataLocalProperties = metadataLocalProperties;
+ this.routerRuleLabelResolver = routerRuleLabelResolver;
+
+ if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
+ routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder));
+ this.routerLabelResolvers = routerLabelResolvers;
+ }
+ else {
+ this.routerLabelResolvers = null;
+ }
+ // To support Servlet API 3.1 we need to check if getContentLengthLong exists
+ // Spring 5 minimum support is 3.0, so this stays
+ try {
+ HttpServletRequest.class.getMethod("getContentLengthLong");
+ }
+ catch (NoSuchMethodException e) {
+ useServlet31 = false;
+ }
+ }
+
+
+ @Override
+ protected RibbonCommandContext buildCommandContext(RequestContext context) {
+ HttpServletRequest request = context.getRequest();
+
+ MultiValueMap headers = this.helper
+ .buildZuulRequestHeaders(request);
+ MultiValueMap params = this.helper
+ .buildZuulRequestQueryParams(request);
+ String verb = getVerb(request);
+ InputStream requestEntity = getRequestBody(request);
+ if (request.getContentLength() < 0 && !verb.equalsIgnoreCase("GET")) {
+ context.setChunkedRequestBody();
+ }
+
+ String serviceId = (String) context.get(SERVICE_ID_KEY);
+ Boolean retryable = (Boolean) context.get(RETRYABLE_KEY);
+ Object loadBalancerKey = context.get(LOAD_BALANCER_KEY);
+ if (loadBalancerKey == null) {
+ // By default, use the routerContext as loadBalancerKey
+ loadBalancerKey = genRouterContext(request, serviceId);
+ }
+
+ String uri = this.helper.buildZuulRequestURI(request);
+
+ // remove double slashes
+ uri = uri.replace("//", "/");
+
+ long contentLength = useServlet31 ? request.getContentLengthLong()
+ : request.getContentLength();
+
+ return new RibbonCommandContext(serviceId, verb, uri, retryable, headers, params,
+ requestEntity, this.requestCustomizers, contentLength, loadBalancerKey);
+ }
+
+ PolarisRouterContext genRouterContext(HttpServletRequest request, String serviceId) {
+ // local service labels
+ Map labels = new HashMap<>(metadataLocalProperties.getContent());
+
+ // labels from rule expression
+ Set expressionLabelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
+ MetadataContext.LOCAL_SERVICE, serviceId);
+ 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, 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(PolarisRouterContext.ROUTER_LABELS, labels);
+ routerContext.putLabels(PolarisRouterContext.TRANSITIVE_LABELS, transitiveLabels);
+
+ return routerContext;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.factory = beanFactory;
+ }
+
+ private Map getExpressionLabels(HttpServletRequest request, Set labelKeys) {
+ if (CollectionUtils.isEmpty(labelKeys)) {
+ return Collections.emptyMap();
+ }
+
+ return ServletExpressionLabelUtils.resolve(request, labelKeys);
+ }
+
+ private void init() {
+ this.requestCustomizers = BeanFactoryUtils.getBeans(factory, RibbonRequestCustomizer.class);
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilterTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilterTest.java
new file mode 100644
index 000000000..537e4eca5
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/zuul/PolarisRibbonRoutingFilterTest.java
@@ -0,0 +1,373 @@
+/*
+ * 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.zuul;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.netflix.client.config.CommonClientConfigKey;
+import com.netflix.client.config.IClientConfig;
+import com.netflix.hystrix.HystrixCommandProperties;
+import com.netflix.niws.client.http.RestClient;
+import com.netflix.zuul.context.RequestContext;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
+import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
+import com.tencent.cloud.polaris.loadbalancer.PolarisLoadBalancer;
+import com.tencent.cloud.polaris.router.PolarisRouterContext;
+import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
+import com.tencent.cloud.polaris.router.spi.ServletRouterLabelResolver;
+import okhttp3.OkHttpClient;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+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.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
+import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
+import org.springframework.cloud.netflix.ribbon.support.RibbonCommandContext;
+import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
+import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
+import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
+import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommand;
+import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
+import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommand;
+import org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommand;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.LOAD_BALANCER_KEY;
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.RETRYABLE_KEY;
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVICE_ID_KEY;
+
+/**
+ * test for {@link PolarisRibbonRoutingFilter}.
+ *
+ * @author jarvisxiong 2022-08-09
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class PolarisRibbonRoutingFilterTest {
+
+ private static final String callerService = "callerService";
+ private static final String calleeService = "calleeService";
+ private static MockedStatic mockedApplicationContextAwareUtils;
+ private static MockedStatic mockedMetadataContextHolder;
+ @Mock
+ private MetadataLocalProperties metadataLocalProperties;
+ @Mock
+ private RouterRuleLabelResolver routerRuleLabelResolver;
+ @Mock
+ private ServletRouterLabelResolver routerLabelResolver;
+ @Mock
+ private ProxyRequestHelper proxyRequestHelper;
+ @Mock
+ private RibbonCommandFactory> ribbonCommandFactory;
+ @Mock
+ private FallbackProvider fallbackProvider;
+ @Mock
+ private PolarisLoadBalancer polarisLoadBalancer;
+
+ @BeforeClass
+ public static void beforeClass() {
+ 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("t1", "v1");
+ transitiveLabels.put("t2", "v2");
+ when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels);
+
+ mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class);
+ mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ mockedApplicationContextAwareUtils.close();
+ mockedMetadataContextHolder.close();
+ }
+
+ @Test
+ public void testGenRouterContext() {
+ PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter(proxyRequestHelper,
+ ribbonCommandFactory, metadataLocalProperties, routerRuleLabelResolver,
+ Lists.newArrayList(routerLabelResolver));
+
+ Map localMetadata = new HashMap<>();
+ localMetadata.put("env", "blue");
+ when(metadataLocalProperties.getContent()).thenReturn(localMetadata);
+
+ Set expressionLabelKeys = Sets.newHashSet("${http.header.k1}", "${http.query.userid}");
+ when(routerRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString(), anyString()))
+ .thenReturn(expressionLabelKeys);
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setRequestURI("/" + calleeService + "/users");
+ request.addHeader("k1", "v1");
+ request.setQueryString("userid=zhangsan");
+
+ Map customMetadata = new HashMap<>();
+ customMetadata.put("k2", "v2");
+ when(routerLabelResolver.resolve(request, expressionLabelKeys)).thenReturn(customMetadata);
+
+ PolarisRouterContext routerContext = polarisRibbonRoutingFilter.genRouterContext(request, calleeService);
+
+ Map routerLabels = routerContext.getLabels(PolarisRouterContext.ROUTER_LABELS);
+ Assert.assertEquals("v1", routerLabels.get("${http.header.k1}"));
+ Assert.assertEquals("zhangsan", routerLabels.get("${http.query.userid}"));
+ Assert.assertEquals("blue", routerLabels.get("env"));
+ Assert.assertEquals("v1", routerLabels.get("t1"));
+ Assert.assertEquals("v2", routerLabels.get("t2"));
+ }
+
+ @Test
+ public void testHttpCallWithoutRouter() {
+ ZuulProperties zuulProperties = new ZuulProperties();
+ zuulProperties.setRibbonIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
+ zuulProperties.setThreadPool(new ZuulProperties.HystrixThreadPool());
+
+ PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter(
+ new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, metadataLocalProperties,
+ routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver));
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setRequestURI("http://" + calleeService + "/users");
+ request.addHeader("k1", "v1");
+ request.setQueryString("userid=zhangsan");
+
+ RequestContext context = new RequestContext();
+ context.setRequest(request);
+ context.set(SERVICE_ID_KEY, calleeService);
+ context.set(RETRYABLE_KEY, Boolean.FALSE);
+ context.set(LOAD_BALANCER_KEY, calleeService);
+ RequestContext.testSetCurrentContext(context);
+ RibbonCommandContext commandContext = polarisRibbonRoutingFilter.buildCommandContext(context);
+
+ IClientConfig clientConfig = IClientConfig.Builder.newBuilder().build();
+ RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(clientConfig, null);
+ client.setLoadBalancer(polarisLoadBalancer);
+
+ HttpClientRibbonCommand command = new HttpClientRibbonCommand(calleeService, client, commandContext,
+ zuulProperties, fallbackProvider, clientConfig);
+ command.execute();
+
+ verify(polarisLoadBalancer).chooseServer(calleeService);
+ verify(metadataLocalProperties, times(0)).getContent();
+ }
+
+ @Test
+ public void testRestCallWithoutRouter() {
+ ZuulProperties zuulProperties = new ZuulProperties();
+ zuulProperties.setRibbonIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
+ zuulProperties.setThreadPool(new ZuulProperties.HystrixThreadPool());
+
+ PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter(
+ new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, metadataLocalProperties,
+ routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver));
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setRequestURI("http://" + calleeService + "/users");
+ request.addHeader("k1", "v1");
+ request.setQueryString("userid=zhangsan");
+ request.setMethod("GET");
+
+ RequestContext context = new RequestContext();
+ context.setRequest(request);
+ context.set(SERVICE_ID_KEY, calleeService);
+ context.set(RETRYABLE_KEY, Boolean.FALSE);
+ context.set(LOAD_BALANCER_KEY, calleeService);
+ RequestContext.testSetCurrentContext(context);
+ RibbonCommandContext commandContext = polarisRibbonRoutingFilter.buildCommandContext(context);
+
+ IClientConfig clientConfig = IClientConfig.Builder.newBuilder().build();
+ RestClient restClient = new RestClient(polarisLoadBalancer);
+
+ RestClientRibbonCommand command = new RestClientRibbonCommand(calleeService, restClient, commandContext,
+ zuulProperties, fallbackProvider, clientConfig);
+ command.execute();
+
+ // RestClient not use loadBalancerKey
+ verify(polarisLoadBalancer).chooseServer(null);
+ verify(metadataLocalProperties, times(0)).getContent();
+ }
+
+ @Test
+ public void testOkHttpCallWithoutRouter() {
+ ZuulProperties zuulProperties = new ZuulProperties();
+ zuulProperties.setRibbonIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
+ zuulProperties.setThreadPool(new ZuulProperties.HystrixThreadPool());
+
+ PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter(
+ new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, metadataLocalProperties,
+ routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver));
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setRequestURI("http://" + calleeService + "/users");
+ request.addHeader("k1", "v1");
+ request.setQueryString("userid=zhangsan");
+
+ RequestContext context = new RequestContext();
+ context.setRequest(request);
+ context.set(SERVICE_ID_KEY, calleeService);
+ context.set(RETRYABLE_KEY, Boolean.FALSE);
+ context.set(LOAD_BALANCER_KEY, calleeService);
+ RequestContext.testSetCurrentContext(context);
+ RibbonCommandContext commandContext = polarisRibbonRoutingFilter.buildCommandContext(context);
+
+ IClientConfig clientConfig = IClientConfig.Builder.newBuilder().build();
+ OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(new OkHttpClient(), clientConfig, null);
+ client.setLoadBalancer(polarisLoadBalancer);
+
+ OkHttpRibbonCommand command = new OkHttpRibbonCommand(calleeService, client, commandContext, zuulProperties,
+ fallbackProvider, clientConfig);
+ command.execute();
+
+ verify(polarisLoadBalancer).chooseServer(calleeService);
+ verify(metadataLocalProperties, times(0)).getContent();
+ }
+
+ @Test
+ public void testHttpCallWithRouter() {
+ ZuulProperties zuulProperties = new ZuulProperties();
+ zuulProperties.setRibbonIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
+ zuulProperties.setThreadPool(new ZuulProperties.HystrixThreadPool());
+
+ PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter(
+ new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, metadataLocalProperties,
+ routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver));
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setRequestURI("http://" + calleeService + "/users");
+ request.addHeader("k1", "v1");
+ request.setQueryString("userid=zhangsan");
+
+ RequestContext context = new RequestContext();
+ context.setRequest(request);
+ context.set(SERVICE_ID_KEY, calleeService);
+ context.set(RETRYABLE_KEY, Boolean.FALSE);
+ PolarisRouterContext routerContext = polarisRibbonRoutingFilter.genRouterContext(request, calleeService);
+ context.set(LOAD_BALANCER_KEY, routerContext);
+ RequestContext.testSetCurrentContext(context);
+ RibbonCommandContext commandContext = polarisRibbonRoutingFilter.buildCommandContext(context);
+
+ IClientConfig clientConfig = IClientConfig.Builder.newBuilder().build();
+ RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(clientConfig, null);
+ client.setLoadBalancer(polarisLoadBalancer);
+
+ HttpClientRibbonCommand command = new HttpClientRibbonCommand(calleeService, client, commandContext,
+ zuulProperties, fallbackProvider, clientConfig);
+ command.execute();
+ verify(polarisLoadBalancer).chooseServer(routerContext);
+ verify(metadataLocalProperties, times(1)).getContent();
+ }
+
+ @Test
+ public void testRestCallWithRouter() {
+ ZuulProperties zuulProperties = new ZuulProperties();
+ zuulProperties.setRibbonIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
+ zuulProperties.setThreadPool(new ZuulProperties.HystrixThreadPool());
+
+ PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter(
+ new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, metadataLocalProperties,
+ routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver));
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setRequestURI("http://" + calleeService + "/users");
+ request.addHeader("k1", "v1");
+ request.setQueryString("userid=zhangsan");
+ request.setMethod("GET");
+
+ RequestContext context = new RequestContext();
+ context.setRequest(request);
+ context.set(SERVICE_ID_KEY, calleeService);
+ context.set(RETRYABLE_KEY, Boolean.FALSE);
+ PolarisRouterContext routerContext = polarisRibbonRoutingFilter.genRouterContext(request, calleeService);
+ context.set(LOAD_BALANCER_KEY, routerContext);
+ RequestContext.testSetCurrentContext(context);
+ RibbonCommandContext commandContext = polarisRibbonRoutingFilter.buildCommandContext(context);
+
+ IClientConfig clientConfig = IClientConfig.Builder.newBuilder().build();
+ RestClient restClient = new RestClient(polarisLoadBalancer);
+
+ RestClientRibbonCommand command = new RestClientRibbonCommand(calleeService, restClient, commandContext,
+ zuulProperties, fallbackProvider, clientConfig);
+ command.execute();
+
+ // RestClient not use loadBalancerKey
+ verify(polarisLoadBalancer).chooseServer(null);
+ verify(metadataLocalProperties, times(1)).getContent();
+ }
+
+ @Test
+ public void testOkHttpCallWithRouter() {
+ ZuulProperties zuulProperties = new ZuulProperties();
+ zuulProperties.setRibbonIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
+ zuulProperties.setThreadPool(new ZuulProperties.HystrixThreadPool());
+
+ PolarisRibbonRoutingFilter polarisRibbonRoutingFilter = new PolarisRibbonRoutingFilter(
+ new ProxyRequestHelper(zuulProperties), ribbonCommandFactory, metadataLocalProperties,
+ routerRuleLabelResolver, Lists.newArrayList(routerLabelResolver));
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setRequestURI("http://" + calleeService + "/users");
+ request.addHeader("k1", "v1");
+ request.setQueryString("userid=zhangsan");
+ request.setMethod("GET");
+
+ RequestContext context = new RequestContext();
+ context.setRequest(request);
+ context.set(SERVICE_ID_KEY, calleeService);
+ context.set(RETRYABLE_KEY, Boolean.FALSE);
+ PolarisRouterContext routerContext = polarisRibbonRoutingFilter.genRouterContext(request, calleeService);
+ context.set(LOAD_BALANCER_KEY, routerContext);
+ RequestContext.testSetCurrentContext(context);
+ RibbonCommandContext commandContext = polarisRibbonRoutingFilter.buildCommandContext(context);
+
+ IClientConfig clientConfig = IClientConfig.Builder.newBuilder().build();
+ // Retry once by default, so close retry
+ clientConfig.set(CommonClientConfigKey.MaxAutoRetriesNextServer, 0);
+
+ OkHttpLoadBalancingClient client = new OkHttpLoadBalancingClient(new OkHttpClient(), clientConfig, null);
+ client.setLoadBalancer(polarisLoadBalancer);
+
+ OkHttpRibbonCommand command = new OkHttpRibbonCommand(calleeService, client, commandContext, zuulProperties,
+ fallbackProvider, clientConfig);
+ command.execute();
+
+ verify(polarisLoadBalancer).chooseServer(routerContext);
+ verify(metadataLocalProperties, times(1)).getContent();
+ }
+
+}
diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/src/main/resources/bootstrap.yml
index a0956f9ae..f4dda3aa8 100644
--- a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/src/main/resources/bootstrap.yml
+++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/src/main/resources/bootstrap.yml
@@ -15,6 +15,11 @@ spring:
router:
feature-env:
enabled: true
+ metadata:
+ content:
+ a: 1
+ transitive:
+ - a
polaris:
address: grpc://183.47.111.80:8091
namespace: default
diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-zuul-service/pom.xml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-zuul-service/pom.xml
index 2e4060d28..b937a3046 100644
--- a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-zuul-service/pom.xml
+++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-zuul-service/pom.xml
@@ -24,6 +24,11 @@
com.tencent.cloud
+
+ com.tencent.cloud
+ spring-cloud-starter-tencent-polaris-router
+
+
com.tencent.cloud
spring-cloud-starter-tencent-metadata-transfer