需求描述

使用 Spring Cloud Alibaba 搭建微服务,业务实例默认使用内网 IP 注册到 Nacos 服务端,这样在跨局域网时会无法请求成功。那如何解决呢?

答:指定外网 IP 注册到 Nacos 上。

下述方法来自网络,版权归原著作人,本人只是搬运工+实践者!

方案一:指定IP注册

在 Nacos 客户端指定IP,启动成功后即可以在 Nacos 服务端上看到对应的IP。

在 bootstrap.yml 配置文件中写入:

spring.cloud.nacos.discovery.ip = xx
spring.cloud.nacos.discovery.port = xxx

或指定网卡注册:

spring.cloud.nacos.discovery.networkInterface = xx

也可以使用spring cloud 的 InetUtils工具,配置项为,具体说明可以自行检索

spring.cloud.inetutils.default-hostname
spring.cloud.inetutils.default-ip-address
spring.cloud.inetutils.ignored-interfaces
spring.cloud.inetutils.preferred-networks
spring.cloud.inetutils.timeout-seconds
spring.cloud.inetutils.use-only-site-local-interfaces

方案二: 动态注册IP-配置文件形式

package com.zhongyi.doctor.config;

import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

/**
 * @author SymbolWong
 * @description
 * @date 2023/2/6 16:25
 */
@Configuration
@Slf4j
public class NacosDiscoveryConfig {
    /**
     * nacosConfigServerAaddr yml中配置的nacos配置中心地址
     */
    @Value("${spring.cloud.nacos.config.server-addr}")
    String nacosConfigServerAaddr;
    /**
     * nacosConfignamespace yml中配置的nacos配置中心命名空间
     */
    @Value("${spring.cloud.nacos.config.namespace}")
    String nacosConfignamespace;
    /**
     * getServerInternetIP 通过Nginx获取本机外网IP,需要Nginx配合配置
     * @author IPMan
     * @date 2022/7/10
     *
     * @return java.lang.String 返回本机外网IP
     */
    private String getServerInternetIPByNginx(){
        //通过配置中心地址构造查询IP请求地址
        String url="http://"+nacosConfigServerAaddr.split(":")[0]+"/getIp";
        //调试输出,这里不推荐err的方式输出,这样仅为测试使用,推荐采用日志实现或者不输出
        // System.err.println(url);
        log.warn("Nacos server addr is {}",url);
        //外网IP
        String internetIP="127.0.0.1";
        //这里一步完成了,构造一个RestTemplate对象,通过对指定URL执行GET请求来获取响应实体
        ResponseEntity<String> response =
                new RestTemplate()
                        .getForEntity(url, String.class);
        // //从响应实体对象中获取内容
        internetIP = response.getBody();
        // //调试输出,这里不推荐err的方式输出,这样仅为测试使用,推荐采用日志实现或者不输出
        // System.err.println(internetIP);
        log.warn("Internet ip is {}",url);
        return internetIP;
    }

    /**
     * nacosProperties Nacos 服务发现配置类,代替yml中spring.cloud.nacos.discovery:配置
     * @author IPMan
     * @date 2022/7/10
     *
     * @return com.alibaba.cloud.nacos.NacosDiscoveryProperties
     */
    @Bean
    public NacosDiscoveryProperties nacosProperties() {
        //new一个nacos服务发现配置对象
        NacosDiscoveryProperties properties = new NacosDiscoveryProperties();
        //设置发现注册的IP,即注册中心详情中的IP,这里很关键,默认是Inet4Address.getLocalHost(),即如果包含子网,则获取的是子网IP
        properties.setIp(getServerInternetIP());
        //设置注册中心地址
        properties.setServerAddr(nacosConfigServerAaddr);
        //设置注册中心命名空间
        properties.setNamespace(nacosConfignamespace);
        return properties;
    }

    private String getServerInternetIP() {
        String internetip = "";
        String url = "https://ip.3322.org";
        internetip = HttpRequest.get(url)
                .header(Header.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36")
                .execute().body();
        log.warn("Internet ip is {}",internetip);
        return internetip;
    }

    /**
     * nacosServiceDiscovery nacos 服务发现对象,这个对象构造完成后是无法设置配置的
     * @author IPMan
     * @date 2022/7/10
     *
     * @param discoveryProperties com.alibaba.cloud.nacos.NacosDiscoveryProperties
     * @param nacosServiceManager com.alibaba.cloud.nacos.NacosServiceManager
     * @return com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery
     */
    @Bean
    public NacosServiceDiscovery nacosServiceDiscovery(
            NacosDiscoveryProperties discoveryProperties,
            NacosServiceManager nacosServiceManager) {
        return new NacosServiceDiscovery(discoveryProperties, nacosServiceManager);
    }
}

PS:本人使用该方法虽然能注册成功,但是请求是失败的,报错:Illegal character in authority at index 7,本来以为是得到的公网IP前后有空格之类的,用了trim方法依然还是报错,有待继续研究。

方案三: 动态IP注册-监听器形式

  1. 新建监听器类
package com.zhongyi.doctor.config;

import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import org.springframework.boot.context.config.ConfigFileApplicationListener;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.core.Ordered;

/**
 * @author SymbolWong
 * @description
 * @date 2023/2/18 9:13
 */
public class AfterConfigListener implements SmartApplicationListener, Ordered {


    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        return (ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(aClass) ||
                ApplicationPreparedEvent.class.isAssignableFrom(aClass));
    }

    @Override
    public int getOrder() {
        return (ConfigFileApplicationListener.DEFAULT_ORDER + 1);
    }

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        String currentIp = getServerInternetIP();
        if (applicationEvent instanceof ApplicationEnvironmentPreparedEvent) {
            System.setProperty("spring.cloud.nacos.discovery.ip", currentIp);
        }
    }


    private String getServerInternetIP() {
        String internetip = "";
        String url = "https://ip.3322.org";
        internetip = HttpRequest.get(url)
                .header(Header.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36")
                .execute().body();
        return internetip.trim();
    }
}

2、启动入口处注入该监听类

SpringApplication springApplication = new SpringApplication(DoctorApplication.class);
springApplication.addListeners(new AfterConfigListener());
springApplication.run(args);

PS:使用此方法完美解决,可以愉快的跨服务器请求了!

特别强调

  1. 跨服务器组装业务一定要使用公网IP,千万不要用内网IP!!!
  2. 注意防火墙、安全组的限制!!!

参考文章:[Nacos] 业务实例如何指定IP注入Nacos_nacos指定ip-CSDN博客

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐