From 89536c5c966c92b34d81bedfa7184e60d9e4a6e8 Mon Sep 17 00:00:00 2001 From: "Shanyou Yu (Sean Yu)" Date: Fri, 14 Apr 2023 16:46:01 +0800 Subject: [PATCH] feature: add polaris ring hash load balancer (#971) --- CHANGELOG.md | 3 +- ...olarisLoadBalancerClientConfiguration.java | 38 +++++- ...olarisLoadBalancerRingHashKeyProvider.java | 42 ++++++ .../PolarisRingHashLoadBalancer.java | 111 +++++++++++++++ ...=> PolarisWeightedRandomLoadBalancer.java} | 15 +- ...itional-spring-configuration-metadata.json | 27 +++- .../PolarisRingHashLoadBalancerTest.java | 129 ++++++++++++++++++ ...domLoadBalancerAutoConfigurationTest.java} | 40 +++++- ...olarisWeightedRandomLoadBalancerTest.java} | 8 +- 9 files changed, 392 insertions(+), 21 deletions(-) create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerRingHashKeyProvider.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisRingHashLoadBalancer.java rename spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/{PolarisLoadBalancer.java => PolarisWeightedRandomLoadBalancer.java} (85%) create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisRingHashLoadBalancerTest.java rename spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/{PolarisLoadBalancerAutoConfigurationTest.java => PolarisWeightedRandomLoadBalancerAutoConfigurationTest.java} (63%) rename spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/{PolarisLoadBalancerTest.java => PolarisWeightedRandomLoadBalancerTest.java} (92%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c271dcec..abe43bf11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,4 +7,5 @@ - [fix: fix nacos CircuitBreaker disable bug.](https://github.com/Tencent/spring-cloud-tencent/pull/949) - [feature: add config for customized local port.](https://github.com/Tencent/spring-cloud-tencent/pull/957) - [refactor:refactor reactor code in router module.](https://github.com/Tencent/spring-cloud-tencent/pull/959) -- [refactor: refactor circuitbreaker .](https://github.com/Tencent/spring-cloud-tencent/pull/964) \ No newline at end of file +- [refactor: refactor circuitbreaker .](https://github.com/Tencent/spring-cloud-tencent/pull/964) +- [feature: add polaris ring hash load balancer .](https://github.com/Tencent/spring-cloud-tencent/pull/971) diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java index 3d9bf1a40..bda6babc3 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java @@ -29,7 +29,9 @@ import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; +import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer; import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; +import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; import org.springframework.context.ConfigurableApplicationContext; @@ -53,15 +55,43 @@ public class PolarisLoadBalancerClientConfiguration { */ private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465; - private final static String STRATEGY_WEIGHT = "polarisWeighted"; + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = "roundRobin", matchIfMissing = true) + public ReactorLoadBalancer roundRobinLoadBalancer(Environment environment, + LoadBalancerClientFactory loadBalancerClientFactory) { + String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); + return new RoundRobinLoadBalancer( + loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = "random") + public ReactorLoadBalancer randomLoadBalancer(Environment environment, + LoadBalancerClientFactory loadBalancerClientFactory) { + String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); + return new RandomLoadBalancer( + loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = "polarisWeightedRandom") + public ReactorLoadBalancer polarisWeightedLoadBalancer(Environment environment, + LoadBalancerClientFactory loadBalancerClientFactory, RouterAPI routerAPI) { + String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); + return new PolarisWeightedRandomLoadBalancer(name, + loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), routerAPI); + } @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = STRATEGY_WEIGHT) - public ReactorLoadBalancer polarisLoadBalancer(Environment environment, + @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = "polarisRingHash") + public ReactorLoadBalancer polarisRingHashLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory, RouterAPI routerAPI) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); - return new PolarisLoadBalancer(name, + return new PolarisRingHashLoadBalancer(name, loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), routerAPI); } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerRingHashKeyProvider.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerRingHashKeyProvider.java new file mode 100644 index 000000000..8a94f799b --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerRingHashKeyProvider.java @@ -0,0 +1,42 @@ +/* + * 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 com.tencent.cloud.common.metadata.MetadataContextHolder; + +/** + * PolarisLoadBalancerRingHashKeyProvider. + * + * @author seanthefish + */ +public final class PolarisLoadBalancerRingHashKeyProvider { + + private static final String LOAD_BALANCER_HASH_KEY = "LOAD_BALANCER_HASH_KEY"; + + private PolarisLoadBalancerRingHashKeyProvider() { + } + + public static void hashKey(String key) { + MetadataContextHolder.get().setLoadbalancer(LOAD_BALANCER_HASH_KEY, key); + } + + static String getHashKey() { + return MetadataContextHolder.get().getLoadbalancerMetadata().get(LOAD_BALANCER_HASH_KEY); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisRingHashLoadBalancer.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisRingHashLoadBalancer.java new file mode 100644 index 000000000..26684fea1 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisRingHashLoadBalancer.java @@ -0,0 +1,111 @@ +/* + * 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.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.pojo.PolarisServiceInstance; +import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; +import com.tencent.polaris.api.pojo.DefaultServiceInstances; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.Criteria; +import com.tencent.polaris.router.api.core.RouterAPI; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceRequest; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.DefaultResponse; +import org.springframework.cloud.client.loadbalancer.EmptyResponse; +import org.springframework.cloud.client.loadbalancer.Request; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier; +import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; + +/** + * PolarisRingHashLoadBalancer. + * + * @author sean yu + */ +public class PolarisRingHashLoadBalancer implements ReactorServiceInstanceLoadBalancer { + + private static final Logger log = LoggerFactory.getLogger(PolarisWeightedRandomLoadBalancer.class); + + private final String serviceId; + + private final RouterAPI routerAPI; + + private ObjectProvider supplierObjectProvider; + + public PolarisRingHashLoadBalancer(String serviceId, + ObjectProvider supplierObjectProvider, + RouterAPI routerAPI) { + this.serviceId = serviceId; + this.supplierObjectProvider = supplierObjectProvider; + this.routerAPI = routerAPI; + } + + private static ServiceInstances convertToPolarisServiceInstances(List serviceInstances) { + ServiceKey serviceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, serviceInstances.get(0).getServiceId()); + List polarisInstances = serviceInstances.stream() + .map(serviceInstance -> ((PolarisServiceInstance) serviceInstance).getPolarisInstance()) + .collect(Collectors.toList()); + return new DefaultServiceInstances(serviceKey, polarisInstances); + } + + @Override + public Mono> choose(Request request) { + ServiceInstanceListSupplier supplier = supplierObjectProvider + .getIfAvailable(NoopServiceInstanceListSupplier::new); + String hashKey = Optional.ofNullable(PolarisLoadBalancerRingHashKeyProvider.getHashKey()).orElse(""); + return supplier.get(request).next().map(serviceInstances -> getInstanceResponse(serviceInstances, hashKey)); + } + + private Response getInstanceResponse(List serviceInstances, String hashKey) { + if (serviceInstances.isEmpty()) { + log.warn("No servers available for service: " + this.serviceId); + return new EmptyResponse(); + } + + ProcessLoadBalanceRequest request = new ProcessLoadBalanceRequest(); + request.setDstInstances(convertToPolarisServiceInstances(serviceInstances)); + request.setLbPolicy(LoadBalanceConfig.LOAD_BALANCE_RING_HASH); + Criteria criteria = new Criteria(); + criteria.setHashKey(hashKey); + request.setCriteria(criteria); + + try { + ProcessLoadBalanceResponse response = routerAPI.processLoadBalance(request); + return new DefaultResponse(new PolarisServiceInstance(response.getTargetInstance())); + } + catch (Exception e) { + log.warn("PolarisRoutingLoadbalancer error", e); + return new EmptyResponse(); + } + } + +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancer.java similarity index 85% rename from spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java rename to spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancer.java index 03057a4fe..775f44457 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancer.java @@ -42,7 +42,7 @@ import org.springframework.cloud.client.loadbalancer.EmptyResponse; import org.springframework.cloud.client.loadbalancer.Request; import org.springframework.cloud.client.loadbalancer.Response; import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier; -import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; /** @@ -50,18 +50,17 @@ import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; * * @author liaochuntao */ -public class PolarisLoadBalancer extends RoundRobinLoadBalancer { +public class PolarisWeightedRandomLoadBalancer implements ReactorServiceInstanceLoadBalancer { - private static final Logger LOGGER = LoggerFactory.getLogger(PolarisLoadBalancer.class); + private static final Logger log = LoggerFactory.getLogger(PolarisWeightedRandomLoadBalancer.class); private final String serviceId; private final RouterAPI routerAPI; - private final ObjectProvider supplierObjectProvider; + private ObjectProvider supplierObjectProvider; - public PolarisLoadBalancer(String serviceId, ObjectProvider supplierObjectProvider, RouterAPI routerAPI) { - super(supplierObjectProvider, serviceId); + public PolarisWeightedRandomLoadBalancer(String serviceId, ObjectProvider supplierObjectProvider, RouterAPI routerAPI) { this.serviceId = serviceId; this.supplierObjectProvider = supplierObjectProvider; this.routerAPI = routerAPI; @@ -84,7 +83,7 @@ public class PolarisLoadBalancer extends RoundRobinLoadBalancer { private Response getInstanceResponse(List serviceInstances) { if (serviceInstances.isEmpty()) { - LOGGER.warn("No servers available for service: " + this.serviceId); + log.warn("No servers available for service: " + this.serviceId); return new EmptyResponse(); } @@ -98,7 +97,7 @@ public class PolarisLoadBalancer extends RoundRobinLoadBalancer { return new DefaultResponse(new PolarisServiceInstance(response.getTargetInstance())); } catch (Exception e) { - LOGGER.warn("PolarisRoutingLoadbalancer error", e); + log.warn("PolarisRoutingLoadbalancer error", e); return new EmptyResponse(); } } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json index f5c547207..a16b6b8e4 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -75,8 +75,8 @@ { "name": "spring.cloud.polaris.loadbalancer.strategy", "type": "java.lang.String", - "defaultValue": "random", - "description": "retry,best_available,availability_filtering,round_robin,weighted_response_time,zone_avoidance,random,consistent_hash,weighted_random." + "defaultValue": "roundRobin", + "description": "loadbalancer strategy." }, { "name": "spring.cloud.nacos.discovery.enabled", @@ -122,5 +122,28 @@ "description": "the nacos authentication cluster-name.", "sourceType": "com.tencent.cloud.polaris.extend.nacos.NacosContextProperties" } + ], + "hints" : [ + { + "name": "spring.cloud.polaris.loadbalancer.strategy", + "values": [ + { + "value": "roundRobin", + "description": "round robin load balancer." + }, + { + "value": "random", + "description": "random load balancer." + }, + { + "value": "polarisWeightedRandom", + "description": "polaris weighted random load balancer." + }, + { + "value": "polarisRingHash", + "description": "polaris ring hash load balancer." + } + ] + } ] } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisRingHashLoadBalancerTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisRingHashLoadBalancerTest.java new file mode 100644 index 000000000..83779b827 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisRingHashLoadBalancerTest.java @@ -0,0 +1,129 @@ +/* + * 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.StaticMetadataManager; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.pojo.PolarisServiceInstance; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.router.api.core.RouterAPI; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.Request; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.context.ApplicationContext; + +import static com.tencent.cloud.common.metadata.MetadataContext.LOCAL_NAMESPACE; +import static com.tencent.cloud.common.metadata.MetadataContext.LOCAL_SERVICE; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * PolarisRingHashLoadBalancerTest. + * + * @author sean yu + */ +@ExtendWith(MockitoExtension.class) +public class PolarisRingHashLoadBalancerTest { + + private static MockedStatic mockedApplicationContextAwareUtils; + private static Instance testInstance; + @Mock + private RouterAPI routerAPI; + @Mock + private ObjectProvider supplierObjectProvider; + + @BeforeAll + static void beforeAll() { + mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn("unit-test"); + ApplicationContext applicationContext = mock(ApplicationContext.class); + MetadataLocalProperties metadataLocalProperties = mock(MetadataLocalProperties.class); + StaticMetadataManager staticMetadataManager = mock(StaticMetadataManager.class); + doReturn(metadataLocalProperties).when(applicationContext).getBean(MetadataLocalProperties.class); + doReturn(staticMetadataManager).when(applicationContext).getBean(StaticMetadataManager.class); + mockedApplicationContextAwareUtils.when(ApplicationContextAwareUtils::getApplicationContext) + .thenReturn(applicationContext); + testInstance = Instance.createDefaultInstance("instance-id", LOCAL_NAMESPACE, + LOCAL_SERVICE, "host", 8090); + } + + @AfterAll + static void afterAll() { + mockedApplicationContextAwareUtils.close(); + } + + @Test + public void chooseNormalLogicTest_thenReturnAvailablePolarisInstance() { + Request request = Mockito.mock(Request.class); + List mockInstanceList = new ArrayList<>(); + mockInstanceList.add(new PolarisServiceInstance(testInstance)); + + ServiceInstanceListSupplier serviceInstanceListSupplier = Mockito.mock(ServiceInstanceListSupplier.class); + when(serviceInstanceListSupplier.get(request)).thenReturn(Flux.just(mockInstanceList)); + + when(supplierObjectProvider.getIfAvailable(any())).thenReturn(serviceInstanceListSupplier); + + ProcessLoadBalanceResponse mockLbRes = new ProcessLoadBalanceResponse(testInstance); + when(routerAPI.processLoadBalance(any())).thenReturn(mockLbRes); + + // request construct and execute invoke + PolarisRingHashLoadBalancer polarisRingHashLoadBalancer = new PolarisRingHashLoadBalancer(LOCAL_SERVICE, supplierObjectProvider, routerAPI); + Mono> responseMono = polarisRingHashLoadBalancer.choose(request); + ServiceInstance serviceInstance = responseMono.block().getServer(); + + // verify method has invoked + verify(supplierObjectProvider).getIfAvailable(any()); + + //result assert + Assertions.assertThat(serviceInstance).isNotNull(); + Assertions.assertThat(serviceInstance instanceof PolarisServiceInstance).isTrue(); + + PolarisServiceInstance polarisServiceInstance = (PolarisServiceInstance) serviceInstance; + + Assertions.assertThat(polarisServiceInstance.getPolarisInstance().getId()).isEqualTo("instance-id"); + Assertions.assertThat(polarisServiceInstance.getPolarisInstance().getNamespace()).isEqualTo(LOCAL_NAMESPACE); + Assertions.assertThat(polarisServiceInstance.getPolarisInstance().getService()).isEqualTo(LOCAL_SERVICE); + Assertions.assertThat(polarisServiceInstance.getPolarisInstance().getHost()).isEqualTo("host"); + Assertions.assertThat(polarisServiceInstance.getPolarisInstance().getPort()).isEqualTo(8090); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerAutoConfigurationTest.java similarity index 63% rename from spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerAutoConfigurationTest.java rename to spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerAutoConfigurationTest.java index dc338a54d..12b9b77ec 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerAutoConfigurationTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerAutoConfigurationTest.java @@ -20,6 +20,8 @@ package com.tencent.cloud.polaris.loadbalancer; import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.polaris.router.api.core.RouterAPI; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -39,13 +41,13 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * * @author Haotian Zhang */ -public class PolarisLoadBalancerAutoConfigurationTest { +@ExtendWith(MockitoExtension.class) +public class PolarisWeightedRandomLoadBalancerAutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") - .withPropertyValues("spring.cloud.polaris.loadbalancer.strategy=polarisWeighted") .withConfiguration(AutoConfigurations.of( PolarisRibbonTest.class, PolarisLoadBalancerAutoConfiguration.class, @@ -62,6 +64,40 @@ public class PolarisLoadBalancerAutoConfigurationTest { }); } + @Test + public void testRandomInitialization() { + this.contextRunner.withPropertyValues("spring.cloud.polaris.loadbalancer.strategy=random").run(context -> { + assertThat(context).hasSingleBean(RouterAPI.class); + assertThat(context).hasSingleBean(RestTemplate.class); + assertThatThrownBy(() -> { + context.getBean(RestTemplate.class).getForEntity("http://wrong.url", String.class); + }).isInstanceOf(Exception.class); + }); + } + + @Test + public void testPolarisWeightedInitialization() { + this.contextRunner.withPropertyValues("spring.cloud.polaris.loadbalancer.strategy=polarisWeightedRandom").run(context -> { + assertThat(context).hasSingleBean(RouterAPI.class); + assertThat(context).hasSingleBean(RestTemplate.class); + assertThatThrownBy(() -> { + context.getBean(RestTemplate.class).getForEntity("http://wrong.url", String.class); + }).isInstanceOf(Exception.class); + }); + } + + @Test + public void testPolarisRingHashInitialization() { + this.contextRunner + .withPropertyValues("spring.cloud.polaris.loadbalancer.strategy=polarisRingHash").run(context -> { + assertThat(context).hasSingleBean(RouterAPI.class); + assertThat(context).hasSingleBean(RestTemplate.class); + assertThatThrownBy(() -> { + context.getBean(RestTemplate.class).getForEntity("http://wrong.url", String.class); + }).isInstanceOf(Exception.class); + }); + } + @Configuration @EnableAutoConfiguration static class PolarisRibbonTest { diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerTest.java similarity index 92% rename from spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerTest.java rename to spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerTest.java index 2b9958001..4384081cd 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerTest.java @@ -52,12 +52,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** - * Test for {@link PolarisLoadBalancer}. + * Test for {@link PolarisWeightedRandomLoadBalancer}. * * @author rod.xu */ @ExtendWith(MockitoExtension.class) -public class PolarisLoadBalancerTest { +public class PolarisWeightedRandomLoadBalancerTest { private static MockedStatic mockedApplicationContextAwareUtils; private static Instance testInstance; @@ -97,8 +97,8 @@ public class PolarisLoadBalancerTest { when(routerAPI.processLoadBalance(any())).thenReturn(mockLbRes); // request construct and execute invoke - PolarisLoadBalancer polarisLoadBalancer = new PolarisLoadBalancer(LOCAL_SERVICE, supplierObjectProvider, routerAPI); - Mono> responseMono = polarisLoadBalancer.choose(request); + PolarisWeightedRandomLoadBalancer polarisWeightedRandomLoadBalancer = new PolarisWeightedRandomLoadBalancer(LOCAL_SERVICE, supplierObjectProvider, routerAPI); + Mono> responseMono = polarisWeightedRandomLoadBalancer.choose(request); ServiceInstance serviceInstance = responseMono.block().getServer(); // verify method has invoked