diff --git a/CHANGELOG.md b/CHANGELOG.md index ea2a6304a..22fab005e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,3 +22,4 @@ - [fix:fix SCG report wrong service bug when using IP routing.](https://github.com/Tencent/spring-cloud-tencent/pull/1063) - [fix:fix gray release examples bug.](https://github.com/Tencent/spring-cloud-tencent/pull/1066) - [fix:fix router label feign interceptor order.](https://github.com/Tencent/spring-cloud-tencent/pull/1069) +- [feat:added polaris weighted round robin load balancer.](https://github.com/Tencent/spring-cloud-tencent/pull/1062) diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/AbstractPolarisLoadBalancer.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/AbstractPolarisLoadBalancer.java new file mode 100644 index 000000000..50aa39ac3 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/AbstractPolarisLoadBalancer.java @@ -0,0 +1,101 @@ +/* + * 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.stream.Collectors; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.pojo.PolarisServiceInstance; +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.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; + +/** + * Abstract Loadbalancer of Polaris. + * + * @author veteranchen + */ +public abstract class AbstractPolarisLoadBalancer implements ReactorServiceInstanceLoadBalancer { + + private static final Logger log = LoggerFactory.getLogger(AbstractPolarisLoadBalancer.class); + + private final String serviceId; + + private final RouterAPI routerAPI; + + private ObjectProvider supplierObjectProvider; + + public AbstractPolarisLoadBalancer(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); + return supplier.get(request).next().map(serviceInstances -> { + if (serviceInstances.isEmpty()) { + log.warn("No servers available for service: " + this.serviceId); + return new EmptyResponse(); + } + + ProcessLoadBalanceRequest req = new ProcessLoadBalanceRequest(); + req.setDstInstances(convertToPolarisServiceInstances(serviceInstances)); + req = setProcessLoadBalanceRequest(req); + + try { + ProcessLoadBalanceResponse response = routerAPI.processLoadBalance(req); + return new DefaultResponse(new PolarisServiceInstance(response.getTargetInstance())); + } + catch (Exception e) { + log.warn("PolarisRoutingLoadbalancer error", e); + return new EmptyResponse(); + } + }); + } + + protected abstract ProcessLoadBalanceRequest setProcessLoadBalanceRequest(ProcessLoadBalanceRequest req); +} 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 934b95bd7..c86b08e29 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 @@ -95,6 +95,16 @@ public class PolarisLoadBalancerClientConfiguration { loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), polarisSDKContextManager.getRouterAPI()); } + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = "polarisWeightedRoundRobin") + public ReactorLoadBalancer polarisWeightedRoundRobinLoadBalancer(Environment environment, + LoadBalancerClientFactory loadBalancerClientFactory, PolarisSDKContextManager polarisSDKContextManager) { + String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); + return new PolarisWeightedRoundRobinLoadBalancer(name, + loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), polarisSDKContextManager.getRouterAPI()); + } + @Configuration(proxyBeanMethods = false) @ConditionalOnReactiveDiscoveryEnabled @Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER) 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 index 26684fea1..9b225d9bd 100644 --- 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 @@ -17,95 +17,37 @@ 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 + * @author veteranchen */ -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 class PolarisRingHashLoadBalancer extends AbstractPolarisLoadBalancer { 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); + super(serviceId, supplierObjectProvider, routerAPI); } @Override - public Mono> choose(Request request) { - ServiceInstanceListSupplier supplier = supplierObjectProvider - .getIfAvailable(NoopServiceInstanceListSupplier::new); + protected ProcessLoadBalanceRequest setProcessLoadBalanceRequest(ProcessLoadBalanceRequest req) { 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); + req.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(); - } + req.setCriteria(criteria); + return req; } - } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancer.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancer.java index 775f44457..df5222068 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancer.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancer.java @@ -17,88 +17,30 @@ package com.tencent.cloud.polaris.loadbalancer; -import java.util.List; -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; /** * Loadbalancer of Polaris. * * @author liaochuntao + * @author veteranchen */ -public class PolarisWeightedRandomLoadBalancer implements ReactorServiceInstanceLoadBalancer { - - private static final Logger log = LoggerFactory.getLogger(PolarisWeightedRandomLoadBalancer.class); - - private final String serviceId; - - private final RouterAPI routerAPI; - - private ObjectProvider supplierObjectProvider; +public class PolarisWeightedRandomLoadBalancer extends AbstractPolarisLoadBalancer { public PolarisWeightedRandomLoadBalancer(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); + super(serviceId, supplierObjectProvider, routerAPI); } @Override - public Mono> choose(Request request) { - ServiceInstanceListSupplier supplier = supplierObjectProvider - .getIfAvailable(NoopServiceInstanceListSupplier::new); - return supplier.get(request).next().map(this::getInstanceResponse); - } - - private Response getInstanceResponse(List serviceInstances) { - 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_WEIGHTED_RANDOM); - request.setCriteria(new 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(); - } + protected ProcessLoadBalanceRequest setProcessLoadBalanceRequest(ProcessLoadBalanceRequest req) { + req.setLbPolicy(LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM); + req.setCriteria(new Criteria()); + return req; } } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRoundRobinLoadBalancer.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRoundRobinLoadBalancer.java new file mode 100644 index 000000000..acdd5dc66 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRoundRobinLoadBalancer.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.loadbalancer; + +import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; +import com.tencent.polaris.api.rpc.Criteria; +import com.tencent.polaris.router.api.core.RouterAPI; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceRequest; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; + +/** + * WeightedRoundRobin Loadbalancer of Polaris. + * + * @author veteranchen + */ +public class PolarisWeightedRoundRobinLoadBalancer extends AbstractPolarisLoadBalancer { + + public PolarisWeightedRoundRobinLoadBalancer(String serviceId, ObjectProvider supplierObjectProvider, RouterAPI routerAPI) { + super(serviceId, supplierObjectProvider, routerAPI); + } + + @Override + protected ProcessLoadBalanceRequest setProcessLoadBalanceRequest(ProcessLoadBalanceRequest req) { + req.setLbPolicy(LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_ROUND_ROBIN); + req.setCriteria(new Criteria()); + return req; + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerAutoConfigurationTest.java index c45baf194..15fc6b9cb 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerAutoConfigurationTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerAutoConfigurationTest.java @@ -90,6 +90,18 @@ public class PolarisWeightedRandomLoadBalancerAutoConfigurationTest { }); } + @Test + public void testPolarisWeightedRoundRobinInitialization() { + this.contextRunner.withPropertyValues("spring.cloud.polaris.loadbalancer.strategy=polarisWeightedRoundRobin") + .run(context -> { + 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 diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerTest.java index 4384081cd..51fd71b0d 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRandomLoadBalancerTest.java @@ -23,6 +23,8 @@ import java.util.List; import com.tencent.cloud.common.pojo.PolarisServiceInstance; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; import com.tencent.polaris.api.pojo.Instance; import com.tencent.polaris.router.api.core.RouterAPI; import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse; @@ -42,6 +44,7 @@ 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.NoopServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import static com.tencent.cloud.common.metadata.MetadataContext.LOCAL_NAMESPACE; @@ -117,4 +120,44 @@ public class PolarisWeightedRandomLoadBalancerTest { Assertions.assertThat(polarisServiceInstance.getPolarisInstance().getPort()).isEqualTo(8090); } + @Test + public void chooseExceptionTest_thenReturnEmptyInstance() { + + 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); + + when(routerAPI.processLoadBalance(any())).thenThrow(new PolarisException(ErrorCode.API_TIMEOUT)); + + // request construct and execute invoke + PolarisWeightedRandomLoadBalancer polarisWeightedRandomLoadBalancer = new PolarisWeightedRandomLoadBalancer(LOCAL_SERVICE, supplierObjectProvider, routerAPI); + Mono> responseMono = polarisWeightedRandomLoadBalancer.choose(request); + ServiceInstance serviceInstance = responseMono.block().getServer(); + + // verify method has invoked + verify(supplierObjectProvider).getIfAvailable(any()); + + //result assert + Assertions.assertThat(serviceInstance).isNull(); + } + + @Test + public void chooseEmptySupplierTest_thenReturnEmptyInstance() { + ServiceInstanceListSupplier noopSupplier = new NoopServiceInstanceListSupplier(); + when(supplierObjectProvider.getIfAvailable(any())).thenReturn(noopSupplier); + + // request construct and execute invoke + PolarisWeightedRandomLoadBalancer polarisWeightedRandomLoadBalancer = new PolarisWeightedRandomLoadBalancer(LOCAL_SERVICE, supplierObjectProvider, routerAPI); + Mono> responseMono = polarisWeightedRandomLoadBalancer.choose(); + ServiceInstance serviceInstance = responseMono.block().getServer(); + + //result assert + Assertions.assertThat(serviceInstance).isNull(); + } + } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRoundRobinLoadBalancerTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRoundRobinLoadBalancerTest.java new file mode 100644 index 000000000..70d5726ea --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRoundRobinLoadBalancerTest.java @@ -0,0 +1,120 @@ +/* + * 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.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 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.verify; +import static org.mockito.Mockito.when; + +/** + * Test for {@link PolarisWeightedRandomLoadBalancer}. + * + * @author veteranchen + */ +@ExtendWith(MockitoExtension.class) +public class PolarisWeightedRoundRobinLoadBalancerTest { + + 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"); + + 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 + PolarisWeightedRoundRobinLoadBalancer polarisWeightedRoundRobinLoadBalancer = new PolarisWeightedRoundRobinLoadBalancer(LOCAL_SERVICE, supplierObjectProvider, routerAPI); + Mono> responseMono = polarisWeightedRoundRobinLoadBalancer.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); + } + +}