在使用dubbo注冊服務時會遇到IP解析錯誤導致無法正常訪問.
比如: 本機設置的IP為172.16.11.111, 但實際解析出來的是180.20.174.11
這樣就導致這個Service永遠也無法被訪問到, 而調用方一直報錯.
當然若發現服務無法訪問, 最好先通過dubbo-admin后台排查下注冊的服務是否正常.
IP解析異常時的解決方法:
- 綁定hostname+ip
1. 先查看機器的hostname
2. 修改hosts文件, 增加hostname 172.16.11.111
- 配置nameserver
排查機器上配置的nameserver是否有問題, 若存在無用的nameserver則直接刪掉
- 在dubbo的配置文件中寫死host
<dubbo:protocol host="172.16.11.111"/>
或者在每個provider中綁定host
<dubbo:provider host="172.16.11.111">
最好不要用第三種方式, 限制太多. 而且如果這樣做了就不支持集群了.
dubbo的官網也不建議使用這種方式. 請慎用.
dubbo獲取IP源碼分析
/**
* 判斷host是否為不可用的本地Host
*/
public static boolean isInvalidLocalHost(String host) {
return host == null
|| host.length() == 0
|| host.equalsIgnoreCase("localhost")
|| host.equals("0.0.0.0")
|| (LOCAL_IP_PATTERN.matcher(host).matches());
}
/**
* 獲取本地Host.
* 若address == null ? "127.0.0.1" : InetAddress.getHostAddress();
*/
public static String getLocalHost(){
InetAddress address = getLocalAddress();
return address == null ? LOCALHOST : address.getHostAddress();
}
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
...
...
//1. 先從ProtocolConfig中取host. 若沒有配置則為null
String host = protocolConfig.getHost();
//2. 再從ProviderConfig中取host. 若沒有配置則為null
if (provider != null && (host == null || host.length() == 0)) {
host = provider.getHost();
}
boolean anyhost = false;
//3. 若取出的是本地host, 則繼續取host
if (NetUtils.isInvalidLocalHost(host)) {
anyhost = true;
try {
//4. 通過InetAddress的方式獲取Host
//默認讀取本機hosts中hostname對應的IP
//如: 你在hosts中配置了 leo 172.16.11.111
//則讀取的IP就是172.16.11.111
host = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
logger.warn(e.getMessage(), e);
}
if (NetUtils.isInvalidLocalHost(host)) {
if (registryURLs != null && registryURLs.size() > 0) {
for (URL registryURL : registryURLs) {
try {
Socket socket = new Socket();
try {
//5. 通過Socket的方式獲取Host
//一般解析到這里, 都會獲取到正確的本地IP, 除非你有多網卡, 或者有VPN, 導致無法正常解析.
SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
socket.connect(addr, 1000);
host = socket.getLocalAddress().getHostAddress();
break;
} finally {
try {
socket.close();
} catch (Throwable e) {}
}
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
}
}
//6. 遍歷本地網卡, 返回第一個合理的Host
//最后一個大招. 當上述都解析不到時, 則會遍歷本地網卡.
//逐個獲取IP, 直到有一個合理的IP為止.
if (NetUtils.isInvalidLocalHost(host)) {
host = NetUtils.getLocalHost();
}
}
}
...
}
/**
* 遍歷本地網卡,返回第一個合理的IP。
* @return 本地網卡IP
*/
public static InetAddress getLocalAddress() {
if (LOCAL_ADDRESS != null)
return LOCAL_ADDRESS;
InetAddress localAddress = getLocalAddress0();
LOCAL_ADDRESS = localAddress;
return localAddress;
}
/**
* 遍歷本地網卡,返回第一個合理的IP。
* @return 本地網卡IP
*/
private static InetAddress getLocalAddress0() {
InetAddress localAddress = null;
try {
localAddress = InetAddress.getLocalHost();
if (isValidAddress(localAddress)) {
return localAddress;
}
} catch (Throwable e) {
logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
}
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
if (interfaces != null) {
while (interfaces.hasMoreElements()) {
try {
NetworkInterface network = interfaces.nextElement();
Enumeration<InetAddress> addresses = network.getInetAddresses();
if (addresses != null) {
while (addresses.hasMoreElements()) {
try {
InetAddress address = addresses.nextElement();
if (isValidAddress(address)) {
return address;
}
} catch (Throwable e) {
logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
}
}
}
} catch (Throwable e) {
logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
}
}
}
} catch (Throwable e) {
logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
}
logger.error("Could not get local host ip address, will use 127.0.0.1 instead.");
return localAddress;
}