如何让 Java 遵守 DNS 缓存超时?

发布于 2024-07-30 10:59:43 字数 219 浏览 2 评论 0 原文

我们使用 GSLB 进行地理分布和负载平衡。 每个服务都分配有一个固定的域名。 通过一些 DNS 魔法,域名会被解析为最接近负载最小的服务器的 IP。 为了使负载平衡发挥作用,应用程序服务器需要遵守 DNS 响应的 TTL,并在缓存超时时再次解析域名。 但是,我无法找到在 Java 中执行此操作的方法。

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

We use GSLB for geo-distribution and load-balancing. Each service is assigned a fixed domain name. Through some DNS magic, the domain name is resolved into an IP that's closest to the server with least load. For the load-balancing to work, the application server needs to honor the TTL from DNS response and to resolve the domain name again when cache times out. However, I couldn't figure out a way to do this in Java.

The application is in Java 5, running on Linux (Centos 5).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

于我来说 2024-08-06 10:59:43

根据 Byron 的回答,您无法使用 -Dnetworkaddress.cache.ttl 或 networkaddress.cache.male.ttl 设置为系统属性> 标志或调用 System.setProperty 因为这些不是系统属性 - 它们是安全属性。

如果您想使用系统属性来触发此行为(因此您可以使用 -D 标志或调用 System.setProperty),您将需要设置以下< em>系统属性:

-Dsun.net.inetaddr.ttl=0

此系统属性将启用所需的效果。 这里的“0”(零)表示在 JVM 级别禁用 DNS 缓存,因此将查询并使用来自 DNS 的原始 TTL 值。

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

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

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

这很重要,因为例如,如果您在 .war 文件中调用 Security.setProperty 并将该 .war 部署到 Tomcat,则这将不起作用:Tomcat 使用 Java 网络堆栈来初始化自身早于 .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 欺骗攻击时才执行此操作。

值得注意的是 Oracle JDK 8 文档推荐方法是使用networkaddress.cache.ttlnetworkaddress.cache.negative.ttl,如官方文档中所述。 这两个属性无法在命令行中设置,但应使用 setProperty() 或利用安全策略文件进行设置。

Per Byron's answer, you can't set networkaddress.cache.ttl or networkaddress.cache.negative.ttl as System Properties by using the -D flag or calling System.setProperty because these are not System properties - they are Security properties.

If you want to use a System property to trigger this behavior (so you can use the -D flag or call System.setProperty), you will want to set the following System property:

-Dsun.net.inetaddr.ttl=0

This system property will enable the desired effect. "0" (Zero) here means disable DNS caching at the JVM level, thus the original TTL value from the DNS will be consulted and used.

But be aware: if you don't use the -D flag when starting the JVM process and elect to call this from code instead:

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

This code must execute before any other code in the JVM attempts to perform networking operations.

This is important because, for example, if you called Security.setProperty in a .war file and deployed that .war to Tomcat, this wouldn't work: Tomcat uses the Java networking stack to initialize itself much earlier than your .war's code is executed. Because of this 'race condition', it is usually more convenient to use the -D flag when starting the JVM process.

If you don't use -Dsun.net.inetaddr.ttl=0 or call Security.setProperty, you will need to edit $JRE_HOME/lib/security/java.security and set those security properties in that file, e.g.

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

But pay attention to the security warnings in the comments surrounding those properties. Only do this if you are reasonably confident that you are not susceptible to DNS spoofing attacks.

It is worth noting that the Oracle JDK 8 docs recommended approach is to to use networkaddress.cache.ttl and networkaddress.cache.negative.ttl as already described in the official documentation here. These two properties cannot be set at the command line but should be set using setProperty() or utilize the security policy file.

原来分手还会想你 2024-08-06 10:59:43

Java 有一些非常奇怪的 dns 缓存行为。 最好的办法是关闭 dns 缓存或将其设置为某个较低的数字,例如 5 秒。

networkaddress.cache.ttl(默认值:-1)

指示从名称服务成功进行名称查找的缓存策略。 该值指定为整数,指示缓存成功查找的秒数。 值 -1 表示“永久缓存”。

networkaddress.cache.negative.ttl(默认值:10)

指示从名称服务中查找不成功的名称的缓存策略。 该值指定为整数,指示缓存不成功查找失败的秒数。 值 0 表示“从不缓存”。 值 -1 表示“永久缓存”。

Java has some seriously weird dns caching behavior. Your best bet is to turn off dns caching or set it to some low number like 5 seconds.

networkaddress.cache.ttl (default: -1)

Indicates the caching policy for successful name lookups from the name service. The value is specified as as integer to indicate the number of seconds to cache the successful lookup. A value of -1 indicates "cache forever".

networkaddress.cache.negative.ttl (default: 10)

Indicates the caching policy for un-successful name lookups from the name service. The value is specified as as integer to indicate the number of seconds to cache the failure for un-successful lookups. A value of 0 indicates "never cache". A value of -1 indicates "cache forever".

下壹個目標 2024-08-06 10:59:43

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

This has obviously been fixed in newer releases (SE 6 and 7). I experience a 30 second caching time max when running the following code snippet while watching port 53 activity using tcpdump.

/**
 * 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();
        }
    }
}
污味仙女 2024-08-06 10:59:43

为了扩展拜伦的答案,我相信您需要编辑 %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 文件 此处

To expand on Byron's answer, I believe you need to edit the file java.security in the %JRE_HOME%\lib\security directory to effect this change.

Here is the relevant section:

#
# 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 

Documentation on the java.security file here.

你的心境我的脸 2024-08-06 10:59:43

总结其他答案,在 /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 是不够的。 您需要将系统属性 disableWSAddressCaching 设置为 true。 与生存时间属性不同,它可以设置为 JVM 参数或通过 System.setProperty 进行设置。

IBM 在此处。 与上述相关的部分是:

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

To summarize the other answers, in <jre-path>/lib/security/java.security you can set the value of the property networkaddress.cache.ttl to adjust how DNS lookups are cached. Note that this is not a system property but a security property. I was able to set this using:

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

This can also be set by the system property -Dsun.net.inetaddr.ttl though this will not override a security property if it is set elsewhere.

I would also like to add that if you are seeing this issue with web services in WebSphere, as I was, setting networkaddress.cache.ttl will not be enough. You need to set the system property disableWSAddressCaching to true. Unlike the time-to-live property, this can be set as a JVM argument or via System.setProperty).

IBM has a pretty detailed post on how WebSphere handles DNS caching here. The relevant piece to the above is:

To disable address caching for Web services, you need to set an additional JVM custom property disableWSAddressCaching to true. Use this property to disable address caching for Web services. If your system typically runs with lots of client threads, and you encounter lock contention on the wsAddrCache cache, you can set this custom property to true, to prevent caching of the Web services data.

情仇皆在手 2024-08-06 10:59:43

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

According to the official oracle java properties, sun.net.inetaddr.ttl is Sun implementation-specific property, which "may not be supported in future releases". "the preferred way is to use the security property" networkaddress.cache.ttl.

迷荒 2024-08-06 10:59:43

所以我决定查看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.male.ttl(从文件中删除),则 java 类内的默认值为 0。文档在这方面是错误的。 这就是让我绊倒的原因。

So I decided to look at the java source code because I found official docs a bit confusing. And what I found (for OpenJDK 11) mostly aligns with what others have written. What is important is the order of evaluation of properties.

InetAddressCachePolicy.java (I'm omitting some boilerplate for readability):


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;
  }
}

You can clearly see that the security property is evaluated first, system property second and if any of them is set cachePolicy value is set to that number or -1 (FOREVER) if they hold a value that is bellow -1. If nothing is set it defaults to 30 seconds. As it turns out for OpenJDK that is almost always the case because by default java.security does not set that value, only a negative one.

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

BTW if the networkaddress.cache.negative.ttl is not set (removed from the file) the default inside the java class is 0. Documentation is wrong in this regard. This is what tripped me over.

拥抱影子 2024-08-06 10:59:43

我遇到过同样的问题。 默认情况下,JVM 会永久缓存 DNS 查找的结果。 将 networkaddress.cache.ttl 设置为 0 会有所帮助,但它不会“尊重”DNS 记录 TTL。

我使用了 https://github.com/dnsjava/dnsjava 库。

String remoteIp = org.xbill.DNS.Address.getByName(remoteHostname).getHostAddress();

无论 networkaddress.cache.ttl 设置如何,这都会遵循 DNS 记录 TTL。

I had the same issue. By default JVM caches the results of DNS lookup forever. Setting networkaddress.cache.ttl to 0 helps, but it doesn't "honor" the DNS record TTL.

I used https://github.com/dnsjava/dnsjava library.

String remoteIp = org.xbill.DNS.Address.getByName(remoteHostname).getHostAddress();

This does honor the DNS record TTL regardless of networkaddress.cache.ttl setting.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文