我们使用GSLB进行地理分布和负载平衡.为每项服务分配一个固定的域名.通过一些DNS魔术,域名被解析为最接近服务器且负载最小的IP.为了使负载平衡起作用,应用程序服务器需要遵守DNS响应中的TTL,并在缓存超时时再次解析域名.但是,我无法想出用Java做到这一点的方法.
该应用程序使用Java 5,在Linux(Centos 5)上运行.
Java有一些非常奇怪的dns缓存行为.最好的办法是关闭dns缓存或将其设置为5秒的低数字.
networkaddress.cache.ttl(默认值:-1)
表示从名称服务成功进行名称查找的缓存策略.该值指定为整数,以指示缓存成功查找的秒数.值-1表示"永远缓存".networkaddress.cache.negative.ttl(默认值:10)
表示来自名称服务的未成功名称查找的缓存策略.该值指定为整数,以指示缓存未成功查找失败的秒数.值0表示"从不缓存".值-1表示"永远缓存".
http://java.sun.com/j2se/1.4.2/docs/api/java/net/InetAddress.html
Per Byron的答案是,您不能使用标志或调用来设置networkaddress.cache.ttl
或networkaddress.cache.negative.ttl
作为系统属性,因为这些不是系统属性 - 它们是安全属性.-D
System.setProperty
如果要使用System属性来触发此行为(因此可以使用-D
标志或调用System.setProperty
),则需要设置以下System属性:
-Dsun.net.inetaddr.ttl=0
此系统属性将实现所需的效果.
但要注意:如果-D
在启动JVM进程时不使用该标志,则选择从代码中调用它:
java.security.Security.setProperty("networkaddress.cache.ttl" , "0")
此代码必须在JVM中的任何其他代码尝试执行网络操作之前执行.
这很重要,因为,例如,如果您调用Security.setProperty
.war文件并将.war部署到Tomcat,这将不起作用:Tomcat使用Java网络堆栈比执行.war的代码更早地初始化自身.由于这种"竞争条件",-D
在启动JVM进程时使用该标志通常更方便.
如果您不使用-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和7)中得到修复.我在使用tcpdump观察端口53活动时运行以下代码片段时遇到最长30秒的缓存时间.
/** * 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的答案,我认为您需要编辑目录java.security
中的%JRE_HOME%\lib\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
文件的文档.
要总结其他答案,
您可以设置属性的值networkaddress.cache.ttl
以调整DNS查找的缓存方式.请注意,这不是系统属性,而是安全属性.我能够使用以下方式设置:
java.security.Security.setProperty("networkaddress.cache.ttl", "");
这也可以由系统属性设置,-Dsun.net.inetaddr.ttl
但如果在其他地方设置,则不会覆盖安全属性.
我还想补充一点,如果您在WebSphere中看到Web服务的这个问题,就像我一样,设置networkaddress.cache.ttl
是不够的.您需要将系统属性设置disableWSAddressCaching
为true
.与生存时间属性不同,可以将其设置为JVM参数或通过System.setProperty
).
IBM在此处有关于WebSphere如何处理DNS缓存的非常详细的文章.以上相关内容是:
要禁用Web服务的地址缓存,需要将其他JVM自定义属性disableWSAddressCaching设置为true.使用此属性可禁用Web服务的地址缓存.如果您的系统通常运行大量客户端线程,并且您在wsAddrCache缓存上遇到锁争用,则可以将此自定义属性设置为true,以防止缓存Web服务数据.