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 ) { 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(); } if (cachePolicy == InetAddressCachePolicy.NEVER) { cache.remove(host, this ); } else { CachedAddresses cachedAddresses = new CachedAddresses ( host, inetAddresses, cachePolicy == InetAddressCachePolicy.FOREVER ? 0L : System.nanoTime() + 1000_000_000L * cachePolicy ); if (cache.replace(host, this , cachedAddresses) && cachePolicy != InetAddressCachePolicy.FOREVER) { 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 private static final String cachePolicyProp = "networkaddress.cache.ttl" ;private static final String cachePolicyPropFallback = "sun.net.inetaddr.ttl" ;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");
参考
windows 下看 dns ttl
1 nslookup -debug www.www.baidu.com
windows nslokup
linux下看dns ttl
linux dig
java dns cache ttl
how-to see time to live ttl for a dns record