jdk的InetAddress有一個(gè)特性,就是當(dāng)系統(tǒng)訪問過一個(gè)域名的時(shí)候,InetAddress就會通過其私有變量addressCache把域名對應(yīng)的ip地址緩存起來.
雖然緩存起來能極大的提高系統(tǒng)性能,但有時(shí)候會給系統(tǒng)帶來很大的麻煩.例如,當(dāng)對方改動了ip地址后,系統(tǒng)就不能再訪問到新的ip地址了,這個(gè)時(shí)候最直接的方案就是:重啟jvm!!!這對于需要7*24小時(shí)服務(wù)的系統(tǒng)來說,是不可忍受的.
下面一段代碼可以重現(xiàn)這個(gè)現(xiàn)象(但需要你在運(yùn)行的時(shí)候是在調(diào)試模式):
public void testDnsCachePolicy() throws Exception {
InetAddress addr1 = InetAddress.getByName("
System.out.println(addr1.getHostAddress());
//在下一行設(shè)置斷點(diǎn).
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
這個(gè)ip地址是google的ip
2.運(yùn)行代碼到斷點(diǎn)處
這時(shí)候打印出的ip地址是64.233.189.104
3.修改hosts文件,把"64.233.189.104
4.繼續(xù)運(yùn)行代碼到結(jié)束
這時(shí)候打印出的ip地址還是64.233.189.104,并沒有更改為baidu的ip地址.
那么應(yīng)該怎么來解決這個(gè)問題呢?
查了下網(wǎng)上的解決方案,一般是在啟動jvm的時(shí)候,指定jvm參數(shù):networkaddress.cache.ttl和networkaddress.cache.negative.ttl,具體的含義你可以查看InetAddress的源代碼.
這種方法的缺點(diǎn)是在JVM啟動的時(shí)候就固定了dns的緩存策略.如果不緩存的話,對系統(tǒng)性能的影響是很大的,那么能不能動態(tài)的修改這個(gè)緩存的值呢?
正好前段時(shí)間寫了篇文章:怎么通過反射修改類的私有字段值.正好有了用武之地!
下面是測試代碼:
//方法中的字符串常量policy,cache,addressCache請參考InetAddress源代碼.
public void testDnsCachePolicy() throws Exception {
InetAddress addr1 = InetAddress.getByName("
System.out.println(addr1.getHostAddress());
//在下一行設(shè)置斷點(diǎn).
int i = 0;
//修改緩存數(shù)據(jù)開始
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("
//修改緩存數(shù)據(jù)結(jié)束
InetAddress addr2 = InetAddress.getByName("
System.out.println(addr2.getHostAddress());
}
重新按照上面的測試方法測試一次,第2次已經(jīng)能夠拿到正確的ip地址了.
如果在用apache的httpclient,那么,在把緩存中的數(shù)據(jù)清除后,需要重新創(chuàng)建GetMethod/PostMethod對象.
例如:
HttpClient client = new HttpClient();
GetMethod m1 = new GetMethod("http://www.baidu.com");
client.executeMethod(m1);
String content = m1.getResponseBodyAsString();
........//通過上面的反射方法清楚緩存
//重新執(zhí)行m1,仍然不會得到正確的結(jié)果
client.executeMethod(m1);
String content = m1.getResponseBodyAsString();
//重新創(chuàng)建GetMethod,才能得到正確的結(jié)果
GetMethod m2 = new GetMethod("http://www.baidu.com");
client.executeMethod(m2);
content = m2.getResponseBodyAsString();