域名解析并非一個簡單的過程,其解析結果可能會被層層緩存,如瀏覽器 DNS 緩存、操作系統 DNS 緩存、ISP 的 DNS緩存,容易被忽略的是 JVM 本身也會對 DNS 進行緩存。
JVM 的 DNS 緩存可以通過以下參數進行配置:
- networkaddress.cache.ttl
- sun.net.inetaddr.ttl
- 如果開啟了 SecurityManager,優先從 ${java.home}/jre/lib/security/java.security 中讀取參數 networkaddress.cache.ttl,單位為秒。
- 如果未讀取到,則會讀取啟動參數 sun.net.inetaddr.ttl 作為緩存 ttl.
- 如果以上參數均未讀取到,并且未開啟 SecurityManager,則會使用默認值 30s.
- 否則使用初始值 -1,表示永久生效。
以上邏輯可以在 InetAddressCachePolicy.class 中體現,反編譯后,有如下代碼
static {
Integer var0 = (Integer)AccessController.doPrivileged(new PrivilegedAction<Integer>() {
public Integer run() {
String var1;
// 優先讀取 networkaddress.cache.ttl
try {
var1 = Security.getProperty("networkaddress.cache.ttl");
if (var1 != null) {
return Integer.valueOf(var1);
}
} catch (NumberFormatException var3) {
;
}
// 未讀取到,則讀取啟動參數 sun.net.inetaddr.ttl
try {
var1 = System.getProperty("sun.net.inetaddr.ttl");
if (var1 != null) {
return Integer.decode(var1);
}
} catch (NumberFormatException var2) {
;
}
return null;
}
});
if (var0 != null) {
cachePolicy = var0.intValue();
if (cachePolicy < 0) {
cachePolicy = -1;
}
propertySet = true;
} else if (System.getSecurityManager() == null) {
// 如果未啟用 SecurityManager,使用默認值 30
cachePolicy = 30;
}
其中 cachePolicy 初始值為 -1,表示緩存永不失效。
我們通過以下代碼對 DNS 緩存參數進行測試:
public class HostProcessor implements Runnable {
private int count = 1;
/**
* 每秒解析1次 www.baidu.com 的 IP
*/
@Override
public void run() {
while (true) {
System.out.println(count);
printIp("www.baidu.com");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count = count + 1;
System.out.print("\r\n");
}
}
/**
* 解析并打印 IP
*/
private void printIp(String host) {
InetAddress address = null;
try {
address = Inet4Address.getByName(host);
} catch (UnknownHostException e) {
e.printStackTrace();
}
if (address == null) {
return;
}
System.out.println(address.getHostAddress());
System.out.println(address.getHostName());
}
public static void main(String[] args) {
Thread thread = new Thread(new HostProcessor());
thread.start();
}
}
networkaddress.cache.ttl 參數測試
首先修改 java.security 中的 networkaddress.cache.ttl 配置,改為以下值:
networkaddress.cache.ttl=15
啟動程序,開始打印域名對應 IP 后,立刻修改 /etc/hosts,將 www.baidu.com 指向到陌生 IP:
47.95.164.112 www.baidu.com
觀察控制臺輸出,有如下結果:
image
在 15 秒后,對 host 映射的修改才反應出來
sun.net.inetaddr.ttl 參數測試
注釋掉 java.security 中的 networkaddress.cache.ttl 配置,如下:
#networkaddress.cache.ttl=15
添加程序啟動參數:
-Dsun.net.inetaddr.ttl=10
同樣啟動程序后,將原先 host 文件的修改回退,觀察到如下輸出:
image
在 10 秒后,對 host 映射的修改才反應出來
除了解析成功的結果,JVM 也會緩存解析失敗的結果,可以通過下列參數配置:
- networkaddress.cache.negative.ttl
- sun.net.inetaddr.negative.ttl
這里不展開介紹