From 62d7f9c4487d8669481d2a16e70d7da16a0c62fc Mon Sep 17 00:00:00 2001 From: lepdou Date: Mon, 10 Oct 2022 17:40:10 +0800 Subject: [PATCH] support spring-retry router (#633) --- CHANGELOG.md | 1 + ...dBalancerInterceptorBeanPostProcessor.java | 12 +- .../config/RouterAutoConfiguration.java | 41 +++++ .../PolarisLoadBalancerInterceptor.java | 96 +---------- .../RouterLabelRestTemplateInterceptor.java | 150 ++++++++++++++++++ ...ancerInterceptorBeanPostProcessorTest.java | 8 - ...uterLabelRestTemplateInterceptorTest.java} | 96 ++--------- .../router-caller-service/pom.xml | 6 + 8 files changed, 215 insertions(+), 195 deletions(-) create mode 100644 spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterLabelRestTemplateInterceptor.java rename spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/{PolarisLoadBalancerInterceptorTest.java => RouterLabelRestTemplateInterceptorTest.java} (57%) diff --git a/CHANGELOG.md b/CHANGELOG.md index b53f12e2e..dbacc3555 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,3 +5,4 @@ - [Optimize: optimize configuration conditional & optimize config data tips"](https://github.com/Tencent/spring-cloud-tencent/pull/604) - [Optimize: Maybe remove Chinese characters](https://github.com/Tencent/spring-cloud-tencent/pull/608) - [Bugfix: fix feign report call result error when using feign direct call](https://github.com/Tencent/spring-cloud-tencent/pull/622) +- [Feature: support spring-retry router](https://github.com/Tencent/spring-cloud-tencent/pull/633) 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 fcca546dd..29df08d7b 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 @@ -17,13 +17,7 @@ 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 org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; @@ -53,12 +47,8 @@ public class LoadBalancerInterceptorBeanPostProcessor implements BeanPostProcess if (bean instanceof LoadBalancerInterceptor) { 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); - return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory, - routerLabelResolvers, staticMetadataManager, routerRuleLabelResolver); + return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory); } 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 39ca6fd85..19dd7b419 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,11 @@ package com.tencent.cloud.polaris.router.config; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.tencent.cloud.common.metadata.StaticMetadataManager; import com.tencent.cloud.polaris.context.ServiceRuleManager; import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerInterceptorBeanPostProcessor; @@ -28,7 +33,11 @@ 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.resttemplate.RouterLabelRestTemplateInterceptor; +import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver; +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients; @@ -36,6 +45,8 @@ 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.http.client.ClientHttpRequestInterceptor; +import org.springframework.web.client.RestTemplate; import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE; @@ -86,4 +97,34 @@ public class RouterAutoConfiguration { public RuleBasedRouterRequestInterceptor ruleBasedRouterRequestInterceptor(PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties) { return new RuleBasedRouterRequestInterceptor(polarisRuleBasedRouterProperties); } + + /** + * Create when RestTemplate exists. + * @author liuye 2022-09-14 + */ + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") + @ConditionalOnProperty(value = "spring.cloud.polaris.router.rule-router.enabled", matchIfMissing = true) + protected static class RouterLabelRestTemplateConfig { + + @Autowired(required = false) + private List restTemplates = Collections.emptyList(); + + @Bean + public RouterLabelRestTemplateInterceptor routerLabelRestTemplateInterceptor( + List routerLabelResolvers, + StaticMetadataManager staticMetadataManager, + RouterRuleLabelResolver routerRuleLabelResolver) { + return new RouterLabelRestTemplateInterceptor(routerLabelResolvers, staticMetadataManager, routerRuleLabelResolver); + } + + @Bean + public SmartInitializingSingleton addRouterLabelInterceptorForRestTemplate(RouterLabelRestTemplateInterceptor interceptor) { + return () -> restTemplates.forEach(restTemplate -> { + List list = new ArrayList<>(restTemplate.getInterceptors()); + list.add(interceptor); + restTemplate.setInterceptors(list); + }); + } + } } 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 8cd4c9144..e3fcad7be 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,38 +19,15 @@ 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.RouterConstants; -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.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.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. @@ -59,32 +36,15 @@ import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; * @author lepdou, cheese8 */ 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; public PolarisLoadBalancerInterceptor(LoadBalancerClient loadBalancer, - LoadBalancerRequestFactory requestFactory, - List routerLabelResolvers, - StaticMetadataManager staticMetadataManager, - RouterRuleLabelResolver routerRuleLabelResolver) { + LoadBalancerRequestFactory requestFactory) { 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; - } } @Override @@ -94,61 +54,7 @@ public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor { Assert.state(peerServiceName != null, "Request URI does not contain a valid hostname: " + originalUri); - setLabelsToHeaders(request, body, peerServiceName); - return this.loadBalancer.execute(peerServiceName, new PolarisLoadBalancerRequest<>(request, this.requestFactory.createRequest(request, body, execution))); } - - void setLabelsToHeaders(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); - - // pass label by header - String encodedLabelsContent; - try { - encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(labels), UTF_8); - } - catch (UnsupportedEncodingException e) { - throw new RuntimeException("unsupported charset exception " + UTF_8); - } - request.getHeaders().set(RouterConstants.ROUTER_LABEL_HEADER, encodedLabelsContent); - } - - 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/RouterLabelRestTemplateInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterLabelRestTemplateInterceptor.java new file mode 100644 index 000000000..1d6ef7c00 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterLabelRestTemplateInterceptor.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.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.RouterConstants; +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.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.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; + +/** + * Interceptor used for adding the route label in http headers from context when web client + * is RestTemplate. + * + * @author liuye 2022-09-14 + */ +public class RouterLabelRestTemplateInterceptor implements ClientHttpRequestInterceptor, Ordered { + private static final Logger LOGGER = LoggerFactory.getLogger(RouterLabelRestTemplateInterceptor.class); + + private final List routerLabelResolvers; + private final StaticMetadataManager staticMetadataManager; + private final RouterRuleLabelResolver routerRuleLabelResolver; + + public RouterLabelRestTemplateInterceptor(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; + } + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } + + @Override + public ClientHttpResponse intercept(@NonNull HttpRequest request, @NonNull byte[] body, + @NonNull ClientHttpRequestExecution clientHttpRequestExecution) throws IOException { + final URI originalUri = request.getURI(); + String peerServiceName = originalUri.getHost(); + Assert.state(peerServiceName != null, + "Request URI does not contain a valid hostname: " + originalUri); + + setLabelsToHeaders(request, body, peerServiceName); + + return clientHttpRequestExecution.execute(request, body); + } + + void setLabelsToHeaders(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); + + // pass label by header + String encodedLabelsContent; + try { + encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(labels), UTF_8); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException("unsupported charset exception " + UTF_8); + } + request.getHeaders().set(RouterConstants.ROUTER_LABEL_HEADER, encodedLabelsContent); + } + + 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/test/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessorTest.java index 057759af1..84ea5476d 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessorTest.java +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessorTest.java @@ -17,9 +17,7 @@ package com.tencent.cloud.polaris.router.beanprocessor; -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 org.junit.Assert; @@ -50,18 +48,12 @@ public class LoadBalancerInterceptorBeanPostProcessorTest { @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/RouterLabelRestTemplateInterceptorTest.java similarity index 57% rename from spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java rename to spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/RouterLabelRestTemplateInterceptorTest.java index 2d53141f8..056cfb0ed 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/RouterLabelRestTemplateInterceptorTest.java @@ -13,11 +13,11 @@ * 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.net.URLDecoder; import java.util.Collections; @@ -34,8 +34,8 @@ import com.tencent.cloud.common.util.ApplicationContextAwareUtils; import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; import com.tencent.cloud.polaris.router.spi.SpringWebRouterLabelResolver; +import org.assertj.core.api.Assertions; import org.junit.AfterClass; -import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,35 +44,24 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; -import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest; -import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpResponse; -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** - * test for {@link PolarisLoadBalancerInterceptor}. - * - * @author lepdou, cheese8 + * test for {@link RouterLabelRestTemplateInterceptor} + * @author liuye 2022-09-16 */ @RunWith(MockitoJUnitRunner.class) -public class PolarisLoadBalancerInterceptorTest { +public class RouterLabelRestTemplateInterceptorTest { private static MockedStatic mockedApplicationContextAwareUtils; private static MockedStatic mockedMetadataContextHolder; @Mock - private LoadBalancerClient loadBalancerClient; - @Mock - private LoadBalancerRequestFactory loadBalancerRequestFactory; - @Mock private SpringWebRouterLabelResolver routerLabelResolver; @Mock private StaticMetadataManager staticMetadataManager; @@ -94,54 +83,6 @@ public class PolarisLoadBalancerInterceptorTest { mockedMetadataContextHolder.close(); } - @Test - public void testProxyRibbonLoadBalance() throws Exception { - 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("k3", "v3"); - customResolvedLabels.put("k4", "v4"); - when(routerLabelResolver.resolve(request, null, expressionKeys)).thenReturn(customResolvedLabels); - - 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); - - 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); - - polarisLoadBalancerInterceptor.intercept(request, null, null); - - verify(staticMetadataManager).getMergedStaticMetadata(); - verify(routerRuleLabelResolver).getExpressionLabelKeys(callerService, callerService, calleeService); - verify(routerLabelResolver).resolve(request, null, expressionKeys); - } - @Test public void testRouterContext() throws Exception { String callerService = "callerService"; @@ -177,30 +118,23 @@ public class PolarisLoadBalancerInterceptorTest { mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); - PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor = new PolarisLoadBalancerInterceptor(loadBalancerClient, - loadBalancerRequestFactory, Collections.singletonList(routerLabelResolver), staticMetadataManager, routerRuleLabelResolver); + RouterLabelRestTemplateInterceptor routerLabelRestTemplateInterceptor = new RouterLabelRestTemplateInterceptor( + Collections.singletonList(routerLabelResolver), staticMetadataManager, routerRuleLabelResolver); - polarisLoadBalancerInterceptor.setLabelsToHeaders(request, null, calleeService); + routerLabelRestTemplateInterceptor.setLabelsToHeaders(request, null, calleeService); verify(staticMetadataManager).getMergedStaticMetadata(); verify(routerRuleLabelResolver).getExpressionLabelKeys(callerService, callerService, calleeService); verify(routerLabelResolver).resolve(request, null, expressionKeys); - Map headers = JacksonUtils.deserialize2Map(URLDecoder.decode(request.getHeaders() - .get(RouterConstants.ROUTER_LABEL_HEADER).get(0), UTF_8)); - Assert.assertEquals("v1", headers.get("k1")); - Assert.assertEquals("v22", headers.get("k2")); - Assert.assertEquals("v4", headers.get("k4")); - Assert.assertEquals("GET", headers.get("${http.method}")); - Assert.assertEquals("/user/get", headers.get("${http.uri}")); - } - - static class MockedLoadBalancerRequest implements LoadBalancerRequest { - @Override - public T apply(ServiceInstance instance) { - return null; - } + Map headers = JacksonUtils.deserialize2Map(URLDecoder.decode(request.getHeaders() + .get(RouterConstants.ROUTER_LABEL_HEADER).get(0), "UTF-8")); + Assertions.assertThat("v1").isEqualTo(headers.get("k1")); + Assertions.assertThat("v22").isEqualTo(headers.get("k2")); + Assertions.assertThat("v4").isEqualTo(headers.get("k4")); + Assertions.assertThat("GET").isEqualTo(headers.get("${http.method}")); + Assertions.assertThat("/user/get").isEqualTo(headers.get("${http.uri}")); } static class MockedHttpRequest implements HttpRequest { 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 + + org.springframework.retry + spring-retry + 1.3.1 + + org.springframework.boot spring-boot-starter-web