当前位置:  开发笔记 > 编程语言 > 正文

如何让Java尊重DNS缓存超时?

如何解决《如何让Java尊重DNS缓存超时?》经验,为你挑选了5个好方法。

我们使用GSLB进行地理分布和负载平衡.为每项服务分配一个固定的域名.通过一些DNS魔术,域名被解析为最接近服务器且负载最小的IP.为了使负载平衡起作用,应用程序服务器需要遵守DNS响应中的TTL,并在缓存超时时再次解析域名.但是,我无法想出用Java做到这一点的方法.

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



1> Byron Whitlo..:

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


您如何知道是否安装了安全管理器?
注意:这不会禁用操作系统中的所有DNS缓存.只是禁用Java在库中自己破碎的内存中缓存.您可以在调用JVM时在命令行上简单地设置这些属性.
该文档在1.7中略有不同.具体来说,现在只有在安全管理器存在时才会永远发生缓存:"默认行为是在安装安全管理器时永远缓存,并在未安装安全管理器时缓存特定于实现的时间段." http://docs.oracle.com/javase/7/docs/technotes/guides/net/properties.html
请注意,您不能将它们设置为系统属性(即使用-D标志或System.setProperty),因为它们不是系统属性 - 它们是安全属性.
我不知道"破碎"是有效的.Java(出于安全原因)永远缓存DNS条目,或者直到JVM重新启动,以先到者为准.这(据我所知)是设计的.可以在java.security策略文件中或在命令行中进行设置.每个设置都不同.参考:http://www.rgagnon.com/javadetails/java-0445.html

2> Les Hazlewoo..:

Per Byron的答案是,您不能使用标志或调用来设置networkaddress.cache.ttlnetworkaddress.cache.negative.ttl作为系统属性,因为这些不是系统属性 - 它们是安全属性.-DSystem.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欺骗攻击的情况下才能执行此操作.


系统属性是否也可以与OpenJDK一起使用,或者它是否特定于Oracle?
FQN是`java.security.Security`(至少在jdk7中)

3> user1050755..:

这显然已在新版本(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();
        }
    }
}


是的,Java 1.5的默认值为无限缓存.Java 1.6和1.7默认值为30秒.
1.7的文档表明,只有在安全管理器不存在的情况下才会出现这种情况:"默认行为是在安装安全管理器时永远缓存,并在安全性时缓存特定于实现的时间段经理没有安装." http://docs.oracle.com/javase/7/docs/technotes/guides/net/properties.html
@rustyx Oracle的1.6和1.7 JDK在jad/lib/security/java.security中为networkaddress.cache.ttl提供了这个:"#default value is forever(FOREVER).出于安全考虑,这个#caching是永久性的,当安全管理器如果未设置安全性#Manager,则默认行为是缓存30秒." 因此,通过Java Web Start部署的applet和应用程序仍会永久缓存,否则为30秒.

4> matt b..:

为了扩展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文件的文档.


要添加到此,当使用tomcat6时,我必须修改我的lib/security文件,因为以编程方式或通过JAVA_OPTS变量设置networkaddress.cache.ttl或sun.net.inetaddr.ttl不起作用.

5> thesquaregro..:

要总结其他答案,/lib/security/java.security您可以设置属性的值networkaddress.cache.ttl以调整DNS查找的缓存方式.请注意,这不是系统属性,而是安全属性.我能够使用以下方式设置:

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

这也可以由系统属性设置,-Dsun.net.inetaddr.ttl但如果在其他地方设置,则不会覆盖安全属性.

我还想补充一点,如果您在WebSphere中看到Web服务的这个问题,就像我一样,设置networkaddress.cache.ttl是不够的.您需要将系统属性设置disableWSAddressCachingtrue.与生存时间属性不同,可以将其设置为JVM参数或通过System.setProperty).

IBM在此处有关于WebSphere如何处理DNS缓存的非常详细的文章.以上相关内容是:

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

推荐阅读
拾味湖
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有