diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ceccf469..772273f0a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,3 +29,4 @@
- [docs: Fix javadoc
error](https://github.com/Tencent/spring-cloud-tencent/pull/375)
- [docs: Update Readme.md](https://github.com/Tencent/spring-cloud-tencent/pull/381)
- [docs:optimize example](https://github.com/Tencent/spring-cloud-tencent/pull/386)
+- [Feature: support spring cloud gateway routers](https://github.com/Tencent/spring-cloud-tencent/pull/388)
diff --git a/spring-cloud-starter-tencent-polaris-router/pom.xml b/spring-cloud-starter-tencent-polaris-router/pom.xml
index 147451b5f..e21ca43f8 100644
--- a/spring-cloud-starter-tencent-polaris-router/pom.xml
+++ b/spring-cloud-starter-tencent-polaris-router/pom.xml
@@ -52,6 +52,20 @@
true
+
+
+ org.springframework.cloud
+ spring-cloud-gateway-server
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+ true
+
+
+
org.springframework.boot
spring-boot-starter-test
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignAutoConfiguration.java
new file mode 100644
index 000000000..89cea90d5
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignAutoConfiguration.java
@@ -0,0 +1,44 @@
+/*
+ * 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.config;
+
+import java.util.List;
+
+import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
+import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
+import com.tencent.cloud.polaris.router.feign.RouterLabelFeignInterceptor;
+import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.lang.Nullable;
+
+@Configuration
+@ConditionalOnClass(name = {"feign.RequestInterceptor"})
+public class FeignAutoConfiguration {
+
+ @Bean
+ public RouterLabelFeignInterceptor routerLabelInterceptor(@Nullable List routerLabelResolvers,
+ MetadataLocalProperties metadataLocalProperties,
+ RouterRuleLabelResolver routerRuleLabelResolver) {
+ return new RouterLabelFeignInterceptor(routerLabelResolvers, metadataLocalProperties, routerRuleLabelResolver);
+ }
+
+}
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 a9e1a54df..ef765fbdd 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,21 +18,17 @@
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.feign.RouterLabelFeignInterceptor;
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerBeanPostProcessor;
-import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
+import com.tencent.cloud.polaris.router.scg.PolarisLoadBalancerClientBeanPostProcessor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
-import org.springframework.lang.Nullable;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
@@ -47,16 +43,17 @@ import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
public class RouterAutoConfiguration {
@Bean
- public RouterLabelFeignInterceptor routerLabelInterceptor(@Nullable List routerLabelResolvers,
- MetadataLocalProperties metadataLocalProperties,
- RouterRuleLabelResolver routerRuleLabelResolver) {
- return new RouterLabelFeignInterceptor(routerLabelResolvers, metadataLocalProperties, routerRuleLabelResolver);
+ @Order(HIGHEST_PRECEDENCE)
+ @ConditionalOnClass(name = "org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor")
+ public PolarisLoadBalancerBeanPostProcessor polarisLoadBalancerBeanPostProcessor() {
+ return new PolarisLoadBalancerBeanPostProcessor();
}
@Bean
@Order(HIGHEST_PRECEDENCE)
- public PolarisLoadBalancerBeanPostProcessor polarisLoadBalancerBeanPostProcessor() {
- return new PolarisLoadBalancerBeanPostProcessor();
+ @ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter")
+ public PolarisLoadBalancerClientBeanPostProcessor polarisLoadBalancerClientBeanPostProcessor() {
+ return new PolarisLoadBalancerClientBeanPostProcessor();
}
@Bean
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientBeanPostProcessor.java
new file mode 100644
index 000000000..ca45878da
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientBeanPostProcessor.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.scg;
+
+import java.util.List;
+
+import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
+import com.tencent.cloud.common.util.BeanFactoryUtils;
+import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
+import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
+
+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.LoadBalancerProperties;
+import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
+import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
+import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
+
+/**
+ * Replaced ReactiveLoadBalancerClientFilter with PolarisReactiveLoadBalancerClientFilter during creating bean phase.
+ *@author lepdou 2022-06-20
+ */
+public class PolarisLoadBalancerClientBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
+
+ private BeanFactory factory;
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.factory = beanFactory;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ // Support spring cloud gateway router.
+ // Replaces the default LoadBalancerClientFilter implementation and returns a custom PolarisLoadBalancerClientFilter
+ if (bean instanceof ReactiveLoadBalancerClientFilter) {
+ LoadBalancerClientFactory loadBalancerClientFactory = this.factory.getBean(LoadBalancerClientFactory.class);
+ GatewayLoadBalancerProperties gatewayLoadBalancerProperties = this.factory.getBean(GatewayLoadBalancerProperties.class);
+ LoadBalancerProperties loadBalancerProperties = this.factory.getBean(LoadBalancerProperties.class);
+ List routerLabelResolvers = BeanFactoryUtils.getBeans(factory, RouterLabelResolver.class);
+ MetadataLocalProperties metadataLocalProperties = this.factory.getBean(MetadataLocalProperties.class);
+ RouterRuleLabelResolver routerRuleLabelResolver = this.factory.getBean(RouterRuleLabelResolver.class);
+
+ return new PolarisReactiveLoadBalancerClientFilter(
+ loadBalancerClientFactory, gatewayLoadBalancerProperties, loadBalancerProperties,
+ metadataLocalProperties, routerRuleLabelResolver, routerLabelResolvers);
+ }
+ return bean;
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/scg/PolarisReactiveLoadBalancerClientFilter.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/scg/PolarisReactiveLoadBalancerClientFilter.java
new file mode 100644
index 000000000..b7cb97807
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/scg/PolarisReactiveLoadBalancerClientFilter.java
@@ -0,0 +1,264 @@
+/*
+ * 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.scg;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+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.ExpressionLabelUtils;
+import com.tencent.cloud.common.util.JacksonUtils;
+import com.tencent.cloud.polaris.router.RouterConstants;
+import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
+import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.CompletionContext;
+import org.springframework.cloud.client.loadbalancer.DefaultRequest;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycle;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycleValidator;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
+import org.springframework.cloud.client.loadbalancer.Request;
+import org.springframework.cloud.client.loadbalancer.RequestData;
+import org.springframework.cloud.client.loadbalancer.RequestDataContext;
+import org.springframework.cloud.client.loadbalancer.Response;
+import org.springframework.cloud.client.loadbalancer.ResponseData;
+import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
+import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
+import org.springframework.cloud.gateway.support.NotFoundException;
+import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
+import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
+import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.server.ServerWebExchange;
+
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
+
+/**
+ * ReactiveLoadBalancerClientFilter does not have the ability to pass route labels, so it is replaced with PolarisReactiveLoadBalancerClientFilter.
+ * The passed route labels are used in {@link com.tencent.cloud.polaris.router.PolarisRouterServiceInstanceListSupplier}.
+ *@author lepdou 2022-06-20
+ */
+public class PolarisReactiveLoadBalancerClientFilter extends ReactiveLoadBalancerClientFilter {
+ private static final Logger log = LoggerFactory.getLogger(PolarisReactiveLoadBalancerClientFilter.class);
+
+ private final LoadBalancerClientFactory clientFactory;
+ private final GatewayLoadBalancerProperties gatewayLoadBalancerProperties;
+ private final LoadBalancerProperties loadBalancerProperties;
+ private final MetadataLocalProperties metadataLocalProperties;
+ private final RouterRuleLabelResolver routerRuleLabelResolver;
+ private final List routerLabelResolvers;
+
+ public PolarisReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory,
+ GatewayLoadBalancerProperties gatewayLoadBalancerProperties,
+ LoadBalancerProperties loadBalancerProperties,
+ MetadataLocalProperties metadataLocalProperties,
+ RouterRuleLabelResolver routerRuleLabelResolver,
+ List routerLabelResolvers) {
+ super(clientFactory, gatewayLoadBalancerProperties, loadBalancerProperties);
+
+ this.clientFactory = clientFactory;
+ this.gatewayLoadBalancerProperties = gatewayLoadBalancerProperties;
+ this.loadBalancerProperties = loadBalancerProperties;
+ this.metadataLocalProperties = metadataLocalProperties;
+ this.routerRuleLabelResolver = routerRuleLabelResolver;
+ this.routerLabelResolvers = routerLabelResolvers;
+ }
+
+ /**
+ * Copied from ReactiveLoadBalancerClientFilter, and create new RequestData for passing router labels.
+ */
+ @Override
+ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
+ String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
+ if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
+ return chain.filter(exchange);
+ }
+ // preserve the original url
+ addOriginalRequestUrl(exchange, url);
+
+ if (log.isTraceEnabled()) {
+ log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);
+ }
+
+ URI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
+ String serviceId = requestUri.getHost();
+ Set supportedLifecycleProcessors = LoadBalancerLifecycleValidator
+ .getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
+ RequestDataContext.class, ResponseData.class, ServiceInstance.class);
+
+ // Pass route tags through http headers
+ HttpHeaders routerHttpHeaders = genRouterHttpHeaders(exchange, serviceId);
+
+ ServerHttpRequest request = exchange.getRequest();
+ RequestData requestData = new RequestData(request.getMethod(), request.getURI(), routerHttpHeaders,
+ new HttpHeaders(), new HashMap<>());
+ DefaultRequest lbRequest = new DefaultRequest<>(new RequestDataContext(
+ requestData, getHint(serviceId, loadBalancerProperties.getHint())));
+
+ return choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext(response -> {
+
+ if (!response.hasServer()) {
+ supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
+ .onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, response)));
+ throw NotFoundException.create(gatewayLoadBalancerProperties.isUse404(),
+ "Unable to find instance for " + url.getHost());
+ }
+
+ ServiceInstance retrievedInstance = response.getServer();
+
+ URI uri = exchange.getRequest().getURI();
+
+ // if the `lb:` mechanism was used, use `` as the default,
+ // if the loadbalancer doesn't provide one.
+ String overrideScheme = retrievedInstance.isSecure() ? "https" : "http";
+ if (schemePrefix != null) {
+ overrideScheme = url.getScheme();
+ }
+
+ DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(retrievedInstance,
+ overrideScheme);
+
+ URI requestUrl = reconstructURI(serviceInstance, uri);
+
+ if (log.isTraceEnabled()) {
+ log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
+ }
+ exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
+ exchange.getAttributes().put(GATEWAY_LOADBALANCER_RESPONSE_ATTR, response);
+ supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, response));
+ }).then(chain.filter(exchange))
+ .doOnError(throwable -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
+ .onComplete(new CompletionContext(
+ CompletionContext.Status.FAILED, throwable, lbRequest,
+ exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR)))))
+ .doOnSuccess(aVoid -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
+ .onComplete(new CompletionContext(
+ CompletionContext.Status.SUCCESS, lbRequest,
+ exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR),
+ new ResponseData(exchange.getResponse(), new RequestData(exchange.getRequest()))))));
+ }
+
+ protected URI reconstructURI(ServiceInstance serviceInstance, URI original) {
+ return LoadBalancerUriTools.reconstructURI(serviceInstance, original);
+ }
+
+ private Mono> choose(Request lbRequest, String serviceId,
+ Set supportedLifecycleProcessors) {
+ ReactorLoadBalancer loadBalancer = this.clientFactory.getInstance(serviceId,
+ ReactorServiceInstanceLoadBalancer.class);
+ if (loadBalancer == null) {
+ throw new NotFoundException("No loadbalancer available for " + serviceId);
+ }
+ supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
+ return loadBalancer.choose(lbRequest);
+ }
+
+ // no actual used
+ private String getHint(String serviceId, Map hints) {
+ String defaultHint = hints.getOrDefault("default", "default");
+ String hintPropertyValue = hints.get(serviceId);
+ return hintPropertyValue != null ? hintPropertyValue : defaultHint;
+ }
+
+ // In order to be consistent with feign and restTemplate,
+ // the router label is passed through the http header uniformly instead of the original hint mechanism.
+ HttpHeaders genRouterHttpHeaders(ServerWebExchange exchange, String peerServiceName) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add(RouterConstants.ROUTER_LABEL_HEADER, genRouterHint(exchange, peerServiceName));
+ return headers;
+ }
+
+ private String genRouterHint(ServerWebExchange exchange, String peerServiceName) {
+ Map routerLabels = genRouterLabels(exchange, peerServiceName);
+ String encodedLabelsContent;
+ try {
+ encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(routerLabels), StandardCharsets.UTF_8.name());
+ }
+ catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("unsupported charset exception " + StandardCharsets.UTF_8.name());
+ }
+ return encodedLabelsContent;
+ }
+
+ private Map genRouterLabels(ServerWebExchange exchange, String peerServiceName) {
+ // local service labels
+ Map labels = new HashMap<>(metadataLocalProperties.getContent());
+
+ // labels from rule expression
+ Map ruleExpressionLabels = getExpressionLabels(exchange, peerServiceName);
+ if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
+ labels.putAll(ruleExpressionLabels);
+ }
+
+ // labels from request
+ if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
+ routerLabelResolvers.forEach(resolver -> {
+ try {
+ Map customResolvedLabels = resolver.resolve(exchange);
+ if (!CollectionUtils.isEmpty(customResolvedLabels)) {
+ labels.putAll(customResolvedLabels);
+ }
+ }
+ catch (Throwable t) {
+ log.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t);
+ }
+ });
+ }
+
+ // labels from downstream
+ Map transitiveLabels = MetadataContextHolder.get()
+ .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
+ labels.putAll(transitiveLabels);
+
+ return labels;
+ }
+
+ private Map getExpressionLabels(ServerWebExchange exchange, String peerServiceName) {
+ Set labelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
+ MetadataContext.LOCAL_SERVICE, peerServiceName);
+
+ if (CollectionUtils.isEmpty(labelKeys)) {
+ return Collections.emptyMap();
+ }
+
+ return ExpressionLabelUtils.resolve(exchange, labelKeys);
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/RouterLabelResolver.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/RouterLabelResolver.java
index 99a6b5bf3..d885581ca 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/RouterLabelResolver.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/RouterLabelResolver.java
@@ -25,6 +25,7 @@ import feign.RequestTemplate;
import org.springframework.core.Ordered;
import org.springframework.http.HttpRequest;
+import org.springframework.web.server.ServerWebExchange;
/**
* The spi for resolving labels from request.
@@ -51,4 +52,13 @@ public interface RouterLabelResolver extends Ordered {
default Map resolve(HttpRequest request, byte[] body) {
return Collections.emptyMap();
}
+
+ /**
+ * resolve labels from server web exchange.
+ * @param exchange the server web exchange.
+ * @return resolved labels
+ */
+ default Map resolve(ServerWebExchange exchange) {
+ return Collections.emptyMap();
+ }
}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories
index d33dcea7f..3b20deb56 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories
@@ -1,2 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
- com.tencent.cloud.polaris.router.config.RouterAutoConfiguration
+ com.tencent.cloud.polaris.router.config.RouterAutoConfiguration,\
+ com.tencent.cloud.polaris.router.config.FeignAutoConfiguration
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientBeanPostProcessorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientBeanPostProcessorTest.java
new file mode 100644
index 000000000..35e91ca73
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientBeanPostProcessorTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.scg;
+
+import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
+import com.tencent.cloud.common.util.BeanFactoryUtils;
+import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
+import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
+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.beans.factory.BeanFactory;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
+import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
+import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
+import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
+
+import static org.mockito.Mockito.when;
+
+
+/**
+ * Test for ${@link PolarisLoadBalancerClientBeanPostProcessor}
+ *@author lepdou 2022-07-04
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class PolarisLoadBalancerClientBeanPostProcessorTest {
+
+ @Mock
+ private BeanFactory beanFactory;
+ @Mock
+ private LoadBalancerClientFactory loadBalancerClientFactory;
+ @Mock
+ private GatewayLoadBalancerProperties gatewayLoadBalancerProperties;
+ @Mock
+ private LoadBalancerProperties loadBalancerProperties;
+ @Mock
+ private MetadataLocalProperties metadataLocalProperties;
+ @Mock
+ private RouterRuleLabelResolver routerRuleLabelResolver;
+
+ @Test
+ public void testWrapReactiveLoadBalancerClientFilter() {
+ when(beanFactory.getBean(LoadBalancerClientFactory.class)).thenReturn(loadBalancerClientFactory);
+ when(beanFactory.getBean(GatewayLoadBalancerProperties.class)).thenReturn(gatewayLoadBalancerProperties);
+ when(beanFactory.getBean(LoadBalancerProperties.class)).thenReturn(loadBalancerProperties);
+ when(beanFactory.getBean(MetadataLocalProperties.class)).thenReturn(metadataLocalProperties);
+ when(beanFactory.getBean(RouterRuleLabelResolver.class)).thenReturn(routerRuleLabelResolver);
+
+ try (MockedStatic mockedBeanFactoryUtils = Mockito.mockStatic(BeanFactoryUtils.class)) {
+ mockedBeanFactoryUtils.when(() -> BeanFactoryUtils.getBeans(beanFactory, RouterLabelResolver.class))
+ .thenReturn(null);
+
+ ReactiveLoadBalancerClientFilter reactiveLoadBalancerClientFilter = new ReactiveLoadBalancerClientFilter(
+ loadBalancerClientFactory, gatewayLoadBalancerProperties, loadBalancerProperties);
+
+ PolarisLoadBalancerClientBeanPostProcessor processor = new PolarisLoadBalancerClientBeanPostProcessor();
+ processor.setBeanFactory(beanFactory);
+
+ Object bean = processor.postProcessBeforeInitialization(reactiveLoadBalancerClientFilter, "");
+
+ Assert.assertTrue(bean instanceof PolarisReactiveLoadBalancerClientFilter);
+ }
+ }
+
+ @Test
+ public void testNotWrapLoadBalancerInterceptor() {
+ PolarisLoadBalancerClientBeanPostProcessor processor = new PolarisLoadBalancerClientBeanPostProcessor();
+ processor.setBeanFactory(beanFactory);
+
+ OtherBean otherBean = new OtherBean();
+ Object bean = processor.postProcessBeforeInitialization(otherBean, "");
+ Assert.assertFalse(bean instanceof PolarisReactiveLoadBalancerClientFilter);
+ Assert.assertTrue(bean instanceof OtherBean);
+ }
+
+ static class OtherBean {
+
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/scg/PolarisReactiveLoadBalancerClientFilterTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/scg/PolarisReactiveLoadBalancerClientFilterTest.java
new file mode 100644
index 000000000..1ed4813fd
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/scg/PolarisReactiveLoadBalancerClientFilterTest.java
@@ -0,0 +1,145 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ */
+
+package com.tencent.cloud.polaris.router.scg;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+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.common.util.JacksonUtils;
+import com.tencent.cloud.polaris.router.RouterConstants;
+import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
+import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
+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.client.loadbalancer.LoadBalancerProperties;
+import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
+import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
+import org.springframework.mock.web.server.MockServerWebExchange;
+import org.springframework.util.CollectionUtils;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test for ${@link PolarisReactiveLoadBalancerClientFilter}
+ *@author lepdou 2022-07-04
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class PolarisReactiveLoadBalancerClientFilterTest {
+
+ 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 RouterLabelResolver routerLabelResolver;
+ @Mock
+ private RouterRuleLabelResolver routerRuleLabelResolver;
+ @Mock
+ private LoadBalancerClientFactory loadBalancerClientFactory;
+ @Mock
+ private GatewayLoadBalancerProperties gatewayLoadBalancerProperties;
+ @Mock
+ private LoadBalancerProperties loadBalancerProperties;
+
+ @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 testGenRouterHttpHeaders() throws UnsupportedEncodingException {
+ PolarisReactiveLoadBalancerClientFilter filter = new PolarisReactiveLoadBalancerClientFilter(loadBalancerClientFactory,
+ gatewayLoadBalancerProperties, loadBalancerProperties, 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);
+
+ MockServerHttpRequest request = MockServerHttpRequest.get("/" + calleeService + "/users")
+ .header("k1", "v1")
+ .queryParam("userid", "zhangsan")
+ .build();
+ MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build();
+
+ Map customMetadata = new HashMap<>();
+ customMetadata.put("k2", "v2");
+ when(routerLabelResolver.resolve(webExchange)).thenReturn(customMetadata);
+
+ HttpHeaders headers = filter.genRouterHttpHeaders(webExchange, calleeService);
+
+ Assert.assertNotNull(headers);
+ List routerHeaders = headers.get(RouterConstants.ROUTER_LABEL_HEADER);
+ Assert.assertFalse(CollectionUtils.isEmpty(routerHeaders));
+
+ Map routerLabels = JacksonUtils.deserialize2Map(URLDecoder.decode(routerHeaders.get(0), StandardCharsets.UTF_8.name()));
+ 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"));
+ }
+}
diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/pom.xml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/pom.xml
new file mode 100644
index 000000000..842363af9
--- /dev/null
+++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/pom.xml
@@ -0,0 +1,28 @@
+
+
+
+ polaris-gateway-example
+ com.tencent.cloud
+ ${revision}
+ ../pom.xml
+
+ 4.0.0
+
+ gateway-callee-service2
+ Spring Cloud Starter Tencent Polaris Gateway Callee Example
+
+
+
+ com.tencent.cloud
+ spring-cloud-starter-tencent-polaris-discovery
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeApplication.java b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeApplication.java
new file mode 100644
index 000000000..4e5e816bd
--- /dev/null
+++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeApplication.java
@@ -0,0 +1,35 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.tencent.cloud.polaris.gateway.example.callee;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Gateway callee application.
+ *
+ * @author Haotian Zhang
+ */
+@SpringBootApplication
+public class GatewayCalleeApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(GatewayCalleeApplication.class, args);
+ }
+
+}
diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeController.java b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeController.java
new file mode 100644
index 000000000..eae571921
--- /dev/null
+++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeController.java
@@ -0,0 +1,71 @@
+/*
+ * 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.gateway.example.callee;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+
+import com.tencent.cloud.common.constant.MetadataConstant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Gateway callee controller.
+ *
+ * @author Haotian Zhang
+ */
+@RestController
+@RequestMapping("/gateway/example/callee")
+public class GatewayCalleeController {
+
+ private static Logger LOG = LoggerFactory.getLogger(GatewayCalleeController.class);
+
+ @Value("${server.port:0}")
+ private int port;
+
+ /**
+ * Get information of callee.
+ * @return information of callee
+ */
+ @RequestMapping("/info")
+ public String info() {
+ LOG.info("Gateway Example Callee [{}] is called.", port);
+ return String.format("Gateway Example Callee [%s] is called.", port);
+ }
+
+ /**
+ * Get metadata in HTTP header.
+ *
+ * @param metadataStr metadata string
+ * @return metadata in HTTP header
+ * @throws UnsupportedEncodingException encoding exception
+ */
+ @RequestMapping("/echo")
+ public String echoHeader(@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String metadataStr)
+ throws UnsupportedEncodingException {
+ LOG.info(URLDecoder.decode(metadataStr, StandardCharsets.UTF_8.name()));
+ return URLDecoder.decode(metadataStr, StandardCharsets.UTF_8.name());
+ }
+
+}
diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/resources/bootstrap.yml
new file mode 100644
index 000000000..f124251c8
--- /dev/null
+++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/resources/bootstrap.yml
@@ -0,0 +1,10 @@
+server:
+ session-timeout: 1800
+ port: 48082
+spring:
+ application:
+ name: GatewayCalleeService
+ cloud:
+ polaris:
+ address: grpc://183.47.111.80:8091
+ namespace: default
diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/pom.xml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/pom.xml
index 8b8b50bf6..b6643b49d 100644
--- a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/pom.xml
+++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/pom.xml
@@ -26,7 +26,7 @@
com.tencent.cloud
- spring-cloud-starter-tencent-metadata-transfer
+ spring-cloud-starter-tencent-polaris-router
@@ -39,4 +39,4 @@
spring-cloud-loadbalancer
-
\ No newline at end of file
+
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 5d949573b..55f03dfa3 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
@@ -9,14 +9,10 @@ spring:
metadata:
content:
a: 1
- transitive:
- - a
polaris:
address: grpc://183.47.111.80:8091
namespace: default
enabled: true
- discovery:
- service-list-refresh-interval: 1000
gateway:
discovery:
locator:
diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/pom.xml b/spring-cloud-tencent-examples/polaris-gateway-example/pom.xml
index 2607823df..e67b8a8dd 100644
--- a/spring-cloud-tencent-examples/polaris-gateway-example/pom.xml
+++ b/spring-cloud-tencent-examples/polaris-gateway-example/pom.xml
@@ -17,6 +17,7 @@
gateway-scg-service
gateway-callee-service
+ gateway-callee-service2
@@ -34,4 +35,4 @@
-
\ No newline at end of file
+
diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/CustomRouterLabelResolver.java b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/CustomRouterLabelResolver.java
index 815b2baff..bd289abb9 100644
--- a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/CustomRouterLabelResolver.java
+++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/CustomRouterLabelResolver.java
@@ -58,7 +58,6 @@ public class CustomRouterLabelResolver implements RouterLabelResolver {
return labels;
}
-
@Override
public int getOrder() {
return 0;
diff --git a/spring-cloud-tencent-polaris-loadbalancer/pom.xml b/spring-cloud-tencent-polaris-loadbalancer/pom.xml
index cfec39a05..da560c430 100644
--- a/spring-cloud-tencent-polaris-loadbalancer/pom.xml
+++ b/spring-cloud-tencent-polaris-loadbalancer/pom.xml
@@ -49,6 +49,24 @@
spring-boot-starter-test
test
+
+
+ org.mockito
+ mockito-inline
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+ net.bytebuddy
+ byte-buddy
+ test
+
-
\ No newline at end of file
+
diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java
index 0a6816ba0..47217fd93 100644
--- a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java
+++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java
@@ -18,7 +18,9 @@
package com.tencent.cloud.polaris.loadbalancer;
+import java.util.Collections;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import com.tencent.cloud.common.metadata.MetadataContext;
@@ -40,26 +42,32 @@ import org.springframework.util.CollectionUtils;
public class LoadBalancerUtils {
public static ServiceInstances transferServersToServiceInstances(Flux> servers) {
- List instances = servers.toStream().flatMap(List::stream).map(serviceInstance -> {
- DefaultInstance instance = new DefaultInstance();
- instance.setNamespace(MetadataContext.LOCAL_NAMESPACE);
- instance.setService(serviceInstance.getServiceId());
- instance.setProtocol(serviceInstance.getScheme());
- instance.setId(serviceInstance.getInstanceId());
- instance.setHost(serviceInstance.getHost());
- instance.setPort(serviceInstance.getPort());
- instance.setWeight(100);
- instance.setMetadata(serviceInstance.getMetadata());
- return instance;
- }).collect(Collectors.toList());
+ AtomicReference> instances = new AtomicReference<>();
+ servers.subscribe(serviceInstances -> {
+ instances.set(serviceInstances.stream().map(serviceInstance -> {
+ DefaultInstance instance = new DefaultInstance();
+ instance.setNamespace(MetadataContext.LOCAL_NAMESPACE);
+ instance.setService(serviceInstance.getServiceId());
+ instance.setProtocol(serviceInstance.getScheme());
+ instance.setId(serviceInstance.getInstanceId());
+ instance.setHost(serviceInstance.getHost());
+ instance.setPort(serviceInstance.getPort());
+ instance.setWeight(100);
+ instance.setMetadata(serviceInstance.getMetadata());
+ return instance;
+ }).collect(Collectors.toList()));
+ });
String serviceName = null;
- if (!CollectionUtils.isEmpty(instances)) {
- serviceName = instances.get(0).getService();
+ if (CollectionUtils.isEmpty(instances.get())) {
+ instances.set(Collections.emptyList());
+ }
+ else {
+ serviceName = instances.get().get(0).getService();
}
ServiceKey serviceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, serviceName);
- return new DefaultServiceInstances(serviceKey, instances);
+ return new DefaultServiceInstances(serviceKey, instances.get());
}
}
diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtilsTest.java b/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtilsTest.java
new file mode 100644
index 000000000..28b62fdd6
--- /dev/null
+++ b/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtilsTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.loadbalancer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
+import com.tencent.polaris.api.pojo.Instance;
+import com.tencent.polaris.api.pojo.ServiceInstances;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+import reactor.core.publisher.Flux;
+
+import org.springframework.cloud.client.DefaultServiceInstance;
+import org.springframework.cloud.client.ServiceInstance;
+
+import static org.mockito.ArgumentMatchers.anyString;
+
+/**
+ * Test for ${@link LoadBalancerUtils}.
+ *@author lepdou 2022-07-04
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class LoadBalancerUtilsTest {
+
+ private static MockedStatic mockedApplicationContextAwareUtils;
+ private static MockedStatic mockedMetadataContextHolder;
+
+ private static final String testNamespaceAndService = "testNamespaceAndService";
+
+ @BeforeClass
+ public static void beforeClass() {
+ mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
+ .thenReturn(testNamespaceAndService);
+
+ MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
+ mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class);
+ mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ mockedApplicationContextAwareUtils.close();
+ mockedMetadataContextHolder.close();
+ }
+
+ @Test
+ public void testTransferEmptyInstances() {
+ ServiceInstances serviceInstances = LoadBalancerUtils.transferServersToServiceInstances(Flux.empty());
+ Assert.assertNotNull(serviceInstances.getInstances());
+ Assert.assertEquals(0, serviceInstances.getInstances().size());
+ }
+
+ @Test
+ public void testTransferNotEmptyInstances() {
+ int instanceSize = 100;
+
+ List instances = new ArrayList<>();
+ for (int i = 0; i < instanceSize; i++) {
+ instances.add(new DefaultServiceInstance("ins" + i, testNamespaceAndService, "127.0.0." + i,
+ 8080, false));
+ }
+
+ ServiceInstances serviceInstances = LoadBalancerUtils.transferServersToServiceInstances(Flux.just(instances));
+
+ Assert.assertNotNull(serviceInstances.getInstances());
+ Assert.assertEquals(instanceSize, serviceInstances.getInstances().size());
+
+ List polarisInstances = serviceInstances.getInstances();
+ for (int i = 0; i < instanceSize; i++) {
+ Instance instance = polarisInstances.get(i);
+ Assert.assertEquals(testNamespaceAndService, instance.getNamespace());
+ Assert.assertEquals(testNamespaceAndService, instance.getService());
+ Assert.assertEquals("ins" + i, instance.getId());
+ Assert.assertEquals("127.0.0." + i, instance.getHost());
+ Assert.assertEquals(8080, instance.getPort());
+ Assert.assertEquals(100, instance.getWeight());
+ }
+ }
+}