需求描述
使用 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注册-监听器形式
- 新建监听器类
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:使用此方法完美解决,可以愉快的跨服务器请求了!
特别强调
- 跨服务器组装业务一定要使用公网IP,千万不要用内网IP!!!
- 注意防火墙、安全组的限制!!!