JVM DNS缓存问题

JVM DNS缓存问题

JVM DNS缓存问题

在某一项目中,一个跨机房调用,机房之间日常有专线连接,万一在专线故障的情况下,降级通过公网通讯。

  • 方案一: 为服务配置两个域名,一个域名其中一个域名解析到公网地址,另一个解析到专线地址。客户端会根据一个故障指令,从专线域名切换的公网域名。
  • 方案二: 只用一个域名,通过dns服务来决定走公网还是走专线。这就涉及到两个问题,dns更新,和客户端的dns缓存。

DNS是一个各层都喜欢缓存的东西,比如浏览器,操作系统,DNS服务器等会缓存,每一层缓存的时间和规则都有些不一致。

这里重点讨论JVM的缓存,也顺道提供一下如何查看操作系统的dns TTL的命令。

盲测

直接写测试代码,打印解析结果

1
2
3
4
5
6
7
8
9
10
11
public class DnsLookupTest {
public static void main(String[] args) throws UnknownHostException, InterruptedException {
while (true) {
InetAddress[] inetAddresses = InetAddress.getAllByName("www.baidu.com");
for (InetAddress inetAddress : inetAddresses) {
System.out.println(new Date() + ":" + inetAddress.getHostName() + "-->" + inetAddress.getHostAddress());
}
Thread.sleep(1000);
}
}
}

同时修改windows的hosts文件,模拟dns变更。

1
2
# The current time is: 16:55:29.67
127.0.0.4 www.baidu.com

输出

1
2
3
4
5
6
7
8
Wed Feb 01 16:55:51 CST 2023:www.baidu.com-->127.0.0.3
Wed Feb 01 16:55:52 CST 2023:www.baidu.com-->127.0.0.3
Wed Feb 01 16:55:53 CST 2023:www.baidu.com-->127.0.0.3
Wed Feb 01 16:55:54 CST 2023:www.baidu.com-->127.0.0.3
Wed Feb 01 16:55:55 CST 2023:www.baidu.com-->127.0.0.4
Wed Feb 01 16:55:56 CST 2023:www.baidu.com-->127.0.0.4
Wed Feb 01 16:55:58 CST 2023:www.baidu.com-->127.0.0.4
Wed Feb 01 16:55:59 CST 2023:www.baidu.com-->127.0.0.4

从测试结果看,16:55:29修改hosts,Java代码中16:55:55更新,大概30秒不到。

刨根问底

先看下java.net.InetAddress.java的代码,其中获取地址的一段如下。Jvm中有一个缓存和一个缓存策略。支持三种策略:不缓存,永远缓存,TTL方式。 缓存分两种,一种是成功解析的,一种是没有解析的。对应的两个配置,InetAddressCachePolicy.getNegative()InetAddressCachePolicy.get()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
if (addresses == this) {
// lookup name services
InetAddress[] inetAddresses;
UnknownHostException ex;
int cachePolicy;
try {
inetAddresses = getAddressesFromNameService(host, reqAddr);
ex = null;
cachePolicy = InetAddressCachePolicy.get();
} catch (UnknownHostException uhe) {
inetAddresses = null;
ex = uhe;
cachePolicy = InetAddressCachePolicy.getNegative();
}
// remove or replace us with cached addresses according to cachePolicy
if (cachePolicy == InetAddressCachePolicy.NEVER) {
cache.remove(host, this);
} else {
CachedAddresses cachedAddresses = new CachedAddresses(
host,
inetAddresses,
cachePolicy == InetAddressCachePolicy.FOREVER
? 0L
// cachePolicy is in [s] - we need [ns]
: System.nanoTime() + 1000_000_000L * cachePolicy
);
if (cache.replace(host, this, cachedAddresses) &&
cachePolicy != InetAddressCachePolicy.FOREVER) {
// schedule expiry
expirySet.add(cachedAddresses);
}
}
if (inetAddresses == null) {
throw ex == null ? new UnknownHostException(host) : ex;
} return inetAddresses;
}

再来看下sun.net.InetAddressCachePolicy.java的代码,成功的和失败的,又对应两组配置,命名看,一个配置加上一个兜底的。 默认的,成功解析的TTL是30秒,失败的不解析。

1
2
3
4
5
6
// Controls the cache policy for successful lookups only
private static final String cachePolicyProp = "networkaddress.cache.ttl";
private static final String cachePolicyPropFallback = "sun.net.inetaddr.ttl";
// Controls the cache policy for negative lookups only
private static final String negativeCachePolicyProp = "networkaddress.cache.negative.ttl";
private static final String negativeCachePolicyPropFallback = "sun.net.inetaddr.negative.ttl";

要修改JVM DNS TTL

有以下两种方式,修改全局,Java8 对应的配置文件$JAVA_HOME/jre/lib/security/java.security,Java 11及以上 $JAVA_HOME/conf/security/java.security

1
networkaddress.cache.ttl=60

程序内修改

1
java.security.Security.setProperty("networkaddress.cache.ttl" , "60");

参考

  1. windows 下看 dns ttl
1
nslookup -debug www.www.baidu.com

windows nslokup

  1. linux下看dns ttl
1
dig A www.cyberciti.biz

linux dig

  1. java dns cache ttl
  2. how-to see time to live ttl for a dns record