support both IPv4 and IPv6 register (#3029)

* support both IPv4 and IPv6 register
pull/3074/head^2
HHoflittlefish777 2 years ago committed by GitHub
parent d3c26037e8
commit e680fedbc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -301,6 +301,26 @@ Endpoint 暴露的 json 中包含了两种属性:
[service_name].ribbon.NFLoadBalancerRuleClassName=com.alibaba.cloud.nacos.ribbon.NacosRule
----
=== IPv4至IPv6地址迁移方案
==== IPv4和IPv6地址双注册
在配置完成NacosRule作为负载均衡策略后应用启动后会默认将微服务的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 更多的配置项信息

@ -304,6 +304,27 @@ The followings shows how a service instance accesses the Endpoint:
[service_name].ribbon.NFLoadBalancerRuleClassName=com.alibaba.cloud.nacos.ribbon.NacosRule
----
=== IPv4 to IPv6 address migration scheme
==== Both register IPv4 and IPv6 address
After configuring NacosRule 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:

@ -3,6 +3,8 @@ 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 IPv4 instance
#spring.cloud.nacos.discovery.ip-type=IPv4
#register IPv6 instance
#spring.cloud.nacos.discovery.ip-type=IPv6

@ -163,6 +163,24 @@ NacosServerList 实现了 com.netflix.loadbalancer.ServerList<Server> 接口,
如果需要有更加自定义的可以使用 @Autowired 注入一个 NacosRegistration 实例,通过其持有的 NamingService 字段内容直接调用 Nacos API。
## IPv4至IPv6地址迁移方案
### IPv4和IPv6地址双注册
在配置完成NacosRule作为负载均衡策略后应用启动后会默认将微服务的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
```
## Endpoint 信息查看

@ -168,6 +168,24 @@ NacosServerList implements the com.netflix.loadbalancer.ServerList <Server> inte
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.
## IPv4 to IPv6 address migration scheme
### Both register IPv4 and IPv6 address
After configuring NacosRule 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
```
## Endpoint

@ -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.
*/
@ -165,11 +169,12 @@ public class NacosDiscoveryProperties {
private String networkInterface = "";
/**
* choose IPv4 or IPv6,if you don't set it will choose IPv4.
* When IPv6 is chosen but no IPv6 can be found, system will automatically find IPv4 to ensure there is an
* available service address.
* choose IPv4 or IPv6 or both_IPv4_IPv6. if you don't set it will choose
* both_IPv4_IPv6. When IPv6 is chosen but no IPv6 can be found, system will
* automatically find IPv4 to ensure there is an available service address. If
* both_IPv4_IPv6 is set,both IPv4 and IPv6 will be register.
*/
private String ipType = "IPv4";
private String ipType;
/**
* The port your want to register for your service instance, needn't to set it if the
@ -257,16 +262,21 @@ public class NacosDiscoveryProperties {
if (StringUtils.isEmpty(ip)) {
// traversing network interfaces if didn't specify a interface
if (StringUtils.isEmpty(networkInterface)) {
if ("IPv4".equalsIgnoreCase(ipType)) {
if (IPV4.equalsIgnoreCase(ipType)) {
ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
}
else if ("IPv6".equalsIgnoreCase(ipType)) {
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.");
log.warn(
"There is no available IPv6 found. Spring Cloud Alibaba will automatically find IPv4.");
ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
}
}
else if (ipType == null) {
ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
metadata.put(IPV6, inetIPv6Utils.findIPv6Address());
}
else {
throw new IllegalArgumentException(
"please checking the type of IP " + ipType);

@ -16,13 +16,18 @@
package com.alibaba.cloud.nacos.ribbon;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
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.NacosServiceManager;
import com.alibaba.cloud.nacos.util.InetIPv6Utils;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
@ -33,6 +38,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.util.CollectionUtils;
/**
@ -40,17 +46,50 @@ import org.springframework.util.CollectionUtils;
* instance.
*
* @author itmuch.com
* @author HH
*/
public class NacosRule extends AbstractLoadBalancerRule {
private static final Logger LOGGER = LoggerFactory.getLogger(NacosRule.class);
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";
private String ipv4;
private String ipv6;
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
@Autowired
private InetIPv6Utils inetIPv6Utils;
@Autowired
private InetUtils inetUtils;
@PostConstruct
public void init() {
String ip = nacosDiscoveryProperties.getIp();
if (StringUtils.isNotEmpty(ip)) {
if (Pattern.matches(IPV4_REGEX, ip)) {
this.ipv4 = ip;
this.ipv6 = nacosDiscoveryProperties.getMetadata().get(IPV6_KEY);
}
else {
this.ipv6 = ip;
}
}
else {
this.ipv4 = getAppLocalIPv4Address();
this.ipv6 = getAppLocalIPv6Address();
}
}
@Override
public Server choose(Object key) {
try {
@ -65,6 +104,7 @@ public class NacosRule extends AbstractLoadBalancerRule {
LOGGER.warn("no instance in service {}", name);
return null;
}
instances = filterInstanceByIpType(instances);
List<Instance> instancesToChoose = instances;
if (StringUtils.isNotBlank(clusterName)) {
@ -83,6 +123,7 @@ public class NacosRule extends AbstractLoadBalancerRule {
}
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);
convertIPv4ToIPv6(instance);
return new NacosServer(instance);
}
@ -96,4 +137,53 @@ public class NacosRule extends AbstractLoadBalancerRule {
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
private String getAppLocalIPv4Address() {
return inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
}
private String getAppLocalIPv6Address() {
return inetIPv6Utils.findIPv6Address();
}
private List<Instance> filterInstanceByIpType(List<Instance> instances) {
if (StringUtils.isNotEmpty(this.ipv6)) {
List<Instance> ipv6InstanceList = new ArrayList<>();
for (Instance instance : instances) {
if (Pattern.matches(IPV4_REGEX, instance.getIp())) {
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.getIp()))
.collect(Collectors.toList());
}
else {
return ipv6InstanceList;
}
}
return instances.stream()
.filter(instance -> Pattern.matches(IPV4_REGEX, instance.getIp()))
.collect(Collectors.toList());
}
/**
* There is two type Ip,using IPv6 should use IPv6 in metadata to replace IPv4 in IP
* field.
*/
private void convertIPv4ToIPv6(Instance instance) {
if (Pattern.matches(IPV4_REGEX, instance.getIp())) {
String ip = instance.getMetadata().get(IPV6_KEY);
if (StringUtils.isNotEmpty(ip)) {
instance.setIp(ip);
}
}
}
}

@ -76,7 +76,7 @@ public class InetIPv6Utils implements Closeable {
try {
int lowest = Integer.MAX_VALUE;
for (Enumeration<NetworkInterface> nics = NetworkInterface
.getNetworkInterfaces(); nics.hasMoreElements();) {
.getNetworkInterfaces(); nics.hasMoreElements(); ) {
NetworkInterface ifc = nics.nextElement();
if (ifc.isUp()) {
log.trace("Testing interface:" + ifc.getDisplayName());
@ -89,7 +89,7 @@ public class InetIPv6Utils implements Closeable {
if (!ignoreInterface(ifc.getDisplayName())) {
for (Enumeration<InetAddress> addrs = ifc
.getInetAddresses(); addrs.hasMoreElements();) {
.getInetAddresses(); addrs.hasMoreElements(); ) {
InetAddress inetAddress = addrs.nextElement();
if (inetAddress instanceof Inet6Address
&& !inetAddress.isLoopbackAddress()

@ -89,23 +89,27 @@ public class NacosDiscoveryClientConfigurationTest {
public void testSpringCloudGatewayLocatorHeartBeatPublisherEnabled() {
contextRunner
.withPropertyValues("spring.cloud.gateway.discovery.locator.enabled=true")
.run(context -> assertThat(context).hasSingleBean(GatewayLocatorHeartBeatPublisher.class));
.run(context -> assertThat(context)
.hasSingleBean(GatewayLocatorHeartBeatPublisher.class));
}
@Test
public void testZuulGatewayLocatorHeartBeatPublisherEnabled() {
contextRunner
.withConfiguration(AutoConfigurations.of(ZuulProxyMarkerConfiguration.class))
.withConfiguration(
AutoConfigurations.of(ZuulProxyMarkerConfiguration.class))
.run(context -> assertThat(context)
.hasSingleBean(GatewayLocatorHeartBeatPublisher.class));
.hasSingleBean(GatewayLocatorHeartBeatPublisher.class));
}
@Test
public void testZuulAndSpringCloudGatewayLocatorHeartBeatPublisherEnabled() {
contextRunner
.withPropertyValues("spring.cloud.gateway.discovery.locator.enabled=true")
.withConfiguration(AutoConfigurations.of(ZuulProxyMarkerConfiguration.class))
.run(context -> assertThat(context).hasSingleBean(GatewayLocatorHeartBeatPublisher.class));
.withConfiguration(
AutoConfigurations.of(ZuulProxyMarkerConfiguration.class))
.run(context -> assertThat(context)
.hasSingleBean(GatewayLocatorHeartBeatPublisher.class));
}
}

Loading…
Cancel
Save