`
jialonely
  • 浏览: 19361 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

java 域名解析 inetAddress 缓存解决方案(非原创)

阅读更多

jdk的InetAddress有一个特性,就是当系统访问过一个域名的时候,InetAddress就会通过其私有变量addressCache把域名对应的ip地址缓存起来.
虽然缓存起来能极大的提高系统性能,但有时候会给系统带来很大的麻烦.例如,当对方改动了ip地址后,系统就不能再访问到新的ip地址了,这个时候最直接的方案就是:重启jvm!!!这对于需要7*24小时服务的系统来说,是不可忍受的.
下面一段代码可以重现这个现象(但需要你在运行的时候是在调试模式):
public void testDnsCachePolicy() throws Exception {
        InetAddress addr1 = InetAddress.getByName("www.baidu.com");
        System.out.println(addr1.getHostAddress());
        //在下一行设置断点.
        int i = 0;
        InetAddress addr2 = InetAddress.getByName("www.baidu.com");
        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     www.baidu.com"这行注释掉,"#64.233.189.104     www.baidu.com"
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("www.baidu.com");
        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("www.baidu.com");
        //修改缓存数据结束
        InetAddress addr2 = InetAddress.getByName("www.baidu.com");
        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();

 

转自:http://www.blogjava.net/jjwwhmm/archive/2008/07/09/213685.html

仅供参考

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics