心有多大舞臺便有多大

          Embrace changes, pursue excellence, share niceness.

          java InetAddress 的dns cache問題

          jdk的InetAddress有一個特性,就是當系統訪問過一個域名的時候,InetAddress就會通過其私有變量addressCache把域名對應的ip地址緩存起來.
          雖然緩存起來能極大的提高系統性能,但有時候會給系統帶來很大的麻煩.例如,當對方改動了ip地址后,系統就不能再訪問到新的ip地址了,這個時候最直接的方案就是:重啟jvm!!!這對于需要7*24小時服務的系統來說,是不可忍受的.
          下面一段代碼可以重現這個現象(但需要你在運行的時候是在調試模式):
          public void testDnsCachePolicy() throws Exception {
                  InetAddress addr1 = InetAddress.getByName("         System.out.println(addr1.getHostAddress());
                  //在下一行設置斷點.
                  int i = 0;
                  InetAddress addr2 = InetAddress.getByName("
                  System.out.println(addr2.getHostAddress());
          }
          具體測試方式是:
          1.修改c:/windows/system32/drivers/etc/hosts文件,在文件末尾加入:64.233.189.104    
          www.baidu.com
          這個ip地址是google的ip
          2.運行代碼到斷點處
          這時候打印出的ip地址是64.233.189.104
          3.修改hosts文件,把"64.233.189.104     4.繼續運行代碼到結束
          這時候打印出的ip地址還是64.233.189.104,并沒有更改為baidu的ip地址.

          那么應該怎么來解決這個問題呢?
          查了下網上的解決方案,一般是在啟動jvm的時候,指定jvm參數:networkaddress.cache.ttl和networkaddress.cache.negative.ttl,具體的含義你可以查看InetAddress的源代碼.
          這種方法的缺點是在JVM啟動的時候就固定了dns的緩存策略.如果不緩存的話,對系統性能的影響是很大的,那么能不能動態的修改這個緩存的值呢?
          正好前段時間寫了篇文章:怎么通過反射修改類的私有字段值.正好有了用武之地!
          下面是測試代碼:
          //方法中的字符串常量policy,cache,addressCache請參考InetAddress源代碼.
          public void testDnsCachePolicy() throws Exception {
                  InetAddress addr1 = InetAddress.getByName("
                  System.out.println(addr1.getHostAddress());
                   //在下一行設置斷點.
                  int i = 0;
                  //修改緩存數據開始
                  Class inetAddressClass = java.net.InetAddress.class;       
                  final Field cacheField = inetAddressClass.getDeclaredField("addressCache");       
                  cacheField.setAccessible(true);       
                  final Object obj = cacheField.get(inetAddressClass);       
                  Class cacheClazz = obj.getClass();       
                  final Field cachePolicyField = cacheClazz.getDeclaredField("policy");       
                  final Field cacheMapField = cacheClazz.getDeclaredField("cache");       
                  cachePolicyField.setAccessible(true);
                  cacheMapField.setAccessible(true);    
                  final Map cacheMap = (Map)cacheMapField.get(obj);
                  cacheMap.remove("
                  //修改緩存數據結束
                  InetAddress addr2 = InetAddress.getByName("
                  System.out.println(addr2.getHostAddress());
          }
          重新按照上面的測試方法測試一次,第2次已經能夠拿到正確的ip地址了.

          如果在用apache的httpclient,那么,在把緩存中的數據清除后,需要重新創建GetMethod/PostMethod對象.
          例如:
          HttpClient client = new HttpClient();
          GetMethod m1 = new GetMethod("http://www.baidu.com");
          client.executeMethod(m1);
          String content = m1.getResponseBodyAsString();
          ........//通過上面的反射方法清楚緩存
          //重新執行m1,仍然不會得到正確的結果
          client.executeMethod(m1);
          String content = m1.getResponseBodyAsString();
          //重新創建GetMethod,才能得到正確的結果
          GetMethod m2 = new GetMethod("http://www.baidu.com");
          client.executeMethod(m2);
          content = m2.getResponseBodyAsString();

          posted on 2008-07-09 15:57 pony 閱讀(4718) 評論(3)  編輯  收藏 所屬分類: Java

          評論

          # re: java InetAddress 的dns cache問題 2008-12-24 16:36 路人

          太感謝樓主了,正好遇到這個問題  回復  更多評論   

          # re: java InetAddress 的dns cache問題 2011-06-29 19:55 private250

          樓主高人!向你致敬!  回復  更多評論   

          # re: java InetAddress 的dns cache問題 2015-04-24 22:35 Jerry Lee

          寫了操作Java DNS Cache的庫 https://github.com/alibaba/java-dns-cache-manipulator ,可以方便些設置/重置/清空JVM DNS,支持 JDK 6 7 8??梢栽囋?~   回復  更多評論   

          主站蜘蛛池模板: 松桃| 禹城市| 治多县| 化州市| 中西区| 麦盖提县| 涟水县| 山西省| 余姚市| 通州区| 枝江市| 伊川县| 荣成市| 庄河市| 浮山县| 辉南县| 山东| 特克斯县| 汉寿县| 浦江县| 西吉县| 秭归县| 隆子县| 庆城县| 武夷山市| 昌江| 常德市| 长武县| 梁山县| 张家界市| 温泉县| 锡林郭勒盟| 文安县| 克东县| 莱芜市| 曲沃县| 嘉黎县| 临澧县| 柘荣县| 临漳县| 若羌县|