Skip to content

Commit

Permalink
feature: support dual stack address registry and discovery in SCL (#3207
Browse files Browse the repository at this point in the history
)

Support dual stack address registry and discovery in SCL  (#3207)
  • Loading branch information
steverao authored Mar 21, 2023
1 parent e29f81c commit d50b995
Show file tree
Hide file tree
Showing 15 changed files with 280 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 的配置项如下所示:
Expand Down
24 changes: 23 additions & 1 deletion spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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]
----
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,39 @@
### 服务发现


#### 集成 Ribbon
为了便于使用,NacosServerList 实现了 com.netflix.loadbalancer.ServerList<Server> 接口,并在 @ConditionOnMissingBean 的条件下进行自动注入。如果您有定制化的需求,可以自己实现自己的 ServerList。

Nacos Discovery Starter 默认集成了 Ribbon ,所以对于使用了 Ribbon 做负载均衡的组件,可以直接使用 Nacos 的服务发现。

#### 集成 Spring Cloud Loadbalancer
```xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
</dependencies>
```
增加如下配置,使用 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

Expand Down Expand Up @@ -158,11 +186,6 @@ Spring Cloud Nacos Discovery 遵循了 spring cloud common 标准,实现了 Au



### 服务发现
NacosServerList 实现了 com.netflix.loadbalancer.ServerList<Server> 接口,并在 @ConditionOnMissingBean 的条件下进行自动注入,默认集成了Ribbon。

如果需要有更加自定义的可以使用 @Autowired 注入一个 NacosRegistration 实例,通过其持有的 NamingService 字段内容直接调用 Nacos API。


## Endpoint 信息查看

Expand Down Expand Up @@ -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||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Server> 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
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
</dependencies>
```
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
Expand Down Expand Up @@ -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 <Server> 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.
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -230,7 +234,7 @@ public class NacosDiscoveryProperties {
private boolean failFast = true;

@Autowired
private InetIPv6Util inetIPv6Util;
private InetIPv6Utils inetIPv6Utils;

@Autowired
private InetUtils inetUtils;
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand Down Expand Up @@ -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);
}
}
}

}
Loading

0 comments on commit d50b995

Please sign in to comment.