111

我们使用 GSLB 进行地理分布和负载平衡。每个服务都分配了一个固定的域名。通过一些 DNS 魔术,域名被解析为最接近负载最少的服务器的 IP。为了使负载均衡发挥作用,应用服务器需要遵守来自 DNS 响应的 TTL,并在缓存超时时重新解析域名。但是,我想不出在 Java 中执行此操作的方法。

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

4

7 回答 7

87

根据 Byron 的回答,您不能通过使用标志或调用来设置networkaddress.cache.ttl或作为系统属性,因为这些不是系统属性 - 它们是安全属性。networkaddress.cache.negative.ttl-DSystem.setProperty

如果您想使用 System 属性来触发此行为(因此您可以使用-Dflag 或 call System.setProperty),您将需要设置以下System属性:

-Dsun.net.inetaddr.ttl=0

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

但请注意:如果您在启动 JVM 进程时不使用该-D标志并选择从代码中调用它:

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 欺骗攻击时才执行此操作。

于 2013-06-20T16:50:24.573 回答
73

Java 有一些非常奇怪的 dns 缓存行为。最好的办法是关闭 dns 缓存或将其设置为 5 秒之类的低值。

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

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

于 2009-08-10T19:00:44.893 回答
22

这显然已在较新的版本(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();
        }
    }
}
于 2013-03-07T21:24:28.470 回答
19

要扩展拜伦的答案,我相信您需要编辑目录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文档here

于 2009-08-10T19:33:05.437 回答
8

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

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

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

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

IBM 在此处有一篇关于 WebSphere 如何处理 DNS 缓存的非常详细的文章。与上述相关的部分是:

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

于 2015-06-03T21:12:37.790 回答
2

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

于 2017-10-07T23:44:22.587 回答
1

所以我决定看一下java源代码,因为我发现官方文档有点混乱。我发现的(对于 OpenJDK 11)大多与其他人写的一致。重要的是属性评估的顺序。

InetAddressCachePolicy.java(为了便于阅读,我省略了一些样板文件):


String tmpString = Security.getProperty("networkaddress.cache.ttl");
if (tmpString != null) {
   tmp = Integer.valueOf(tmpString);
   return;
}
...
String tmpString = System.getProperty("sun.net.inetaddr.ttl");
if (tmpString != null) {
   tmp = Integer.valueOf(tmpString);
   return;
}
...
if (tmp != null) {
  cachePolicy = tmp < 0 ? FOREVER : tmp;
  propertySet = true;
} else {
  /* No properties defined for positive caching. If there is no
  * security manager then use the default positive cache value.
  */
  if (System.getSecurityManager() == null) {
    cachePolicy = 30;
  }
}

您可以清楚地看到首先评估安全属性,然后评估系统属性,如果它们中的任何一个被设置,则cachePolicy值被设置为该数字,或者-1 (FOREVER)它们是否持有低于 -1 的值。如果未设置任何内容,则默认为 30 秒。事实证明,OpenJDK 几乎总是如此,因为默认情况下java.security不设置该值,只有一个负值。

#networkaddress.cache.ttl=-1 <- this line is commented out
networkaddress.cache.negative.ttl=10

顺便说一句,如果networkaddress.cache.negative.ttl未设置(从文件中删除),则 java 类中的默认值为 0。这方面的文档是错误的。这就是让我绊倒的原因。

于 2021-02-07T12:28:13.987 回答