【发布时间】:2010-11-18 09:34:03
【问题描述】:
我们使用 GSLB 进行地理分布和负载平衡。每个服务都分配了一个固定的域名。通过一些 DNS 魔术,域名被解析为最接近负载最少的服务器的 IP。为了使负载均衡发挥作用,应用服务器需要遵守来自 DNS 响应的 TTL,并在缓存超时时重新解析域名。但是,我想不出在 Java 中执行此操作的方法。
应用程序采用 Java 5,在 Linux (Centos 5) 上运行。
【问题讨论】:
我们使用 GSLB 进行地理分布和负载平衡。每个服务都分配了一个固定的域名。通过一些 DNS 魔术,域名被解析为最接近负载最少的服务器的 IP。为了使负载均衡发挥作用,应用服务器需要遵守来自 DNS 响应的 TTL,并在缓存超时时重新解析域名。但是,我想不出在 Java 中执行此操作的方法。
应用程序采用 Java 5,在 Linux (Centos 5) 上运行。
【问题讨论】:
根据 Byron 的回答,您不能使用 -D 标志或调用 System.setProperty 将 networkaddress.cache.ttl 或 networkaddress.cache.negative.ttl 设置为系统属性,因为这些不是系统属性 - 它们是安全性 属性。
如果您想使用系统属性来触发此行为(因此您可以使用-D 标志或调用System.setProperty),您需要设置以下系统 属性:
-Dsun.net.inetaddr.ttl=0
此系统属性将启用所需的效果。
但请注意:如果您在启动 JVM 进程时不使用 -D 标志并选择从代码中调用它:
java.security.Security.setProperty("networkaddress.cache.ttl" , "0")
此代码必须在 JVM 中的任何其他代码尝试执行网络操作之前执行。
这很重要,因为例如,如果您在 .war 文件中调用 Security.setProperty 并将该 .war 部署到 Tomcat,这将不起作用:Tomcat 使用 Java 网络堆栈比您的 .战争的代码被执行。由于这种“竞争条件”,在启动 JVM 进程时使用-D 标志通常更方便。
如果您不使用-Dsun.net.inetaddr.ttl=0 或调用Security.setProperty,则需要编辑$JRE_HOME/lib/security/java.security 并在该文件中设置这些安全属性,例如
networkaddress.cache.ttl = 0
networkaddress.cache.negative.ttl = 0
但请注意这些属性周围的 cmets 中的安全警告。仅当您有理由确信自己不会受到 DNS spoofing attacks 的影响时才这样做。
【讨论】:
java.security.Security(至少在 jdk7 中)
Java 有一些非常奇怪的 dns 缓存行为。最好的办法是关闭 dns 缓存或将其设置为 5 秒等较低的数字。
networkaddress.cache.ttl(默认值:-1)
指示从名称服务中成功查找名称的缓存策略。该值指定为整数,以指示缓存成功查找的秒数。 -1 值表示“永久缓存”。networkaddress.cache.negative.ttl(默认:10)
指示来自名称服务的不成功名称查找的缓存策略。该值指定为整数,以指示缓存不成功查找失败的秒数。值 0 表示“从不缓存”。 -1 值表示“永远缓存”。
【讨论】:
System.getSecurityManager()。 Java 8 文档:docs.oracle.com/javase/8/docs/api/java/lang/…
这显然已在较新的版本(SE 6 和 7)中得到修复。在使用 tcpdump 观察端口 53 活动时,我在运行以下代码 sn-p 时遇到最长 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 的答案,我相信您需要编辑 %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 文件 here 上的文档。
【讨论】:
总结其他答案,在<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 是不够的。您需要将系统属性disableWSAddressCaching 设置为true。与 time-to-live 属性不同,它可以设置为 JVM 参数或通过System.setProperty)。
IBM 有一篇关于 WebSphere 如何处理 DNS 缓存 here 的非常详细的文章。与上述相关的部分是:
要禁用 Web 服务的地址缓存,您需要将附加的 JVM 定制属性 disableWSAddressCaching 设置为 true。使用此属性禁用 Web 服务的地址缓存。如果您的系统通常使用大量客户端线程运行,并且您在 wsAddrCache 缓存上遇到锁争用,那么您可以将此定制属性设置为 true,以防止缓存 Web 服务数据。
【讨论】:
根据official oracle java properties,sun.net.inetaddr.ttl 是特定于 Sun 实现的属性,“可能在未来的版本中不受支持”。 “首选方法是使用安全属性”networkaddress.cache.ttl。
【讨论】:
所以我决定查看 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。在这方面文档是错误的。这就是让我绊倒的原因。
【讨论】: