任何使Java荣获DNScaching超时(TTL)的方法?

我们使用GSLB进行地理分布和负载平衡。 每个服务都被分配一个固定的域名。 通过一些DNS魔术,域名parsing成最接近服务器的IP,负载最轻。 为了使负载平衡起作用,应用服务器需要遵守DNS响应中的TTL,并在caching超时时再次parsing域名。 但是,我无法想出一个在Java中这样做的方法。

该应用程序在Java 5上运行,在Linux(Centos 5)上运行。

Java有一些严重奇怪的DNScaching行为。 你最好的select是closuresDNScaching或将其设置为5秒钟的低数字。

networkaddress.cache.ttl(默认值:-1)
指示名称服务成功进行名称查找的caching策略。 该值指定为整数,以指示caching成功查找的秒数。 值为-1表示“永远caching”。

networkaddress.cache.negative.ttl(默认值:10)
指出名称服务中不成功的名称查找的caching策略。 该值以整数forms指定,以指示caching未成功查找失败的秒数。 值为0表示“永不caching”。 值为-1表示“永远caching”。

根据Byron的回答,您不能通过使用-D标志或调用System.setPropertynetworkaddress.cache.ttlnetworkaddress.cache.negative.ttl设置为系统属性,因为它们不是系统属性 – 它们是安全性属性。

如果你想使用一个System属性触发这个行为(所以你可以使用-D标志或调用System.setProperty ),你将需要设置下面的System属性:

 -Dsun.net.inetaddr.ttl=0 

这个系统属性将启用所需的效果。

但请注意:如果您在启动JVM进程时不使用-D标志,而是select从代码中调用此标志:

 java.security.Security.setProperty("networkaddress.cache.ttl" , "0") 

此代码必须在JVM中的任何其他代码尝试执行联网操作之前执行。

这很重要,例如,如果在.war文件中调用了Security.setProperty并将.war部署到Tomcat,则这将不起作用:Tomcat使用Javanetworking堆栈来初始化自己,远远早于.war的代码执行。 由于这种“竞争条件”,启动JVM进程时使用-D标志通常更方便。

如果您不使用-Dsun.net.inetaddr.ttl=0或调用Security.setProperty ,则需要编辑$JRE_HOME/lib/security/java.security并在该文件中设置这些安全属性,例如

 networkaddress.cache.ttl = 0 networkaddress.cache.negative.ttl = 0 

但要注意围绕这些属性的注释中的安全警告。 只有在有理由相信您不会受到DNS欺骗攻击的情况下才能这样做。

新版本(SE 6和SE 7)显然已经修复了这个问题。 运行下面的代码片段时,我使用tcpdump观察端口53的活动时,我经历了30秒的caching时间最大值。

 /** * http://stackoverflow.com/questions/1256556/any-way-to-make-java-honor-the-dns-caching-timeout-ttl * * Result: Java 6 distributed with Ubuntu 12.04 and Java 7 u15 downloaded from Oracle have * an expiry time for dns lookups of approx. 30 seconds. */ import java.util.*; import java.text.*; import java.security.*; import java.net.InetAddress; import java.net.UnknownHostException; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; public class Test { final static String hostname = "www.google.com"; public static void main(String[] args) { // only required for Java SE 5 and lower: //Security.setProperty("networkaddress.cache.ttl", "30"); System.out.println(Security.getProperty("networkaddress.cache.ttl")); System.out.println(System.getProperty("networkaddress.cache.ttl")); System.out.println(Security.getProperty("networkaddress.cache.negative.ttl")); System.out.println(System.getProperty("networkaddress.cache.negative.ttl")); while(true) { int i = 0; try { makeRequest(); InetAddress inetAddress = InetAddress.getLocalHost(); System.out.println(new Date()); inetAddress = InetAddress.getByName(hostname); displayStuff(hostname, inetAddress); } catch (UnknownHostException e) { e.printStackTrace(); } try { Thread.sleep(5L*1000L); } catch(Exception ex) {} i++; } } public static void displayStuff(String whichHost, InetAddress inetAddress) { System.out.println("Which Host:" + whichHost); System.out.println("Canonical Host Name:" + inetAddress.getCanonicalHostName()); System.out.println("Host Name:" + inetAddress.getHostName()); System.out.println("Host Address:" + inetAddress.getHostAddress()); } public static void makeRequest() { try { URL url = new URL("http://"+hostname+"/"); URLConnection conn = url.openConnection(); conn.connect(); InputStream is = conn.getInputStream(); InputStreamReader ird = new InputStreamReader(is); BufferedReader rd = new BufferedReader(ird); String res; while((res = rd.readLine()) != null) { System.out.println(res); break; } rd.close(); } catch(Exception ex) { ex.printStackTrace(); } } } 

为了扩展Byron的答案,我相信你需要编辑%JRE_HOME%\lib\security目录中的文件java.security来实现这个改变。

这里是相关的部分:

 # # The Java-level namelookup cache policy for successful lookups: # # any negative value: caching forever # any positive value: the number of seconds to cache an address for # zero: do not cache # # default value is forever (FOREVER). For security reasons, this # caching is made forever when a security manager is set. When a security # manager is not set, the default behavior is to cache for 30 seconds. # # NOTE: setting this to anything other than the default value can have # serious security implications. Do not set it unless # you are sure you are not exposed to DNS spoofing attack. # #networkaddress.cache.ttl=-1 

有关java.security文件的文档。

总结其他答案,在<jre-path>/lib/security/java.security ,可以设置属性networkaddress.cache.ttl的值来调整DNS查找的caching方式。 请注意,这不是一个系统属性,而是一个安全属性。 我能够使用以下设置:

 java.security.Security.setProperty("networkaddress.cache.ttl", "<value>"); 

这也可以通过系统属性-Dsun.net.inetaddr.ttl来设置,尽pipe如果它被设置在别的地方,这将不会覆盖安全属性。

我还想补充一点,如果您在WebSphere中看到了Web服务的这个问题,那么设置networkaddress.cache.ttl将是不够的。 您需要将系统属性disableWSAddressCachingtrue 。 与time-to-live属性不同,这可以设置为JVM参数或通过System.setProperty )。

IBM在这里介绍了如何处理DNScaching的相当详细的文章。 上面的相关部分是:

要禁用Web服务的地址caching,您需要将其他JVM自定义属性disableWSAddressCaching设置为true。 使用此属性可禁用Web服务的地址caching。 如果系统通常运行大量客户端线程,并且在wsAddrCachecaching上遇到locking争用,则可以将此自定义属性设置为true,以防止cachingWeb服务数据。

根据官方的oracle java属性 , sun.net.inetaddr.ttl是Sun特定实现的属性,“未来版本可能不支持”。 “首选的方法是使用安全属性” networkaddress.cache.ttl