diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc index e461c81739..5bf408792e 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc @@ -312,6 +312,27 @@ spring.cloud.loadbalancer.ribbon.enabled=false spring.cloud.loadbalancer.nacos.enabled=true ---- +=== IPv4至IPv6地址迁移方案 + +==== IPv4和IPv6地址双注册 +在配置完成以上Spring Cloud LoadBalancer作为负载均衡策略后,应用启动后会默认将微服务的IPv4地址和IPv6地址注册到注册中心中,其中IPv4地址会存放在Nacos服务列表中的IP字段下,IPv6地址在Nacos的metadata字段中,其对应的Key为IPv6。当服务消费者调用服务提供者时,会根据自身的IP地址栈支持情况,选择合适的IP地址类型发起服务调用。具体规则: +(1)服务消费者本身支持IPv4和IPv6双地址栈或仅支持IPv6地址栈的情况下,服务消费者会使用服务提供的IPv6地址发起服务调用,IPv6地址调用失败如本身还同事支持IPv4地址栈时,暂不支持切换到IPv4再发起重试调用; +(2)服务消费者本身仅支持IPv4单地址栈的情况下,服务消费者会使用服务提供的IPv4地址发起服务调用。 + +==== 仅注册IPv4 +如果您只想使用IPv4地址进行注册,可以在application.properties使用以下配置: +[source,properties] +---- +spring.cloud.nacos.discovery.ip-type=IPv4 +---- + +==== 仅注册IPv6 +如果您只想使用IPv6地址,可以在application.properties使用以下配置: +[source,properties] +---- +spring.cloud.nacos.discovery.ip-type=IPv6 +---- + === 关于 Nacos Discovery Starter 更多的配置项信息 更多关于 Nacos Discovery Starter 的配置项如下所示: diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc index d49be13f55..780cb52c89 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc @@ -296,7 +296,7 @@ The followings shows how a service instance accesses the Endpoint: === Weight Route -==== Spring Cloud Loadbalancer +==== Spring Cloud LoadBalancer .pom.xml [source,xml] ---- @@ -315,6 +315,28 @@ spring.cloud.loadbalancer.ribbon.enabled=false spring.cloud.loadbalancer.nacos.enabled=true ---- +=== IPv4 to IPv6 address migration scheme + +==== Both register IPv4 and IPv6 address +After configuring above Spring Cloud LoadBalancer as the load balancing policy, the IPv4 address and IPv6 address of the microservice will be registered with the registry by default after the application is started, where the IPv4 address will be stored in the IP field of the Nacos service list, the IPv6 address will be in the metadata field of Nacos, and its corresponding Key will be IPv6. When a service consumer calls a service provider, it selects the appropriate IP address type to initiate a service call based on its IP address stack support. Specific rules: +(1) If the service consumer itself supports IPv4 and IPv6 dual address stacks or only supports IPv6 address stacks, the service consumer will use the IPv6 address provided by the service to initiate a service call, and if the IPv6 address call fails, if it also supports the IPv4 address stack, it is temporarily not supported to switch to IPv4 and then initiate a retry call; +(2) If the service consumer itself only supports IPv4 single-address stack, the service consumer will use the IPv4 address provided by the service to initiate service calls. + +==== Only Register IPv4 address +If you only want to register IPv4 address.Config in application.properties as follows: +[source,properties] +---- +spring.cloud.nacos.discovery.ip-type=IPv4 +---- + +==== Only Register IPv6 address +If you only want to register IPv6 address.Config in application.properties as follows: +[source,properties] +---- +spring.cloud.nacos.discovery.ip-type=IPv6 +---- + + === More Information about Nacos Discovery Starter Configurations The following shows the other configurations of the starter of Nacos Discovery: diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties index c1071da863..7e2151344d 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/application.properties @@ -19,3 +19,4 @@ spring.cloud.sentinel.datasource.ds1.file.rule-type=flow spring.cloud.sentinel.datasource.ds2.file.file=classpath: degraderule.json spring.cloud.sentinel.datasource.ds2.file.data-type=json spring.cloud.sentinel.datasource.ds2.file.rule-type=degrade +spring.cloud.loadbalancer.nacos.enabled=true diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/resources/application.properties index 4e4862325b..af358409dc 100755 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/resources/application.properties +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/src/main/resources/application.properties @@ -1,9 +1,11 @@ -server.port=18082 +server.port=0 spring.application.name=service-provider spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 spring.cloud.nacos.discovery.enabled=true #spring.cloud.nacos.discovery.instance-enabled=true -#register IPv6 +#only register IPv4 instance +#spring.cloud.nacos.discovery.ip-type=IPv4 +#only register IPv6 instance #spring.cloud.nacos.discovery.ip-type=IPv6 spring.cloud.nacos.username=nacos diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md index 913e002a5e..756360b078 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md @@ -78,11 +78,39 @@ ### 服务发现 -#### 集成 Ribbon -为了便于使用,NacosServerList 实现了 com.netflix.loadbalancer.ServerList 接口,并在 @ConditionOnMissingBean 的条件下进行自动注入。如果您有定制化的需求,可以自己实现自己的 ServerList。 - -Nacos Discovery Starter 默认集成了 Ribbon ,所以对于使用了 Ribbon 做负载均衡的组件,可以直接使用 Nacos 的服务发现。 - +#### 集成 Spring Cloud Loadbalancer +```xml + + + org.springframework.cloud + spring-cloud-loadbalancer + + +``` +增加如下配置,使用 Spring Cloud Alibaba 社区针对 Spring Cloud Loadbalancer 负载均衡依赖提供的负载均衡策略,以便使用 Spring Cloud Alibaba 提供的所有的能力: +```properties +spring.cloud.loadbalancer.ribbon.enabled=false +spring.cloud.loadbalancer.nacos.enabled=true +``` + +#### IPv4至IPv6地址迁移方案 + +##### IPv4和IPv6地址双注册 +在配置完成Spring Cloud Loadbalancer作为负载均衡策略后,应用启动后会默认将微服务的IPv4地址和IPv6地址注册到注册中心中,其中IPv4地址会存放在Nacos服务列表中的IP字段下,IPv6地址在Nacos的metadata字段中,其对应的Key为IPv6。当服务消费者调用服务提供者时,会根据自身的IP地址栈支持情况,选择合适的IP地址类型发起服务调用。具体规则: +(1)服务消费者本身支持IPv4和IPv6双地址栈或仅支持IPv6地址栈的情况下,服务消费者会使用服务提供的IPv6地址发起服务调用,IPv6地址调用失败如本身还同事支持IPv4地址栈时,暂不支持切换到IPv4再发起重试调用; +(2)服务消费者本身仅支持IPv4单地址栈的情况下,服务消费者会使用服务提供的IPv4地址发起服务调用。 + +##### 仅注册IPv4 +如果您只想使用IPv4地址进行注册,可以在application.properties使用以下配置: +``` +spring.cloud.nacos.discovery.ip-type=IPv4 +``` + +##### 仅注册IPv6 +如果您只想使用IPv6地址,可以在application.properties使用以下配置: +``` +spring.cloud.nacos.discovery.ip-type=IPv6 +``` #### 使用 RestTemplate 和 FeignClient @@ -158,11 +186,6 @@ Spring Cloud Nacos Discovery 遵循了 spring cloud common 标准,实现了 Au -### 服务发现 -NacosServerList 实现了 com.netflix.loadbalancer.ServerList 接口,并在 @ConditionOnMissingBean 的条件下进行自动注入,默认集成了Ribbon。 - -如果需要有更加自定义的可以使用 @Autowired 注入一个 NacosRegistration 实例,通过其持有的 NamingService 字段内容直接调用 Nacos API。 - ## Endpoint 信息查看 @@ -191,7 +214,7 @@ Spring Boot 2.x 可以通过访问 http://127.0.0.1:18083/actuator/nacos-discove 权重|spring.cloud.nacos.discovery.weight|1|取值范围 1 到 100,数值越大,权重越大 网卡名|spring.cloud.nacos.discovery.network-interface||当IP未配置时,注册的IP为此网卡所对应的IP地址,如果此项也未配置,则默认取第一块网卡的地址 注册的IP地址|spring.cloud.nacos.discovery.ip||优先级最高 -注册的IP地址类型|spring.cloud.nacos.discovery.ip-type|IPv4|可以配置IPv4和IPv6两种类型,如果网卡同类型IP地址存在多个,希望制定特定网段地址,可使用`spring.cloud.inetutils.preferred-networks`配置筛选地址 +注册的IP地址类型|spring.cloud.nacos.discovery.ip-type|双栈地址|可以配置IPv4和IPv6两种类型,如果网卡同类型IP地址存在多个,希望制定特定网段地址,可使用`spring.cloud.inetutils.preferred-networks`配置筛选地址 注册的端口|spring.cloud.nacos.discovery.port|-1|默认情况下不用配置,会自动探测 命名空间|spring.cloud.nacos.discovery.namespace||常用场景之一是不同环境的注册的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。 AccessKey|spring.cloud.nacos.discovery.access-key|| diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md index 04af4f5150..450d8cff4f 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md @@ -80,11 +80,41 @@ Enter `http://127.0.0.1:8848/nacos/#/serviceDetail?name=service-provider&groupNa ### Service Discovery -#### Integration Ribbon - -For ease of use, NacosServerList implements the com.netflix.loadbalancer.ServerList interface and auto-injects under the @ConditionOnMissingBean condition. If you have customized requirements, you can implement your own ServerList yourself. - -Nacos Discovery Starter integrates Ribbon by default, so for components that use Ribbon for load balancing, you can use Nacos Service discovery directly. +#### Integration Spring Cloud Loadbalancer + +```xml + + + org.springframework.cloud + spring-cloud-loadbalancer + + +``` +Add the following configuration to use the load balancing strategy provided by the Spring Cloud Alibaba community for Spring Cloud Loadbalancer load balancing dependencies, so as to use all the capabilities provided by Spring Cloud Alibaba: + +```properties +spring.cloud.loadbalancer.ribbon.enabled=false +spring.cloud.loadbalancer.nacos.enabled=true +``` + +#### IPv4 to IPv6 address migration scheme + +##### Both register IPv4 and IPv6 address +After configuring above Spring Cloud Loadbalancer as the load balancing policy, the IPv4 address and IPv6 address of the microservice will be registered with the registry by default after the application is started, where the IPv4 address will be stored in the IP field of the Nacos service list, the IPv6 address will be in the metadata field of Nacos, and its corresponding Key will be IPv6. When a service consumer calls a service provider, it selects the appropriate IP address type to initiate a service call based on its IP address stack support. Specific rules: +(1) If the service consumer itself supports IPv4 and IPv6 dual address stacks or only supports IPv6 address stacks, the service consumer will use the IPv6 address provided by the service to initiate a service call, and if the IPv6 address call fails, if it also supports the IPv4 address stack, it is temporarily not supported to switch to IPv4 and then initiate a retry call; +(2) If the service consumer itself only supports IPv4 single-address stack, the service consumer will use the IPv4 address provided by the service to initiate service calls. + +##### Only Register IPv4 address +If you only want to register IPv4 address.Config in application.properties as follows: +``` +spring.cloud.nacos.discovery.ip-type=IPv4 +``` + +##### Only Register IPv6 address +If you only want to register IPv6 address.Config in application.properties as follows: +``` +spring.cloud.nacos.discovery.ip-type=IPv6 +``` #### Use RestTemplate and FeignClient @@ -161,14 +191,6 @@ Spring Cloud Nacos Discovery follows the spring cloud common standard and implem During the startup phase of the spring cloud application, the WebServerInitializedEvent event is watched. When the WebServerInitializedEvent event is received after the Web container is initialized, the registration action is triggered, and the ServiceRegistry register method is called to register the service to the Nacos Server. - -### Service Discovery - -NacosServerList implements the com.netflix.loadbalancer.ServerList interface and auto-injects it under @ConditionOnMissingBean. The ribbon is integrated by default. - -If you need to be more customizable, you can use @Autowired to inject a NacosRegistration bean and call the Nacos API directly through the contents of the NamingService field it holds. - - ## Endpoint Nacos Discovery Starter also supports the implementation of Spring Boot actuator endpoints. @@ -198,7 +220,7 @@ server address|spring.cloud.nacos.discovery.server-addr|| service|spring.cloud.nacos.discovery.service|${spring.application.name}|service id to registry weight|spring.cloud.nacos.discovery.weight|1|value from 1 to 100, The larger the value, the larger the weight ip|spring.cloud.nacos.discovery.ip||ip address to registry, Highest priority -ip type|spring.cloud.nacos.discovery.ip-type|IPv4|IPv4 and IPv6 can be configured, If there are multiple IP addresses of the same type of network card, and you want to specify a specific network segment address, you can use `spring.cloud.inetutils.preferred-networks` to configure the filter address. +ip type|spring.cloud.nacos.discovery.ip-type|Dual Stack Address|IPv4 and IPv6 can be configured, If there are multiple IP addresses of the same type of network card, and you want to specify a specific network segment address, you can use `spring.cloud.inetutils.preferred-networks` to configure the filter address. network interface|spring.cloud.nacos.discovery.network-interface||When the IP is not configured, the registered IP address is the IP address corresponding to the network-interface. If this item is not configured, the address of the first network-interface is taken by default. port|spring.cloud.nacos.discovery.port|-1|port to registry, Automatically detect without configuration namesapce|spring.cloud.nacos.discovery.namespace||One of the common scenarios is the separation of the configuration of different environments, such as the development of the test environment and the resource isolation of the production environment. diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java index b12ec178c9..a70e9fa4f2 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java @@ -32,7 +32,7 @@ import com.alibaba.cloud.commons.lang.StringUtils; import com.alibaba.cloud.nacos.event.NacosDiscoveryInfoChangedEvent; -import com.alibaba.cloud.nacos.utils.InetIPv6Util; +import com.alibaba.cloud.nacos.util.InetIPv6Utils; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.PreservedMetadataKeys; import com.alibaba.nacos.client.naming.utils.UtilAndComs; @@ -80,6 +80,10 @@ public class NacosDiscoveryProperties { private static final Pattern PATTERN = Pattern.compile("-(\\w)"); + private static final String IPV4 = "IPv4"; + + private static final String IPV6 = "IPv6"; + /** * nacos discovery server address. */ @@ -169,7 +173,7 @@ public class NacosDiscoveryProperties { * When IPv6 is chosen but no IPv6 can be found, system will automatically find IPv4 to ensure there is an * available service address. */ - private String ipType = "IPv4"; + private String ipType; /** * The port your want to register for your service instance, needn't to set it if the @@ -230,7 +234,7 @@ public class NacosDiscoveryProperties { private boolean failFast = true; @Autowired - private InetIPv6Util inetIPv6Util; + private InetIPv6Utils inetIPv6Utils; @Autowired private InetUtils inetUtils; @@ -261,13 +265,21 @@ public void init() throws Exception { logName = Objects.toString(logName, ""); if (StringUtils.isEmpty(ip)) { - // traversing network interfaces if didn't specify a interface + // traversing network interfaces if didn't specify an interface if (StringUtils.isEmpty(networkInterface)) { - if ("IPv4".equalsIgnoreCase(ipType)) { + if (ipType == null) { + ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); + String ipv6Addr = inetIPv6Utils.findIPv6Address(); + metadata.put(IPV6, ipv6Addr); + if (ipv6Addr != null) { + metadata.put(IPV6, ipv6Addr); + } + } + else if (IPV4.equalsIgnoreCase(ipType)) { ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); } - else if ("IPv6".equalsIgnoreCase(ipType)) { - ip = inetIPv6Util.findIPv6Address(); + else if (IPV6.equalsIgnoreCase(ipType)) { + ip = inetIPv6Utils.findIPv6Address(); if (StringUtils.isEmpty(ip)) { log.warn("There is no available IPv6 found. Spring Cloud Alibaba will automatically find IPv4."); ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/balancer/NacosBalancer.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/balancer/NacosBalancer.java index 02eb0a4733..836cffe498 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/balancer/NacosBalancer.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/balancer/NacosBalancer.java @@ -19,8 +19,13 @@ import java.util.HashMap; 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.NacosServiceInstance; +import com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancer; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.client.naming.core.Balancer; @@ -32,6 +37,10 @@ */ public class NacosBalancer extends Balancer { + private static final String IPV4_REGEX = "((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}"; + + private static final String IPV6_KEY = "IPv6"; + /** * Choose instance by weight. * @param instances Instance List @@ -64,7 +73,25 @@ public static ServiceInstance getHostByRandomWeight3( }).collect(Collectors.toList()); Instance instance = getHostByRandomWeight2(nacosInstance); - return instanceMap.get(instance); + NacosServiceInstance nacosServiceInstance = (NacosServiceInstance) instanceMap.get(instance); + // When local support IPv6 address stack, referred to use IPv6 address. + if (StringUtils.isNotEmpty(NacosLoadBalancer.ipv6)) { + convertIPv4ToIPv6(nacosServiceInstance); + } + return nacosServiceInstance; + } + + /** + * There is two type Ip,using IPv6 should use IPv6 in metadata to replace IPv4 in IP + * field. + */ + private static void convertIPv4ToIPv6(NacosServiceInstance instance) { + if (Pattern.matches(IPV4_REGEX, instance.getHost())) { + String ip = instance.getMetadata().get(IPV6_KEY); + if (StringUtils.isNotEmpty(ip)) { + instance.setHost(ip); + } + } } } 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 198837af9d..33aa35cbd8 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 @@ -16,18 +16,24 @@ package com.alibaba.cloud.nacos.loadbalancer; +import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.annotation.PostConstruct; + 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; @@ -54,6 +60,57 @@ public class NacosLoadBalancer implements ReactorServiceInstanceLoadBalancer { private final NacosDiscoveryProperties nacosDiscoveryProperties; + private static final String IPV4_REGEX = "((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}"; + + private static final String IPV6_KEY = "IPv6"; + /** + * Storage local valid IPv6 address, it's a flag whether local machine support IPv6 address stack. + */ + public static String ipv6; + + @Autowired + private InetIPv6Utils inetIPv6Utils; + + + @PostConstruct + public void init() { + String ip = nacosDiscoveryProperties.getIp(); + if (StringUtils.isNotEmpty(ip)) { + ipv6 = Pattern.matches(IPV4_REGEX, ip) ? nacosDiscoveryProperties.getMetadata().get(IPV6_KEY) : ip; + } + else { + ipv6 = inetIPv6Utils.findIPv6Address(); + } + } + + private List filterInstanceByIpType(List instances) { + if (StringUtils.isNotEmpty(ipv6)) { + List ipv6InstanceList = new ArrayList<>(); + for (ServiceInstance instance : instances) { + if (Pattern.matches(IPV4_REGEX, instance.getHost())) { + if (StringUtils.isNotEmpty(instance.getMetadata().get(IPV6_KEY))) { + ipv6InstanceList.add(instance); + } + } + else { + ipv6InstanceList.add(instance); + } + } + // Provider has no IPv6, should use IPv4. + if (ipv6InstanceList.size() == 0) { + return instances.stream() + .filter(instance -> Pattern.matches(IPV4_REGEX, instance.getHost())) + .collect(Collectors.toList()); + } + else { + return ipv6InstanceList; + } + } + return instances.stream() + .filter(instance -> Pattern.matches(IPV4_REGEX, instance.getHost())) + .collect(Collectors.toList()); + } + public NacosLoadBalancer( ObjectProvider serviceInstanceListSupplierProvider, String serviceId, NacosDiscoveryProperties nacosDiscoveryProperties) { @@ -96,6 +153,7 @@ private Response getInstanceResponse( "A cross-cluster call occurs,name = {}, clusterName = {}, instance = {}", serviceId, clusterName, serviceInstances); } + instancesToChoose = this.filterInstanceByIpType(instancesToChoose); ServiceInstance instance = NacosBalancer .getHostByRandomWeight3(instancesToChoose); @@ -106,7 +164,6 @@ private Response getInstanceResponse( log.warn("NacosLoadBalancer error", e); return null; } - } } diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/utils/InetIPv6Util.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/util/InetIPv6Utils.java similarity index 53% rename from spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/utils/InetIPv6Util.java rename to spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/util/InetIPv6Utils.java index 0e37f06815..165beda669 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/utils/InetIPv6Util.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/util/InetIPv6Utils.java @@ -14,20 +14,14 @@ * limitations under the License. */ -package com.alibaba.cloud.nacos.utils; +package com.alibaba.cloud.nacos.util; -import java.io.Closeable; import java.io.IOException; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; -import java.net.UnknownHostException; import java.util.Enumeration; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; import com.alibaba.cloud.commons.lang.StringUtils; import org.apache.commons.logging.Log; @@ -39,64 +33,55 @@ /** * @author HH */ -public class InetIPv6Util implements Closeable { +public class InetIPv6Utils { - private final ExecutorService executorService; - - private final Log log = LogFactory.getLog(InetIPv6Util.class); + private final static Log log = LogFactory.getLog(InetIPv6Utils.class); private final InetUtilsProperties properties; - @Override - public void close() { - this.executorService.shutdown(); - } - public InetIPv6Util(final InetUtilsProperties properties) { + public InetIPv6Utils(final InetUtilsProperties properties) { this.properties = properties; - this.executorService = Executors.newSingleThreadExecutor((r) -> { - Thread thread = new Thread(r); - thread.setName("spring.cloud.alibaba.inetIPV6Util"); - thread.setDaemon(true); - return thread; - }); } - public InetUtils.HostInfo findFirstNonLoopbackHostInfo() { - InetAddress address = this.findFirstNonLoopbackIPv6Address(); - if (address != null) { - return this.convertAddress(address); - } - return null; + private InetUtils.HostInfo findFirstValidHostInfo() { + InetAddress address = this.findFirstValidIPv6Address(); + return address != null ? this.getHostInfo(address) : null; } - public InetAddress findFirstNonLoopbackIPv6Address() { + private InetAddress findFirstValidIPv6Address() { InetAddress address = null; try { - int lowest = Integer.MAX_VALUE; for (Enumeration nics = NetworkInterface - .getNetworkInterfaces(); nics.hasMoreElements();) { + .getNetworkInterfaces(); nics.hasMoreElements(); ) { NetworkInterface ifc = nics.nextElement(); - if (ifc.isUp()) { - log.trace("Testing interface:" + ifc.getDisplayName()); - if (ifc.getIndex() < lowest || address == null) { - lowest = ifc.getIndex(); - } - else if (address != null) { - continue; + if (ifc.isUp() || !ifc.isVirtual() || !ifc.isLoopback()) { + if (address != null) { + break; } if (!ignoreInterface(ifc.getDisplayName())) { for (Enumeration addrs = ifc - .getInetAddresses(); addrs.hasMoreElements();) { + .getInetAddresses(); addrs.hasMoreElements(); ) { InetAddress inetAddress = addrs.nextElement(); if (inetAddress instanceof Inet6Address + // filter ::1 && !inetAddress.isLoopbackAddress() + // filter fe80::/10 + && !inetAddress.isLinkLocalAddress() + // filter ::/128 + && !inetAddress.isAnyLocalAddress() + // filter fec0::/10,which was discarded, but some + // address may be deployed. + && !inetAddress.isSiteLocalAddress() + // filter fd00::/8 + && !isUniqueLocalAddress(inetAddress) && isPreferredAddress(inetAddress)) { log.trace("Found non-loopback interface: " + ifc.getDisplayName()); address = inetAddress; + break; } } } @@ -106,40 +91,22 @@ && isPreferredAddress(inetAddress)) { catch (IOException e) { log.error("Cannot get first non-loopback address", e); } - if (address == null) { - try { - InetAddress localHost = InetAddress.getLocalHost(); - if (localHost instanceof Inet6Address && !localHost.isLoopbackAddress() - && isPreferredAddress(localHost)) { - address = localHost; - } - } - catch (UnknownHostException e) { - log.warn("Unable to retrieve localhost"); - } - } return address; } public String findIPv6Address() { - InetUtils.HostInfo hostInfo = findFirstNonLoopbackHostInfo(); - String ip = hostInfo != null ? hostInfo.getIpAddress() : ""; - if (!StringUtils.isEmpty(ip)) { - int index = ip.indexOf('%'); - ip = index > 0 ? ip.substring(0, index) : ip; - return normalizeIPv6(ip); - } - return ip; + InetUtils.HostInfo hostInfo = findFirstValidHostInfo(); + return hostInfo != null ? normalizeIPv6(hostInfo.getIpAddress()) : null; } - public String normalizeIPv6(String ip) { + private String normalizeIPv6(String ip) { // Remove the suffix of network card in IPv6 address, such as // 2408:400a:8c:5400:6578:5c42:77b1:bc5d%eth0 int idx = ip.indexOf("%"); return idx != -1 ? "[" + ip.substring(0, idx) + "]" : "[" + ip + "]"; } - boolean isPreferredAddress(InetAddress address) { + private boolean isPreferredAddress(InetAddress address) { if (this.properties.isUseOnlySiteLocalInterfaces()) { final boolean siteLocalAddress = address.isSiteLocalAddress(); if (!siteLocalAddress) { @@ -157,35 +124,43 @@ boolean isPreferredAddress(InetAddress address) { return true; } } - log.trace("Ignoring address: " + address.getHostAddress()); return false; } boolean ignoreInterface(String interfaceName) { for (String regex : this.properties.getIgnoredInterfaces()) { if (interfaceName.matches(regex)) { - log.trace("Ignoring interface: " + interfaceName); return true; } } return false; } - public InetUtils.HostInfo convertAddress(final InetAddress address) { + private InetUtils.HostInfo getHostInfo(final InetAddress address) { InetUtils.HostInfo hostInfo = new InetUtils.HostInfo(); - Future result = this.executorService.submit(address::getHostName); - - String hostname; - try { - hostname = result.get(this.properties.getTimeoutSeconds(), TimeUnit.SECONDS); + String hostName = address.getHostName(); + if (hostName == null) { + hostName = "localhost"; } - catch (Exception e) { - log.info("Cannot determine local hostname"); - hostname = "localhost"; + hostInfo.setHostname(hostName); + if (StringUtils.isNotEmpty(address.getHostAddress())) { + hostInfo.setIpAddress(address.getHostAddress()); + } + else { + hostInfo.setIpAddress(StringUtils.EMPTY); } - hostInfo.setHostname(hostname); - hostInfo.setIpAddress(address.getHostAddress()); return hostInfo; } + /** + * If the address is Unique Local Address. + * + * @param inetAddress {@link InetAddress} + * @return {@code true} if the address is Unique Local Address, otherwise {@code false} + */ + private boolean isUniqueLocalAddress(InetAddress inetAddress) { + byte[] ip = inetAddress.getAddress(); + return (ip[0] & 0xff) == 0xfd; + } + } diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/utils/UtilIPv6AutoConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/util/UtilIPv6AutoConfiguration.java similarity index 89% rename from spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/utils/UtilIPv6AutoConfiguration.java rename to spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/util/UtilIPv6AutoConfiguration.java index a33781e8a8..30b6bd0e80 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/utils/UtilIPv6AutoConfiguration.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/util/UtilIPv6AutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.alibaba.cloud.nacos.utils; +package com.alibaba.cloud.nacos.util; import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled; @@ -37,8 +37,8 @@ public UtilIPv6AutoConfiguration() { @Bean @ConditionalOnMissingBean - public InetIPv6Util inetIPv6Util(InetUtilsProperties properties) { - return new InetIPv6Util(properties); + public InetIPv6Utils inetIPv6Utils(InetUtilsProperties properties) { + return new InetIPv6Utils(properties); } } diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/resources/META-INF/spring.factories index 3a26d92443..728db642e2 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/resources/META-INF/spring.factories @@ -7,7 +7,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration,\ com.alibaba.cloud.nacos.loadbalancer.LoadBalancerNacosAutoConfiguration,\ com.alibaba.cloud.nacos.NacosServiceAutoConfiguration,\ - com.alibaba.cloud.nacos.utils.UtilIPv6AutoConfiguration + com.alibaba.cloud.nacos.util.UtilIPv6AutoConfiguration org.springframework.cloud.bootstrap.BootstrapConfiguration=\ com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration org.springframework.boot.SpringApplicationRunListener=\ diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryAutoConfigurationTests.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryAutoConfigurationTests.java index bea6e4856a..cccad65b6e 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryAutoConfigurationTests.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryAutoConfigurationTests.java @@ -18,7 +18,7 @@ import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosServiceAutoConfiguration; -import com.alibaba.cloud.nacos.utils.UtilIPv6AutoConfiguration; +import com.alibaba.cloud.nacos.util.UtilIPv6AutoConfiguration; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryClientConfigurationTest.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryClientConfigurationTest.java index 0b9012b758..384bbdcfa6 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryClientConfigurationTest.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryClientConfigurationTest.java @@ -18,7 +18,7 @@ import com.alibaba.cloud.nacos.NacosServiceAutoConfiguration; import com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration; -import com.alibaba.cloud.nacos.utils.UtilIPv6AutoConfiguration; +import com.alibaba.cloud.nacos.util.UtilIPv6AutoConfiguration; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/reactive/NacosReactiveDiscoveryClientConfigurationTests.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/reactive/NacosReactiveDiscoveryClientConfigurationTests.java index 4ace7e2d2d..f043762e30 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/reactive/NacosReactiveDiscoveryClientConfigurationTests.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/discovery/reactive/NacosReactiveDiscoveryClientConfigurationTests.java @@ -18,7 +18,7 @@ import com.alibaba.cloud.nacos.NacosServiceAutoConfiguration; import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration; -import com.alibaba.cloud.nacos.utils.UtilIPv6AutoConfiguration; +import com.alibaba.cloud.nacos.util.UtilIPv6AutoConfiguration; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations;