diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/DefaultLoadBalancerAlgorithm.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/DefaultLoadBalancerAlgorithm.java
new file mode 100644
index 0000000000..43d4c1cc71
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/DefaultLoadBalancerAlgorithm.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.alibaba.cloud.nacos.loadbalancer;
+
+import java.util.List;
+
+import com.alibaba.cloud.nacos.balancer.NacosBalancer;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.Request;
+import org.springframework.core.Ordered;
+
+/**
+ * This is a default implementation of load balancing algorithm.
+ * use {@link com.alibaba.cloud.nacos.balancer.NacosBalancer}
+ *
+ * @author zhangbinhub
+ */
+public class DefaultLoadBalancerAlgorithm implements LoadBalancerAlgorithm {
+ @Override
+ public String getServiceId() {
+ return LoadBalancerAlgorithm.DEFAULT_SERVICE_ID;
+ }
+
+ @Override
+ public ServiceInstance getInstance(Request> request, List serviceInstances) {
+ return NacosBalancer.getHostByRandomWeight3(serviceInstances);
+ }
+
+ @Override
+ public int getOrder() {
+ return Ordered.LOWEST_PRECEDENCE;
+ }
+}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/LoadBalancerAlgorithm.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/LoadBalancerAlgorithm.java
new file mode 100644
index 0000000000..759ccc093d
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/LoadBalancerAlgorithm.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.alibaba.cloud.nacos.loadbalancer;
+
+import java.util.List;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.Request;
+import org.springframework.core.Ordered;
+
+/**
+ * Load Balancer algorithm interface.
+ * When expanding the load balancing algorithm, implement this interface and register it as a bean.
+ *
+ * @author zhangbinhub
+ */
+public interface LoadBalancerAlgorithm extends Ordered {
+ /**
+ * default service id.
+ */
+ String DEFAULT_SERVICE_ID = "defaultServiceId";
+
+ String getServiceId();
+
+ ServiceInstance getInstance(Request> request, List serviceInstances);
+}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/LoadBalancerNacosAutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/LoadBalancerNacosAutoConfiguration.java
index e600e33c11..a185511999 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/LoadBalancerNacosAutoConfiguration.java
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/LoadBalancerNacosAutoConfiguration.java
@@ -20,6 +20,7 @@
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
@@ -32,5 +33,8 @@
@ConditionalOnNacosDiscoveryEnabled
@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerClientConfiguration.class)
public class LoadBalancerNacosAutoConfiguration {
-
+ @Bean
+ public LoadBalancerAlgorithm defaultLoadBalancerAlgorithm() {
+ return new DefaultLoadBalancerAlgorithm();
+ }
}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/NacosLoadBalancer.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/NacosLoadBalancer.java
index 733a62dc5c..6856072472 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/NacosLoadBalancer.java
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/NacosLoadBalancer.java
@@ -18,12 +18,12 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
-import com.alibaba.cloud.nacos.balancer.NacosBalancer;
import com.alibaba.cloud.nacos.util.InetIPv6Utils;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import jakarta.annotation.PostConstruct;
@@ -32,7 +32,6 @@
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
@@ -55,7 +54,7 @@ public class NacosLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final String serviceId;
- private ObjectProvider serviceInstanceListSupplierProvider;
+ private final ObjectProvider serviceInstanceListSupplierProvider;
private final NacosDiscoveryProperties nacosDiscoveryProperties;
@@ -67,9 +66,11 @@ public class NacosLoadBalancer implements ReactorServiceInstanceLoadBalancer {
*/
public static String ipv6;
- @Autowired
- private InetIPv6Utils inetIPv6Utils;
+ private final InetIPv6Utils inetIPv6Utils;
+ private final List serviceInstanceFilters;
+
+ private final Map loadBalancerAlgorithmMap;
@PostConstruct
public void init() {
@@ -96,7 +97,7 @@ private List filterInstanceByIpType(List insta
}
}
// Provider has no IPv6, should use IPv4.
- if (ipv6InstanceList.size() == 0) {
+ if (ipv6InstanceList.isEmpty()) {
return instances.stream()
.filter(instance -> Pattern.matches(IPV4_REGEX, instance.getHost()))
.collect(Collectors.toList());
@@ -112,23 +113,28 @@ private List filterInstanceByIpType(List insta
public NacosLoadBalancer(
ObjectProvider serviceInstanceListSupplierProvider,
- String serviceId, NacosDiscoveryProperties nacosDiscoveryProperties) {
+ String serviceId, NacosDiscoveryProperties nacosDiscoveryProperties, InetIPv6Utils inetIPv6Utils,
+ List serviceInstanceFilters,
+ Map loadBalancerAlgorithmMap) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
+ this.inetIPv6Utils = inetIPv6Utils;
+ this.serviceInstanceFilters = serviceInstanceFilters;
+ this.loadBalancerAlgorithmMap = loadBalancerAlgorithmMap;
}
@Override
public Mono> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
- return supplier.get(request).next().map(this::getInstanceResponse);
+ return supplier.get(request).next().map(serviceInstances -> getInstanceResponse(request, serviceInstances));
}
- private Response getInstanceResponse(
+ private Response getInstanceResponse(Request> request,
List serviceInstances) {
if (serviceInstances.isEmpty()) {
- log.warn("No servers available for service: " + this.serviceId);
+ log.warn("No servers available for service: {}", this.serviceId);
return new EmptyResponse();
}
@@ -154,8 +160,20 @@ private Response getInstanceResponse(
}
instancesToChoose = this.filterInstanceByIpType(instancesToChoose);
- ServiceInstance instance = NacosBalancer
- .getHostByRandomWeight3(instancesToChoose);
+ // Filter the service list sequentially based on the order number
+ for (ServiceInstanceFilter filter : serviceInstanceFilters) {
+ instancesToChoose = filter.filterInstance(request, instancesToChoose);
+ }
+
+ ServiceInstance instance;
+ // Find the corresponding load balancing algorithm through the service ID and select the final service instance
+ if (loadBalancerAlgorithmMap.containsKey(serviceId)) {
+ instance = loadBalancerAlgorithmMap.get(serviceId).getInstance(request, instancesToChoose);
+ }
+ else {
+ instance = loadBalancerAlgorithmMap.get(LoadBalancerAlgorithm.DEFAULT_SERVICE_ID)
+ .getInstance(request, instancesToChoose);
+ }
return new DefaultResponse(instance);
}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/NacosLoadBalancerClientConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/NacosLoadBalancerClientConfiguration.java
index 494e7e96ad..c3942cda0b 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/NacosLoadBalancerClientConfiguration.java
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/NacosLoadBalancerClientConfiguration.java
@@ -16,7 +16,12 @@
package com.alibaba.cloud.nacos.loadbalancer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
+import com.alibaba.cloud.nacos.util.InetIPv6Utils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -47,6 +52,7 @@
* @since 2021.1
*/
@Configuration(proxyBeanMethods = false)
+@ConditionalOnLoadBalancerNacos
@ConditionalOnDiscoveryEnabled
public class NacosLoadBalancerClientConfiguration {
@@ -56,12 +62,22 @@ public class NacosLoadBalancerClientConfiguration {
@ConditionalOnMissingBean
public ReactorLoadBalancer nacosLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory,
- NacosDiscoveryProperties nacosDiscoveryProperties) {
+ NacosDiscoveryProperties nacosDiscoveryProperties,
+ InetIPv6Utils inetIPv6Utils,
+ List serviceInstanceFilters,
+ List loadBalancerAlgorithms) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
+ Map loadBalancerAlgorithmMap = new HashMap<>();
+ loadBalancerAlgorithms.forEach(loadBalancerAlgorithm -> {
+ if (!loadBalancerAlgorithmMap.containsKey(loadBalancerAlgorithm.getServiceId())) {
+ loadBalancerAlgorithmMap.put(loadBalancerAlgorithm.getServiceId(), loadBalancerAlgorithm);
+ }
+ });
return new NacosLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name,
ServiceInstanceListSupplier.class),
- name, nacosDiscoveryProperties);
+ name, nacosDiscoveryProperties, inetIPv6Utils,
+ serviceInstanceFilters, loadBalancerAlgorithmMap);
}
@Configuration(proxyBeanMethods = false)
@@ -115,7 +131,5 @@ public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceL
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient()
.withZonePreference().build(context);
}
-
}
-
}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/ServiceInstanceFilter.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/ServiceInstanceFilter.java
new file mode 100644
index 0000000000..4f3dcc3fd9
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/loadbalancer/ServiceInstanceFilter.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.alibaba.cloud.nacos.loadbalancer;
+
+import java.util.List;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.Request;
+import org.springframework.core.Ordered;
+
+/**
+ * Service Instance Filter interface.
+ * When custom service instance list filter, implement this interface and register it as a bean.
+ *
+ * @author zhangbinhub
+ */
+public interface ServiceInstanceFilter extends Ordered {
+ List filterInstance(Request> request, List serviceInstances);
+}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryLoadBalancerConfigurationTest.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryLoadBalancerConfigurationTest.java
new file mode 100644
index 0000000000..8c5cb9f56e
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryLoadBalancerConfigurationTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.alibaba.cloud.nacos.discovery;
+
+import com.alibaba.cloud.nacos.NacosServiceAutoConfiguration;
+import com.alibaba.cloud.nacos.loadbalancer.LoadBalancerAlgorithm;
+import com.alibaba.cloud.nacos.loadbalancer.LoadBalancerNacosAutoConfiguration;
+import com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancerClientConfiguration;
+import com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration;
+import com.alibaba.cloud.nacos.util.UtilIPv6AutoConfiguration;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
+import org.springframework.cloud.commons.util.UtilAutoConfiguration;
+import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author zhangbinhub
+ **/
+public class NacosDiscoveryLoadBalancerConfigurationTest {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(
+ AutoServiceRegistrationConfiguration.class,
+ NacosServiceRegistryAutoConfiguration.class,
+ UtilAutoConfiguration.class,
+ UtilIPv6AutoConfiguration.class,
+ NacosServiceAutoConfiguration.class,
+ NacosDiscoveryAutoConfiguration.class,
+ NacosDiscoveryClientConfiguration.class,
+ LoadBalancerAutoConfiguration.class, this.getClass()));
+
+ @Test
+ public void testNacosLoadBalancerEnabled() {
+ contextRunner.withPropertyValues("spring.cloud.loadbalancer.nacos.enabled=true")
+ .withConfiguration(AutoConfigurations.of(
+ LoadBalancerNacosAutoConfiguration.class,
+ NacosLoadBalancerClientConfiguration.class))
+ .run(context -> {
+ assertThat(context).hasSingleBean(LoadBalancerAlgorithm.class);
+ assertThat(context).hasBean("nacosLoadBalancer");
+ });
+ }
+
+ @Test
+ public void testNacosLoadBalancerDisabled() {
+ contextRunner.withPropertyValues("spring.cloud.loadbalancer.nacos.enabled=false")
+ .withConfiguration(AutoConfigurations.of(
+ LoadBalancerNacosAutoConfiguration.class,
+ NacosLoadBalancerClientConfiguration.class))
+ .run(context -> {
+ assertThat(context).doesNotHaveBean(LoadBalancerAlgorithm.class);
+ assertThat(context).doesNotHaveBean("nacosLoadBalancer");
+ });
+ }
+
+}