今天刚刚碰到一个JVM缓存DNS的引起的问题,分享一下一个同事的博文:
------------------------------------------------------丑陋的分割线---------------------------------------------
最近手上的某java应用频繁因网络问题而出现故障,同时也抛出一个问题:JAVA本身对DNS的缓存时间是多久?
对于非公司内部产品的疑问,第一反应Google之,大致有两种说法:
第1种:默认情况下networkaddress.cache.ttl=-1,代表永久缓存(配置文件路径: JAVA_HOME/jre/lib/security/java.security),就是在应用启动之后第一次DNS 解析成功的结果会一直cache到应用停止。显然在域名对应的IP有变更的时候,如果不重启应用就会造成故障。有部分同事以前也做过相关测试,认同这种说法。
第2种:jdk1.5和1.5之前的版本默认DNS 缓存时间是永久缓存,jdk 1.6以后与security manager策略有关(jboss tomcat 等app server默认不启用,详见此文),如果没有启用security manager ,默认DNS 缓存时间30秒。策略配置文件:JAVA_HOME/jre/lib/security/java.policy
根据上述说法,先看一下配置文件JAVA_HOME/jre/lib/security/java.security
#
# The Java-level namelookup cache policy for successful lookups:
#
# any negative value: caching forever
# any positive value: the number of seconds to cache an address for
# zero: do not cache
#
# default value is forever (FOREVER). For security reasons, this
# caching is made forever when a security manager is set. When a security
# manager is not set, the default behavior in this implementation
# is to cache for 30 seconds.
#
# NOTE: setting this to anything other than the default value can have
# serious security implications. Do not set it unless
# you are sure you are not exposed to DNS spoofing attack.
#
#networkaddress.cache.ttl=-1
# The Java-level namelookup cache policy for failed lookups:
#
# any negative value: cache forever
# any positive value: the number of seconds to cache negative lookup results
# zero: do not cache
#
# In some Microsoft Windows networking environments that employ
# the WINS name service in addition to DNS, name service lookups
# that fail may take a noticeably long time to return (approx. 5 seconds).
# For this reason the default caching policy is to maintain these
# results for 10 seconds.
#
#
networkaddress.cache.negative.ttl=10
|
查看了jboss的run.sh脚本并未设置 java.security 相关参数,那我们的默认缓存时间应该是30 seconds
理论依据往往没有实验结果让人信服,于是又继续搜索相关的内容,终于在stackoverflow上找到了可以输出缓存内容的脚本。
我稍微修改了一下:
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.text.SimpleDateFormat;
public class DNSCache {
public static void main(String[] args) throws Exception {
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
InetAddress.getByName("www.google.com");
try {
InetAddress.getByName("nowhere.example.com");
} catch (UnknownHostException e) {
}
System.out.println("current time:" + sdf.format(d));
String addressCache = "addressCache";
System.out.println(addressCache);
printDNSCache(addressCache);
String negativeCache = "negativeCache";
System.out.println(negativeCache);
printDNSCache(negativeCache);
}
private static void printDNSCache(String cacheName) throws Exception {
Class<InetAddress> klass = InetAddress.class;
Field acf = klass.getDeclaredField(cacheName);
acf.setAccessible(true);
Object addressCache = acf.get(null);
Class cacheKlass = addressCache.getClass();
Field cf = cacheKlass.getDeclaredField("cache");
cf.setAccessible(true);
Map<String, Object> cache = (Map<String, Object>) cf.get(addressCache);
for (Map.Entry<String, Object> hi : cache.entrySet()) {
Object cacheEntry = hi.getValue();
Class cacheEntryKlass = cacheEntry.getClass();
Field expf = cacheEntryKlass.getDeclaredField("expiration");
expf.setAccessible(true);
long expires = (Long) expf.get(cacheEntry);
Field af = cacheEntryKlass.getDeclaredField("address");
af.setAccessible(true);
InetAddress[] addresses = (InetAddress[]) af.get(cacheEntry);
List<String> ads = new ArrayList<String>(addresses.length);
for (InetAddress address : addresses) {
ads.add(address.getHostAddress());
}
System.out.println(hi.getKey() + " "+new Date(expires) +" " +ads);
}
}
}
|
编译 javac -Xlint:unchecked DNSCache.java
执行 java DNSCache 得到结果:
current time:2012-07-27 11:35:31
addressCache
0.0.0.0 Fri Jul 27 11:36:01 CST 2012 [0.0.0.0]
www.google.com Fri Jul 27 11:36:01 CST 2012 [74.125.71.105, 74.125.71.106, 74.125.71.147, 74.125.71.99, 74.125.71.103, 74.125.71.104]
negativeCache
nowhere.example.com Fri Jul 27 11:35:41 CST 2012 [0.0.0.0]
解析成功的域名www.google.com 缓存时间正好30 seconds
解析失败的域名nowhere.example.com 缓存时间正好10 seconds
与前面的理论完全对上,而且我们还看到对于多条A记录的域名它会全部缓存起来,并不是只缓存其中的一条A记录。
这里又引出了一个疑问:对于多条A记录是采用轮循还是什么策略使用呢?
我们可以修改脚本测试一下:
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.text.SimpleDateFormat;
public class DNSCache {
public static void main(String[] args) throws Exception {
System.out.println("start loop\n\n");
for(int i = 0; i < 30; ++i) {
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("current time:" + sdf.format(d));
InetAddress addr1 = InetAddress.getByName("www.google.com");
String addressCache = "addressCache";
System.out.println(addressCache);
printDNSCache(addressCache);
System.out.println("getHostAddress:" + addr1.getHostAddress());
System.out.println("*******************************************");
System.out.println("\n");
java.lang.Thread.sleep(10000);
}
System.out.println("end loop");
}
private static void printDNSCache(String cacheName) throws Exception {
Class<InetAddress> klass = InetAddress.class;
Field acf = klass.getDeclaredField(cacheName);
acf.setAccessible(true);
Object addressCache = acf.get(null);
Class cacheKlass = addressCache.getClass();
Field cf = cacheKlass.getDeclaredField("cache");
cf.setAccessible(true);
Map<String, Object> cache = (Map<String, Object>) cf.get(addressCache);
for (Map.Entry<String, Object> hi : cache.entrySet()) {
Object cacheEntry = hi.getValue();
Class cacheEntryKlass = cacheEntry.getClass();
Field expf = cacheEntryKlass.getDeclaredField("expiration");
expf.setAccessible(true);
long expires = (Long) expf.get(cacheEntry);
Field af = cacheEntryKlass.getDeclaredField("address");
af.setAccessible(true);
InetAddress[] addresses = (InetAddress[]) af.get(cacheEntry);
List<String> ads = new ArrayList<String>(addresses.length);
for (InetAddress address : addresses) {
ads.add(address.getHostAddress());
}
System.out.println(hi.getKey() + " "+new Date(expires) +" " +ads);
}
}
}
|
编译执行
start loop
current time:2012-07-28 15:30:58
addressCache
0.0.0.0 Sat Jul 28 15:31:28 CST 2012 [0.0.0.0]
www.google.com Sat Jul 28 15:31:28 CST 2012 [74.125.71.103, 74.125.71.104, 74.125.71.105, 74.125.71.106, 74.125.71.147, 74.125.71.99]
getHostAddress:74.125.71.103
*******************************************
current time:2012-07-28 15:31:08
addressCache
0.0.0.0 Sat Jul 28 15:31:28 CST 2012 [0.0.0.0]
www.google.com Sat Jul 28 15:31:28 CST 2012 [74.125.71.103, 74.125.71.104, 74.125.71.105, 74.125.71.106, 74.125.71.147, 74.125.71.99]
getHostAddress:74.125.71.103
*******************************************
current time:2012-07-28 15:31:18
addressCache
0.0.0.0 Sat Jul 28 15:31:28 CST 2012 [0.0.0.0]
www.google.com Sat Jul 28 15:31:28 CST 2012 [74.125.71.103, 74.125.71.104, 74.125.71.105, 74.125.71.106, 74.125.71.147, 74.125.71.99]
getHostAddress:74.125.71.103
*******************************************
current time:2012-07-28 15:31:28
addressCache
www.google.com Sat Jul 28 15:31:58 CST 2012 [74.125.71.104, 74.125.71.105, 74.125.71.106, 74.125.71.147, 74.125.71.99, 74.125.71.103]
getHostAddress:74.125.71.104
*******************************************
#后面省略
|
结论:在缓存有效期内,取到的IP永远是缓存中全部A记录的第一条,并没有轮循之类的策略。
缓存失效之后重新进行DNS解析,因为每次域名解析返回的A记录顺序会发生变化(dig www.google.com测试可见),所以缓存中的数据顺序也变了,取到的IP也变化。
当然最可靠的还是看下源码实现,有研究的同学请告诉我一下:)
最后附上几种修改缓存时间的方法:
1. jvm启动参数里面配置-Dsun.net.inetaddr.ttl=value
2. 修改 配置文件JAVA_HOME/jre/lib/security/java.security相应的参数networkaddress.cache.ttl=value
3. 代码里直接设置:java.security.Security.setProperty(”networkaddress.cache.ttl” , “value”);
参考资料:
http://docs.oracle.com/javase/6/docs/api/java/net/InetAddress.html
http://kenwublog.com/java-dns-cache-setting
http://stackoverflow.com/questions/1835421/java-dns-cache-viewer
http://docs.jboss.org/jbossas/docs/Server_Configuration_Guide/4/html/Security_on_JBoss-Running_JBoss_with_a_Java_2_security_manager.html
来源:飘零的代码 piao2010 ’s blog
分享到:
相关推荐
且ctrl + f5会避免请求浏览器自身缓存直接向目标网址发起请求DNS域名解析过程a)浏览器缓存b)操作系统文件c)本地DNS服务器(ISP的LDNS)查看缓存(有->返回)d)根服务器e)gTLD主域名服务器f)gTLD主域名服务器...
5.2.1 ARP cache 45 5.2.2 代理ARP 47 5.2.3 反向地址解析协议 47 5.3 使用ARP命令 47 5.4 小结 47 第6章 DNS:名字服务器 48 6.1 域名系统概述 48 6.2 授权局 50 6.3 DNS分布数据库 50 6.4 域和区 50 6.5 Internet...
5.2.1 ARP cache 45 5.2.2 代理ARP 47 5.2.3 反向地址解析协议 47 5.3 使用ARP命令 47 5.4 小结 47 第6章 DNS:名字服务器 48 6.1 域名系统概述 48 6.2 授权局 50 6.3 DNS分布数据库 50 6.4 域和区 50 6.5 Internet...
5.2.1 ARP cache 45 5.2.2 代理ARP 47 5.2.3 反向地址解析协议 47 5.3 使用ARP命令 47 5.4 小结 47 第6章 DNS:名字服务器 48 6.1 域名系统概述 48 6.2 授权局 50 6.3 DNS分布数据库 50 6.4 域和区 50 6.5 Internet...
5.2.1 ARP cache 45 5.2.2 代理ARP 47 5.2.3 反向地址解析协议 47 5.3 使用ARP命令 47 5.4 小结 47 第6章 DNS:名字服务器 48 6.1 域名系统概述 48 6.2 授权局 50 6.3 DNS分布数据库 50 6.4 域和区 50 6.5 Internet...
5.2.1 ARP cache 45 5.2.2 代理ARP 47 5.2.3 反向地址解析协议 47 5.3 使用ARP命令 47 5.4 小结 47 第6章 DNS:名字服务器 48 6.1 域名系统概述 48 6.2 授权局 50 6.3 DNS分布数据库 50 6.4 域和区 50 6.5 Internet...
名字服务器 48 6.1 域名系统概述 48 6.2 授权局 50 6.3 DNS分布数据库 50 6.4 域和区 50 6.5 Internet顶级域 51 6.6 选择一个域名服务器 52 6.7 名字服务解析过程 52 6.7.1 递归查询 52 ...
一个时间转换的问题,顺便谈谈搜索技巧 .net中的正则表达式使用高级技巧 (一) C#静态成员和方法的学习小结 C#中结构与类的区别 C#中 const 和 readonly 的区别 利用自定义属性,定义枚举值的详细文本 Web标准和ASP...
1.2.3 cache服务器squid 18 1.2.4 带负载均衡的http服务器apache 19 1.2.5 支持集群功能的web服务器tomcat 21 1.2.6 开源数据库服务器之骄子mysql 23 1.2.7 功能强大的flv流媒体服务器red5 24 1.3 门户网站开发...