天天看點

Java網絡程式設計從入門到精通(4):DNS緩存

本文為原創,如需轉載,請注明作者和出處,謝謝!

    在通過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("按任意鍵繼續

Java網絡程式設計從入門到精通(4):DNS緩存

");

        System.in.read();

        time = System.currentTimeMillis();

        InetAddress addresses2[] = InetAddress.getAllByName(args[0]);

        System.out.println("addresses2:   "

        for (InetAddress address : addresses2)

    }

}

    在上面的代碼中設定了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

按任意鍵繼續

Java網絡程式設計從入門到精通(4):DNS緩存

addresses2:  0毫秒

運作結果2(在5秒後按任意鍵):

Java網絡程式設計從入門到精通(4):DNS緩存

addresses2:  484毫秒

在上面的測試中可能出現兩個運作結果。如果在出現“按任意鍵繼續…”後,在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

Java網絡程式設計從入門到精通(4):DNS緩存

addresses2:   0毫秒

     從上面的測試可以看出,将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屬性的用法:

public class MyDNS1

        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秒

            System.out.println("www.ppp123.com不存在! address2: "

在上面的代碼中将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:  4420毫秒

從上面的運作結果可以看出,在第6秒時,DNS緩存中的資料已經被釋放,是以,address2仍需要通路DNS伺服器才能知道www.ppp123.com是不可通路的域名。

在使用DNS緩存時有兩點需要注意:

1. 可以根據實際情況來設定networkaddress.cache.ttl屬性的值。一般将這個屬性的值設為-1。但如果通路的是動态映射的域名(如使用動态域名服務将域名映射成ADSL的動态IP), 就可能産生IP位址變化後,用戶端得到的還是原來的IP位址的情況。

2. 在設定networkaddress.cache.negative.ttl屬性值時最好不要将它設為-1,否則如果一個域名因為暫時的故障而無法通路,那麼程式再次通路這個域名時,即使這個域名恢複正常,程式也無法再通路這個域名了。除非重新運作程式。

<a href="http://www.eoeandroid.com/forumdisplay.php?fid=4">國内最棒的Google Android技術社群(eoeandroid),歡迎通路!</a>

繼續閱讀