在通过DNS查找域名的过程中可能会经过多台中间DNS服务器才能找到指定的域名因此在DNS服务器上查找域名是非常昂贵的操作在Java中为了缓解这个问题提供了DNS缓存当InetAddress类第一次使用某个域名(如)创建InetAddress对象后JVM就会将这个域名和它从DNS上获得的信息(如IP地址)都保存在DNS缓存中当下一次InetAddress类再使用这个域名时就直接从DNS缓存里获得所需的信息而无需再访问DNS服务器
DNS缓存在默认时将永远保留曾经访问过的域名信息但我们可以修改这个默认值一般有两种方法可以修改这个默认值
在程序中通过javasecuritySecuritysetProperty方法设置安全属性netwl的值(单位秒)如下面的代码将缓存超时设为秒
javasecuritySecuritysetProperty(netwl);
设置javasecurity文件中的networkaddresl属性假设JDK的安装目录是C\jdk那么javasecurity文件位于c\jdk\jre\lib\security目录中打开这个文件找到netwl属性并将这个属性值设为相应的缓存超时(单位秒)
如果将netwl属性值设为那么DNS缓存数据将永远不会释放下面的代码演示了使用和不使用DNS缓存所产生效果
package mynet;
import*;
publicclass MyDNS
{
publicstaticvoidmain(String[]args)throwsException
{
//args[]:本机名args[]缓沖时间
if(argslength<)
return;
javasecuritySecuritysetProperty(netwlargs[]);
longtime=SystemcurrentTimeMillis();
InetAddressaddresses[]=InetAddressgetAllByName(args[]);
Systemoutprintln(addresses:
+StringvalueOf(SystemcurrentTimeMillis()time)
+毫秒);
for(InetAddressaddress:addresses)
Systemoutprintln(address);
Systemoutprint(按任意键继续);
Systeminread();
time=SystemcurrentTimeMillis();
InetAddressaddresses[]=InetAddressgetAllByName(args[]);
Systemoutprintln(addresses:
+StringvalueOf(SystemcurrentTimeMillis()time)
+毫秒);
for(InetAddressaddress:addresses)
Systemoutprintln(address);
}
}
在上面的代码中设置了DNS缓存超时(通过args[]参数)用户可以通过命令行参数将这个值传入MyDNS中这个程序首先使用getAllByName建立一个InetAddress数组然后通过Systeminread使程序暂停当用户等待一段时间后可以按任意键继续并使用同一个域名(args[])再建立一个InetAddress数组如果用户等待的这段时间比DNS缓存超时小那么无论情况如何变化addresses和addresses数组中的元素是一样的并且创建addresses数组所花费的时间一般为毫秒(小于毫秒后Java无法获得更精确的时间)
测试
执行如下命令(将DNS缓存超时设为秒)
java mynetMyDNS
运行结果(在秒之内按任意键)
addresses:毫秒
/
按任意键继续
addresses:毫秒
/
运行结果(在秒后按任意键)
addresses:毫秒
/
按任意键继续
addresses:毫秒
/
在上面的测试中可能出现两个运行结果如果在出现按任意键继续…后在秒之内按任意键继续后就会得到运行结果从这个结果可以看出addresses所用的时间为毫秒也就是说addresses并未真正访问DNS服务器而是直接从内存中的DNS缓存得到的数据当在秒后按任意键继续后就会得到运行结果这时内存中的DNS缓存中的数据已经释放所以addresses还得再访问DNS服务器因此addresses的时间是毫秒(addresses和addresses后面的毫秒数可能在不同的环境下的值不一样但一般情况下运行结果的addresses的值为或是一个接近的数如运行结果的addresses的值一般会和addresses的值很接近或是一个远比大的数如)
测试
执行如下命令(ComputerName为本机的计算机名DNS缓存超时设为永不过期[])
java mynetMyDNS ComputerName
运行结果(按任意键继续之前将删除)
addresses:毫秒
myuniverse/
myuniverse/
按任意键继续
addresses:毫秒
myuniverse/
myuniverse/
从上面的测试可以看出将DNS缓存设为永不过期后无论过多少时间按任意键后addresses任然得到了两个IP地址(和)而且addresses的时间是毫秒但在这时已经被删除因此可以判断addresses是从DNS缓存中得到的数据如果运行如下的命令并在秒后按任意键继续后addresses就会只剩下一个IP地址()
java mynetMyDNS ComputerName
如果域名在DNS服务器上不存在那么客户端在进行一段时间的尝试后(平均为秒)就会抛出一个UnknownHostException异常为了让下一次访问这个域名时不再等待DNS缓存将这个错误信息也保存了起来也就是说只有第一次访问错误域名时才进行称左右的尝试以后再访问这个域名时将直接抛出UnknownHostException异常而无需再等待秒钟
访问域名失败的原因可能是这个域名真的不存在也可能是因为DNS服务器或是其他的硬件或软件的临时故障因此一般不能将这个域名错误信息一直保留在Java中可以通过networkaddresl属性设置保留这些信息的时间这个属性的默认值是秒它也可以通过javasecuritySecuritysetProperty方法或javasecurity文件来设置下面的代码演示了networkaddresl属性的用法
package mynet;
import*;
publicclass MyDNS
{
publicstaticvoidmain(String[]args)throwsException
{
javasecuritySecuritysetProperty(networkaddresl
);
longtime=;
try
{
time=SystemcurrentTimeMillis();
InetAddressgetByName();
}
catch(Exceptione)
{
Systemoutprintln(不存在!address:
+StringvalueOf(SystemcurrentTimeMillis()time)
+毫秒);
}
//Threadsleep();//延迟秒
try
{
time=SystemcurrentTimeMillis();
InetAddressgetByName();
}
catch(Exceptione)
{
Systemoutprintln(不存在!address:
+StringvalueOf(SystemcurrentTimeMillis()time)
+毫秒);
}
}
}
在上面的代码中将networkaddresl属性值设为秒这个程序分别测试了address和address访问(这是个不存在的域名读者可以将其换成任何不存在的域名)后用了多长时间抛出UnknownHostException异常
运行结果
不存在!address:毫秒
不存在!address:毫秒
我们从上面的运行结果可以看出address使用了毫秒就抛出了异常因此可以断定address是从DNS缓存里获得了域名不可访问的信息所以就直接抛出了UnknowHostException异常如果将上面代码中的延迟代码的注释去掉那么可能得到如下的运行结果
不存在!address:毫秒
不存在!address:毫秒
从上面的运行结果可以看出在第秒时DNS缓存中的数据已经被释放因此address仍需要访问DNS服务器才能知道是不可访问的域名
在使用DNS缓存时有两点需要注意
可以根据实际情况来设置netwl属性的值一般将这个属性的值设为但如果访问的是动态映射的域名(如使用动态域名服务将域名映射成ADSL的动态IP) 就可能产生IP地址变化后客户端得到的还是原来的IP地址的情况
在设置networkaddresl属性值时最好不要将它设为否则如果一个域名因为暂时的故障而无法访问那么程序再次访问这个域名时即使这个域名恢复正常程序也无法再访问这个域名了除非重新运行程序