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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:
在通过 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 缓存超时小,那么无论情况如何变化,addresses2 addresses1 数组中的元素是一样的,并且创建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 毫秒(addresses1 addresses2 后面的毫秒数可能在不同的环境下的值不一样,但一般情况下,运行结果1 addresses2 的值为0 或是一个接近0 的数,如5 。运行结果2 addresses2 的值一般会和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.10 192.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 秒。这个程序分别测试了address1 address2 访问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 否则如果一个域名因为暂时的故障而无法访问,那么程序再次访问这个域名时,即使这个域名恢复正常,程序也无法再访问这个域名了。除非重新运行程序。



 本文转自 androidguy 51CTO博客,原文链接:http://blog.51cto.com/androidguy/214814,如需转载请自行联系原作者

相关文章
|
2月前
|
缓存 JavaScript 前端开发
Java 如何确保 JS 不被缓存
【10月更文挑战第19天】在 Java 中,可以通过设置 HTTP 响应头来确保 JavaScript 文件不被浏览器缓存。方法包括:1. 使用 Servlet 设置响应头,通过 `doGet` 方法设置 `Expires`、`Cache-Control` 和 `Pragma` 头;2. 在 Spring Boot 中配置拦截器,通过 `NoCacheInterceptor` 类和 `WebConfig` 配置类实现相同功能。这两种方法都能确保每次请求都能获取到最新的 JavaScript 内容。
|
16天前
|
机器学习/深度学习 资源调度 算法
图卷积网络入门:数学基础与架构设计
本文系统地阐述了图卷积网络的架构原理。通过简化数学表述并聚焦于矩阵运算的核心概念,详细解析了GCN的工作机制。
42 3
图卷积网络入门:数学基础与架构设计
|
6天前
|
Web App开发 网络协议 安全
网络编程懒人入门(十六):手把手教你使用网络编程抓包神器Wireshark
Wireshark是一款开源和跨平台的抓包工具。它通过调用操作系统底层的API,直接捕获网卡上的数据包,因此捕获的数据包详细、功能强大。但Wireshark本身稍显复杂,本文将以用抓包实例,手把手带你一步步用好Wireshark,并真正理解抓到的数据包的各项含义。
39 2
|
12天前
|
机器学习/深度学习 人工智能 算法
深度学习入门:用Python构建你的第一个神经网络
在人工智能的海洋中,深度学习是那艘能够带你远航的船。本文将作为你的航标,引导你搭建第一个神经网络模型,让你领略深度学习的魅力。通过简单直观的语言和实例,我们将一起探索隐藏在数据背后的模式,体验从零开始创造智能系统的快感。准备好了吗?让我们启航吧!
39 3
|
20天前
|
数据采集 XML 存储
构建高效的Python网络爬虫:从入门到实践
本文旨在通过深入浅出的方式,引导读者从零开始构建一个高效的Python网络爬虫。我们将探索爬虫的基本原理、核心组件以及如何利用Python的强大库进行数据抓取和处理。文章不仅提供理论指导,还结合实战案例,让读者能够快速掌握爬虫技术,并应用于实际项目中。无论你是编程新手还是有一定基础的开发者,都能在这篇文章中找到有价值的内容。
|
1月前
|
存储 缓存 网络协议
如何防止DNS缓存中毒攻击(一)
DNS缓存中毒也称为DNS欺骗
48 10
|
1月前
|
缓存 网络协议 安全
如何防止DNS缓存中毒(Ⅱ)
服务器应该配置为尽可能少地依赖与其他DNS服务器的信任关系
43 10
|
27天前
|
机器学习/深度学习 人工智能 算法框架/工具
深度学习中的卷积神经网络(CNN)入门
【10月更文挑战第41天】在人工智能的璀璨星空下,卷积神经网络(CNN)如一颗耀眼的新星,照亮了图像处理和视觉识别的路径。本文将深入浅出地介绍CNN的基本概念、核心结构和工作原理,同时提供代码示例,带领初学者轻松步入这一神秘而又充满无限可能的领域。
|
1月前
|
消息中间件 编解码 网络协议
Netty从入门到精通:高性能网络编程的进阶之路
【11月更文挑战第17天】Netty是一个基于Java NIO(Non-blocking I/O)的高性能、异步事件驱动的网络应用框架。使用Netty,开发者可以快速、高效地开发可扩展的网络服务器和客户端程序。本文将带您从Netty的背景、业务场景、功能点、解决问题的关键、底层原理实现,到编写一个详细的Java示例,全面了解Netty,帮助您从入门到精通。
104 0
|
1月前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。

相关产品

  • 云解析DNS
  • 推荐镜像

    更多