Есть ли способ заставить Java соблюдать время ожидания кэширования DNS?
Мы используем GSLB для геораспределения и балансировки нагрузки. Каждому сервису назначается фиксированное доменное имя. Через некоторую магию DNS доменное имя разрешено в IP, наиболее близком к серверу с наименьшей нагрузкой. Для того, чтобы балансировка нагрузки работала, сервер приложений должен соблюдать ТТЛ от ответа DNS и снова разрешать имя домена при истечении времени ожидания кеша. Однако я не мог найти способ сделать это на Java.
Приложение находится в Java 5, работающем на Linux (Centos 5).
Ответы
Ответ 1
В Java есть какое-то серьезное странное поведение кэширования dns. Лучше всего отключить кэширование DNS или установить его на небольшое количество, например, 5 секунд.
networkaddress.cache.ttl(по умолчанию: -1)
Указывает политику кэширования для успешного поиска имен из службы имен. Значение указано как целое, чтобы указать количество секунд для кэширования успешного поиска. Значение -1 указывает "кеш навсегда".
networkaddress.cache.negative.ttl(по умолчанию: 10)
Указывает политику кэширования для неудачных поисков имен из службы имен. Значение указывается как целое число, указывающее количество секунд для кэширования ошибки для неудачных поисков. Значение 0 указывает "никогда не кэшировать". Значение -1 указывает "кеш навсегда".
Ответ 2
В ответ на запрос 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 для инициализации намного раньше, чем ваш. исполняется военный код. Из-за этого "состояния гонки" обычно удобнее использовать флаг -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.
Ответ 3
Это, очевидно, было исправлено в новых версиях (SE 6 и 7). Я испытываю 30-секундное время кэширования max при запуске следующего фрагмента кода во время просмотра активности порта 53 с помощью 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();
}
}
}
Ответ 4
Чтобы развернуть ответ Байрона, я считаю, что вам нужно отредактировать файл 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
здесь.
Ответ 5
Чтобы суммировать другие ответы, в <jre-path>/lib/security/java.security
вы можете установить значение свойства networkaddress.cache.ttl
для настройки кэширования DNS-запросов. Обратите внимание, что это не системное свойство, а свойство безопасности. Я смог установить это, используя:
java.security.Security.setProperty("networkaddress.cache.ttl", "<value>");
Это также может быть задано системным свойством -Dsun.net.inetaddr.ttl
, хотя это не будет отменять свойство безопасности, если оно установлено в другом месте.
Я также хотел бы добавить, что если вы видите эту проблему с веб-службами в WebSphere, как и я, установка networkaddress.cache.ttl
будет недостаточной. Вам необходимо установить системное свойство disableWSAddressCaching
на true
. В отличие от свойства time-to-live, это может быть установлено как аргумент JVM или через System.setProperty
).
В IBM есть довольно подробное сообщение о том, как WebSphere обрабатывает кэширование DNS здесь. Соответствующая часть выше:
Чтобы отключить кеширование адресов для веб-служб, вам необходимо установить дополнительное настраиваемое свойство JVM, чтобы отключитьWSAddressCaching до true. Используйте это свойство, чтобы отключить кеширование адресов для веб-служб. Если ваша система обычно работает с большим количеством клиентских потоков, и вы сталкиваетесь с конфликтом блокировки в кеше wsAddrCache, вы можете установить для этого настраиваемого свойства значение true, чтобы предотвратить кеширование данных веб-служб.
Ответ 6
В соответствии с официальными свойствами oracle java, sun.net.inetaddr.ttl
- это свойство, специфичное для реализации Sun, которое "может не поддерживаться в будущих версиях". "предпочтительным способом является использование свойства безопасности" networkaddress.cache.ttl
.