Merge pull request #2152 from XuDaojie/nacos-loadbalancer-2020.0.0

Nacos loadbalancer pr
pull/2301/head
余黄彬 4 years ago committed by GitHub
commit a064331b88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

3
.gitignore vendored

@ -40,3 +40,6 @@ target
# Maven ignore # Maven ignore
.flattened-pom.xml .flattened-pom.xml
# AsciiDoc
spring-cloud-alibaba-docs/**/*.html

@ -291,6 +291,27 @@ Endpoint 暴露的 json 中包含了两种属性:
} }
---- ----
=== 如何开启权重路由
==== Spring Cloud Loadbalancer
.pom.xml
[source,xml]
----
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
</dependencies>
----
.application.properties
[source,properties]
----
spring.cloud.loadbalancer.ribbon.enabled=false
spring.cloud.loadbalancer.nacos.enabled=true
----
=== 关于 Nacos Discovery Starter 更多的配置项信息 === 关于 Nacos Discovery Starter 更多的配置项信息
更多关于 Nacos Discovery Starter 的配置项如下所示: 更多关于 Nacos Discovery Starter 的配置项如下所示:
@ -310,7 +331,7 @@ Endpoint 暴露的 json 中包含了两种属性:
|日志文件名|`spring.cloud.nacos.discovery.log-name`|| |日志文件名|`spring.cloud.nacos.discovery.log-name`||
|集群|`spring.cloud.nacos.discovery.cluster-name`|`DEFAULT`|Nacos集群名称 |集群|`spring.cloud.nacos.discovery.cluster-name`|`DEFAULT`|Nacos集群名称
|接入点|`spring.cloud.nacos.discovery.endpoint`||地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址 |接入点|`spring.cloud.nacos.discovery.endpoint`||地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
|是否集成Ribbon|`ribbon.nacos.enabled`|`true`|一般都设置成true即可 |是否集成LoadBalancer|`spring.cloud.loadbalancer.nacos.enabled`|`false`|
|是否开启Nacos Watch|`spring.cloud.nacos.discovery.watch.enabled`|`true`|可以设置成false来关闭 watch |是否开启Nacos Watch|`spring.cloud.nacos.discovery.watch.enabled`|`true`|可以设置成false来关闭 watch
|=== |===

@ -294,6 +294,27 @@ The followings shows how a service instance accesses the Endpoint:
} }
---- ----
=== Weight Route
==== Spring Cloud Loadbalancer
.pom.xml
[source,xml]
----
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
</dependencies>
----
.application.properties
[source,properties]
----
spring.cloud.loadbalancer.ribbon.enabled=false
spring.cloud.loadbalancer.nacos.enabled=true
----
=== More Information about Nacos Discovery Starter Configurations === More Information about Nacos Discovery Starter Configurations
The following shows the other configurations of the starter of Nacos Discovery: The following shows the other configurations of the starter of Nacos Discovery:
@ -313,7 +334,7 @@ The following shows the other configurations of the starter of Nacos Discovery:
|Log file name|`spring.cloud.nacos.discovery.log-name`|| |Log file name|`spring.cloud.nacos.discovery.log-name`||
|Cluster Name|`spring.cloud.nacos.discovery.cluster-name`|`DEFAULT`|Cluster name of Nacos |Cluster Name|`spring.cloud.nacos.discovery.cluster-name`|`DEFAULT`|Cluster name of Nacos
|Endpoint|`spring.cloud.nacos.discovery.endpoint`||The domain name of a certain service in a specific region. You can retrieve the server address dynamically with this domain name |Endpoint|`spring.cloud.nacos.discovery.endpoint`||The domain name of a certain service in a specific region. You can retrieve the server address dynamically with this domain name
|Integrate Ribbon or not|`ribbon.nacos.enabled`|`true`|Set to true in most cases |Integrate LoadBalancer or not|`spring.cloud.loadbalancer.nacos.enabled`|`false`|
|Enable Nacos Watch|`spring.cloud.nacos.discovery.watch.enabled`|`true`|set to false to close watch |Enable Nacos Watch|`spring.cloud.nacos.discovery.watch.enabled`|`true`|set to false to close watch
|=== |===

@ -27,6 +27,7 @@ import reactor.core.publisher.Mono;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.ServiceInstance;
@ -161,12 +162,21 @@ public class ConsumerSCLBApplication {
@Autowired @Autowired
private DiscoveryClient discoveryClient; private DiscoveryClient discoveryClient;
@Value("${spring.cloud.loadbalancer.zone:null}")
private String zone;
@GetMapping("/echo-rest/{str}") @GetMapping("/echo-rest/{str}")
public String rest(@PathVariable String str) { public String rest(@PathVariable String str) {
return restTemplate.getForObject("http://service-provider/echo/" + str, return restTemplate.getForObject("http://service-provider/echo/" + str,
String.class); String.class);
} }
@GetMapping("/zone")
public String zone() {
return "consumer zone " + zone + "\n" + restTemplate
.getForObject("http://service-provider/zone", String.class);
}
@GetMapping("/echo-feign/{str}") @GetMapping("/echo-feign/{str}")
public String feign(@PathVariable String str) { public String feign(@PathVariable String str) {
return echoService.echo(str); return echoService.echo(str);

@ -7,6 +7,8 @@ spring.cloud.nacos.username=nacos
spring.cloud.nacos.password=nacos spring.cloud.nacos.password=nacos
spring.cloud.loadbalancer.ribbon.enabled=false spring.cloud.loadbalancer.ribbon.enabled=false
spring.cloud.loadbalancer.configurations=zone-preference
spring.cloud.loadbalancer.zone=hangzhou
feign.sentinel.enabled=true feign.sentinel.enabled=true

@ -16,6 +16,12 @@
package com.alibaba.cloud.examples; package com.alibaba.cloud.examples;
import java.util.Map;
import javax.annotation.Resource;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@ -40,6 +46,9 @@ public class ProviderApplication {
@RestController @RestController
class EchoController { class EchoController {
@Resource
private NacosDiscoveryProperties nacosDiscoveryProperties;
@GetMapping("/") @GetMapping("/")
public ResponseEntity index() { public ResponseEntity index() {
return new ResponseEntity("index error", HttpStatus.INTERNAL_SERVER_ERROR); return new ResponseEntity("index error", HttpStatus.INTERNAL_SERVER_ERROR);
@ -71,6 +80,12 @@ public class ProviderApplication {
return String.valueOf(a / b); return String.valueOf(a / b);
} }
@GetMapping("/zone")
public String zone() {
Map<String, String> metadata = nacosDiscoveryProperties.getMetadata();
return "provider zone " + metadata.get("zone");
}
} }
} }

@ -93,6 +93,12 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>

@ -0,0 +1,70 @@
/*
* Copyright 2013-2018 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.balancer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.client.naming.core.Balancer;
import org.springframework.cloud.client.ServiceInstance;
/**
* @author itmuch.com XuDaojie
* @since 2021.1
*/
public class NacosBalancer extends Balancer {
/**
* Choose instance by weight.
* @param instances Instance List
* @return the chosen instance
*/
public static Instance getHostByRandomWeight2(List<Instance> instances) {
return getHostByRandomWeight(instances);
}
/**
* Spring Cloud LoadBalancer Choose instance by weight.
* @param serviceInstances Instance List
* @return the chosen instance
*/
public static ServiceInstance getHostByRandomWeight3(
List<ServiceInstance> serviceInstances) {
Map<Instance, ServiceInstance> instanceMap = new HashMap<>();
List<Instance> nacosInstance = serviceInstances.stream().map(serviceInstance -> {
Map<String, String> metadata = serviceInstance.getMetadata();
// see
// com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery.hostToServiceInstance()
Instance instance = new Instance();
instance.setIp(serviceInstance.getHost());
instance.setPort(serviceInstance.getPort());
instance.setWeight(Double.parseDouble(metadata.get("nacos.weight")));
instance.setHealthy(Boolean.parseBoolean(metadata.get("nacos.healthy")));
instanceMap.put(instance, serviceInstance);
return instance;
}).collect(Collectors.toList());
Instance instance = getHostByRandomWeight2(nacosInstance);
return instanceMap.get(instance);
}
}

@ -0,0 +1,31 @@
/*
* Copyright 2013-2018 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.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.nacos.enabled", havingValue = "true")
public @interface ConditionalOnLoadBalancerNacos {
}

@ -0,0 +1,36 @@
/*
* Copyright 2013-2018 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 com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that sets up LoadBalancer for Nacos.
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnLoadBalancerNacos
@ConditionalOnNacosDiscoveryEnabled
@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerClientConfiguration.class)
public class LoadBalancerNacosAutoConfiguration {
}

@ -0,0 +1,112 @@
/*
* Copyright 2013-2018 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 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.nacos.client.naming.utils.CollectionUtils;
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;
/**
* see original.
* {@link org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer}
*
* @author XuDaojie
* @since 2021.1
*/
public class NacosLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private static final Logger log = LoggerFactory.getLogger(NacosLoadBalancer.class);
private final String serviceId;
private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
private final NacosDiscoveryProperties nacosDiscoveryProperties;
public NacosLoadBalancer(
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId, NacosDiscoveryProperties nacosDiscoveryProperties) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get().next().map(this::getInstanceResponse);
}
private Response<ServiceInstance> getInstanceResponse(
List<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
log.warn("No servers available for service: " + this.serviceId);
return new EmptyResponse();
}
try {
String clusterName = this.nacosDiscoveryProperties.getClusterName();
List<ServiceInstance> instancesToChoose = serviceInstances;
if (StringUtils.isNotBlank(clusterName)) {
List<ServiceInstance> sameClusterInstances = serviceInstances.stream()
.filter(serviceInstance -> {
String cluster = serviceInstance.getMetadata()
.get("nacos.cluster");
return StringUtils.equals(cluster, clusterName);
}).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(sameClusterInstances)) {
instancesToChoose = sameClusterInstances;
}
}
else {
log.warn(
"A cross-cluster call occursname = {}, clusterName = {}, instance = {}",
serviceId, clusterName, serviceInstances);
}
ServiceInstance instance = NacosBalancer
.getHostByRandomWeight3(instancesToChoose);
return new DefaultResponse(instance);
}
catch (Exception e) {
log.warn("NacosLoadBalancer error", e);
return null;
}
}
}

@ -0,0 +1,121 @@
/*
* Copyright 2013-2018 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 com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
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.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
/**
* {@link ServiceInstanceListSupplier} don't use cache.<br>
* <br>
* 1. LoadBalancerCache causes information such as the weight of the service instance to
* be changed without immediate effect.<br>
* 2. Nacos itself supports caching.
*
* @author XuDaojie
* @since 2021.1
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class NacosLoadBalancerClientConfiguration {
private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 183827465;
@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> nacosLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory,
NacosDiscoveryProperties nacosDiscoveryProperties) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new NacosLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name,
ServiceInstanceListSupplier.class),
name, nacosDiscoveryProperties);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnReactiveDiscoveryEnabled
@Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER)
public static class ReactiveSupportConfiguration {
@Bean
@ConditionalOnBean(ReactiveDiscoveryClient.class)
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default", matchIfMissing = true)
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withDiscoveryClient()
.build(context);
}
@Bean
@ConditionalOnBean(ReactiveDiscoveryClient.class)
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "zone-preference")
public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withDiscoveryClient()
.withZonePreference().build(context);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnBlockingDiscoveryEnabled
@Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER + 1)
public static class BlockingSupportConfiguration {
@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default", matchIfMissing = true)
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient()
.build(context);
}
@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "zone-preference")
public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient()
.withZonePreference().build(context);
}
}
}

@ -68,5 +68,11 @@
"name": "spring.cloud.nacos.password", "name": "spring.cloud.nacos.password",
"type": "java.lang.String", "type": "java.lang.String",
"description": "nacos password to authenticate." "description": "nacos password to authenticate."
},
{
"name": "spring.cloud.loadbalancer.nacos.enabled",
"type": "java.lang.Boolean",
"defaultValue": false,
"description": "Integrate LoadBalancer or not."
} }
]} ]}

@ -5,6 +5,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\ com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\ com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration,\ com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration,\
com.alibaba.cloud.nacos.loadbalancer.LoadBalancerNacosAutoConfiguration,\
com.alibaba.cloud.nacos.NacosServiceAutoConfiguration com.alibaba.cloud.nacos.NacosServiceAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\ org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration

Loading…
Cancel
Save