java 域名解析 inetAddress 缓存解决方案(非原创)
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 这个ip地址是google的ip 2.运行代码到断点处 这时候打印出的ip地址是64.233.189.104 3.修改hosts文件,把"64.233.189.104 "这行注释掉,"#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();
仅供参考