`
nigelzeng
  • 浏览: 599382 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

【转载】关于java dns cache (域名缓存时间)

    博客分类:
  • Java
阅读更多

今天刚刚碰到一个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

?View Code BASH
#
# 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上找到了可以输出缓存内容的脚本。
我稍微修改了一下:

?View Code JAVA
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记录是采用轮循还是什么策略使用呢?

我们可以修改脚本测试一下:

?View Code JAVA
 
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);
    }
  }
}

编译执行

?View Code BASH
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

分享到:
评论
1 楼 oldrat 2015-04-14  
还给出了 给出“对于多条A记录是采用什么策略返回IP”的结论,赞!

写了操作Java DNS Cache的库 https://github.com/alibaba/java-dns-cache-manipulator ,方便用起来,支持 JDK 6 7 8。可以试试 ~ :)

相关推荐

    sourcecode:原始学习1)HashMap 2)NIO编程模型,利用Java的NIO实现完成聊天室程序

    且ctrl + f5会避免请求浏览器自身缓存直接向目标网址发起请求DNS域名解析过程a)浏览器缓存b)操作系统文件c)本地DNS服务器(ISP的LDNS)查看缓存(有-&gt;返回)d)根服务器e)gTLD主域名服务器f)gTLD主域名服务器...

    TCP/IP技术大全(中文PDF非扫描版)

    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...

    TCP/IP教程TCP/IP基础

    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...

    TCP-IP技术大全

    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...

    TCP/IP技术大全

    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...

    TCP/IP详解

    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...

    TCPIP协议详解(4-1)

    名字服务器 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 ...

    asp.net知识库

    一个时间转换的问题,顺便谈谈搜索技巧 .net中的正则表达式使用高级技巧 (一) C#静态成员和方法的学习小结 C#中结构与类的区别 C#中 const 和 readonly 的区别 利用自定义属性,定义枚举值的详细文本 Web标准和ASP...

    低清版 大型门户网站是这样炼成的.pdf

    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 门户网站开发...

Global site tag (gtag.js) - Google Analytics