随笔 - 312, 文章 - 14, 评论 - 1393, 引用 - 0
数据加载中……

Java网络编程从入门到精通(4):DNS缓存

本文为原创,如需转载,请注明作者和出处,谢谢!

上一篇:Java网络编程从入门到精通(3):为什么不能直接通过IP访问网站

    在通过DNS查找域名的过程中,可能会经过多台中间DNS服务器才能找到指定的域名,因此,在DNS服务器上查找域名是非常昂贵的操作。在Java中为了缓解这个问题,提供了DNS缓存。当InetAddress类第一次使用某个域名(如www.csdn.net)创建InetAddress对象后,JVM就会将这个域名和它从DNS上获得的信息(如IP地址)都保存在DNS缓存中。当下一次InetAddress类再使用这个域名时,就直接从DNS缓存里获得所需的信息,而无需再访问DNS服务器。

DNS缓存在默认时将永远保留曾经访问过的域名信息,但我们可以修改这个默认值。一般有两种方法可以修改这个默认值:

1. 在程序中通过java.security.Security.setProperty方法设置安全属性networkaddress.cache.ttl的值(单位:秒)。如下面的代码将缓存超时设为10秒:

java.security.Security.setProperty("networkaddress.cache.ttl"10);

2. 设置java.security文件中的networkaddress.cache.negative.ttl属性。假设JDK的安装目录是C:\jdk1.6,那么java.security文件位于c:\jdk1.6\jre\lib\security目录中。打开这个文件,找到networkaddress.cache.ttl属性,并将这个属性值设为相应的缓存超时(单位:秒)。

    如果将networkaddress.cache.ttl属性值设为-1,那么DNS缓存数据将永远不会释放。下面的代码演示了使用和不使用DNS缓存所产生效果:

package mynet;

import java.net.*;

public class MyDNS
{
    
public static void main(String[] args) throws Exception
    {
        
// args[0]: 本机名 args[1]:缓冲时间
        if (args.length < 2)
            
return;
        java.security.Security.setProperty(
"networkaddress.cache.ttl", args[1]);
        
long time = System.currentTimeMillis();
        InetAddress addresses1[] 
= InetAddress.getAllByName(args[0]);
        System.out.println(
"addresses1:   "
                        
+ String.valueOf(System.currentTimeMillis() - time)
                        
+ "毫秒");
        
for (InetAddress address : addresses1)
            System.out.println(address);
        System.out.print(
"按任意键继续");
        System.in.read();
        time 
= System.currentTimeMillis();
        InetAddress addresses2[] 
= InetAddress.getAllByName(args[0]);
        System.out.println(
"addresses2:   "
                        
+ String.valueOf(System.currentTimeMillis() - time)
                        
+ "毫秒");
        
for (InetAddress address : addresses2)
            System.out.println(address);
    }
}

    在上面的代码中设置了DNS缓存超时(通过args[1]参数),用户可以通过命令行参数将这个值传入MyDNS中。这个程序首先使用getAllByName建立一个InetAddress数组,然后通过System.in.read使程序暂停。当用户等待一段时间后,可以按任意键继续,并使用同一个域名(args[0])再建立一个InetAddress数组。如果用户等待的这段时间比DNS缓存超时小,那么无论情况如何变化,addresses2addresses1数组中的元素是一样的,并且创建addresses2数组所花费的时间一般为0毫秒(小于1毫秒后,Java无法获得更精确的时间)。

   测试1

执行如下命令(将DNS缓存超时设为5秒):

java mynet.MyDNS www.126.com 5

运行结果1(在5秒之内按任意键):

addresses1:   344毫秒
www
.126.com/202.108.9.77
按任意键继续
addresses2:  0毫秒
www
.126.com/202.108.9.77

运行结果2(在5秒后按任意键):

addresses1:   344毫秒
www
.126.com/202.108.9.77
按任意键继续
addresses2:  484毫秒
www
.126.com/202.108.9.77

在上面的测试中可能出现两个运行结果。如果在出现按任意键继续…”后,在5秒之内按任意键继续后,就会得到运行结果1,从这个结果可以看出,addresses2所用的时间为0毫秒,也就是说,addresses2并未真正访问DNS服务器,而是直接从内存中的DNS缓存得到的数据。当在5秒后按任意键继续后,就会得到运行结果2,这时,内存中的DNS缓存中的数据已经释放,所以addresses2还得再访问DNS服务器,因此,addresses2的时间是484毫秒(addresses1addresses2后面的毫秒数可能在不同的环境下的值不一样,但一般情况下,运行结果1addresses2的值为0或是一个接近0的数,如5。运行结果2addresses2的值一般会和addresses1的值很接近,或是一个远比0大的数,如1200)。

测试2

执行如下命令(ComputerName为本机的计算机名,DNS缓存超时设为永不过期[-1]):

java mynet.MyDNS ComputerName -1

运行结果(按任意键继续之前,将192.168.18.20删除):

addresses1:   31毫秒
myuniverse/
192.168.18.10
myuniverse/
192.168.18.20
按任意键继续
addresses2:   0毫秒
myuniverse/
192.168.18.10
myuniverse/
192.168.18.20

     从上面的测试可以看出,将DNS缓存设为永不过期后,无论过多少时间,按任意键后,addresses2任然得到了两个IP地址(192.168.18.10192.168.18.20),而且addresses2的时间是0毫秒,但在这时192.168.18.20已经被删除。因此可以判断,addresses2是从DNS缓存中得到的数据。如果运行如下的命令,并在5秒后按任意键继续后,addresses2就会只剩下一个IP地址(192.168.18.10)。

java mynet.MyDNS ComputerName 5

如果域名在DNS服务器上不存在,那么客户端在进行一段时间的尝试后(平均为5秒),就会抛出一个UnknownHostException异常。为了让下一次访问这个域名时不再等待,DNS缓存将这个错误信息也保存了起来。也就是说,只有第一次访问错误域名时才进行5称左右的尝试,以后再访问这个域名时将直接抛出UnknownHostException异常,而无需再等待5秒钟,

访问域名失败的原因可能是这个域名真的不存在,也可能是因为DNS服务器或是其他的硬件或软件的临时故障,因此,一般不能将这个域名错误信息一直保留。在Java中可以通过networkaddress.cache.negative.ttl属性设置保留这些信息的时间。这个属性的默认值是10秒。它也可以通过java.security.Security.setProperty方法或java.security文件来设置。下面的代码演示了networkaddress.cache.negative.ttl属性的用法:

package mynet;

import java.net.*;

public class MyDNS1
{
    
public static void main(String[] args) throws Exception
    {
        java.security.Security.setProperty(
"networkaddress.cache.negative.ttl",
                        
"5");
        
long time = 0;
        
try
        {
            time 
= System.currentTimeMillis();
            InetAddress.getByName(
"www.ppp123.com");
        }
        
catch (Exception e)
        {
            System.out.println(
"www.ppp123.com不存在! address1: "
                            
+ String.valueOf(System.currentTimeMillis() - time)
                            
+ "毫秒");
        }
        
//Thread.sleep(6000); // 延迟6秒
        try
        {
            time 
= System.currentTimeMillis();
            InetAddress.getByName(
"www.ppp123.com");
        }
        
catch (Exception e)
        {
            System.out.println(
"www.ppp123.com不存在! address2: "
                            
+ String.valueOf(System.currentTimeMillis() - time)
                            
+ "毫秒");
        }
    }
}

在上面的代码中networkaddress.cache.negative.ttl属性值设为5秒。这个程序分别测试了address1address2访问www.ppp123.com(这是个不存在的域名,读者可以将其换成任何不存在的域名)后,用了多长时间抛出UnknownHostException异常。

运行结果:

www.ppp123.com不存在! address1:  4688毫秒
www.ppp123.com不存在! address2:  0毫秒

    我们从上面的运行结果可以看出,address2使用了0毫秒就抛出了异常,因此,可以断定address2是从DNS缓存里获得了域名www.ppp123.com不可访问的信息,所以就直接抛出了UnknowHostException异常。如果将上面代码中的延迟代码的注释去掉,那么可能得到如下的运行结果:

www.ppp123.com不存在! address1:  4688毫秒
www.ppp123.com不存在! address1:  4420毫秒

从上面的运行结果可以看出,在第6秒时,DNS缓存中的数据已经被释放,因此,address2仍需要访问DNS服务器才能知道www.ppp123.com是不可访问的域名。

在使用DNS缓存时有两点需要注意:

1. 可以根据实际情况来设置networkaddress.cache.ttl属性的值。一般将这个属性的值设为-1。但如果访问的是动态映射的域名(如使用动态域名服务将域名映射成ADSL的动态IP, 就可能产生IP地址变化后,客户端得到的还是原来的IP地址的情况。

2. 在设置networkaddress.cache.negative.ttl属性值时最好不要将它设为-1否则如果一个域名因为暂时的故障而无法访问,那么程序再次访问这个域名时,即使这个域名恢复正常,程序也无法再访问这个域名了。除非重新运行程序。

下一篇:Java网络编程从入门到精通(5):使用InetAddress类的getHostName方法获得域名





Android开发完全讲义(第2版)(本书版权已输出到台湾)

http://product.dangdang.com/product.aspx?product_id=22741502



Android高薪之路:Android程序员面试宝典 http://book.360buy.com/10970314.html


新浪微博:http://t.sina.com.cn/androidguy   昵称:李宁_Lining

posted on 2009-04-06 21:06 银河使者 阅读(4852) 评论(2)  编辑  收藏 所属分类: java 原创网络编程

评论

# re: Java网络编程从入门到精通(4):DNS缓存  回复  更多评论   

老师,你好。现在jdk1.7.0_06中,在security文件夹下,没有找到networkaddress.cache.negative.ttl属性,是因为位置变了么?
2014-06-13 10:58 | krave

# re: Java网络编程从入门到精通(4):DNS缓存  回复  更多评论   

找到了 找到了
就是java.security这个文件,打开以后就能设置了。
2014-06-13 11:30 | krave

只有注册用户登录后才能发表评论。


网站导航: