Як змусити Java дотримуватися тайм-ауту кешування DNS?


101

Ми використовуємо GSLB для георозподілу та балансування навантаження. Кожній службі присвоюється фіксоване доменне ім'я. Завдяки деякій магії DNS доменне ім’я перетворюється на IP-адресу, найближчу до сервера із найменшим навантаженням. Щоб балансування навантаження працювало, серверу додатків потрібно виконати відповідь TTL від відповіді DNS і знову визначити доменне ім’я, коли кеш-пам’ять закінчується. Однак я не міг знайти спосіб зробити це на Java.

Додаток знаходиться на Java 5, працює на Linux (Centos 5).

Відповіді:


76

Відповідно до відповіді Байрона, ви не можете встановити 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 для ініціалізації набагато раніше, ніж виконується ваш код .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 .


2
FQN java.security.Security(принаймні в jdk7)
Пабло Фернандес

1
Лише коментар, ці попередження щодо безпеки стосуються здебільшого менеджерів безпеки та віддаленого завантаження. Для будь-якої звичайної серверної програми, яка довіряє DNS певною мірою, зменшення TTL - це нормально. (Однак я не думаю, що 0 - це хороший мінімум, і за замовчуванням 30-х років для менеджерів, що не займаються безпекою, це в більшості випадків чудово).
eckes

3
Чи працює властивість системи також з OpenJDK, чи це специфічно для Oracle?
mhlz

67

Java має деякі дивно поведінки кешування dns. Ваша найкраща ставка - вимкнути кешування dns або встановити для нього деяку низьку цифру, наприклад 5 секунд.

networkaddress.cache.ttl (за замовчуванням: -1)
Вказує політику кешування для успішного пошуку імен із служби імен. Значення вказано як ціле число, щоб вказати кількість секунд для кешування успішного пошуку. Значення -1 означає "кеш назавжди".

networkaddress.cache.negative.ttl (за замовчуванням: 10)
Вказує політику кешування для невдалих пошуків імен із служби імен. Значення вказується як ціле число, щоб вказати кількість секунд для кешування помилки при неуспішному пошуку. Значення 0 означає "ніколи не кешувати". Значення -1 означає "кеш назавжди".


7
Примітка: це не вимикає всі кешування DNS у вашій ОС. Просто вимикає власне зламане кешування в пам'яті Java у бібліотеці. Ви можете просто встановити ці властивості в командному рядку під час виклику JVM.
Нельсон

2
Я не знаю, що "зламаний" є дійсним. Java (із міркувань безпеки) кешує записи DNS назавжди або до перезапуску JVM, залежно від того, що трапиться раніше. Це (з того, що я можу зрозуміти) було за задумом. Налаштування можна зробити у файлі політики java.security або в командному рядку. Налаштування різні для кожного. Довідково: rgagnon.com/javadetails/java-0445.html
Мілнер

4
Зверніть увагу, що ви не можете встановити їх як властивості системи (тобто використовуючи прапори -D або System.setProperty), оскільки вони не є властивостями системи - це властивості безпеки.
Les Hazlewood

6
Ця документація дещо відрізняється в 1.7. Зокрема, кеш назавжди тепер відбувається лише тоді, коли присутній диспетчер безпеки: "Типовою поведінкою є кешування назавжди, коли встановлений диспетчер безпеки, та кешування протягом певного періоду реалізації, коли диспетчер безпеки не встановлений." docs.oracle.com/javase/7/docs/technotes/guides/net/…
Бретт

1
@Michael див System.getSecurityManager(). Документи для Java 8: docs.oracle.com/javase/8/docs/api/java/lang/…
gesellix

22

Це, очевидно, було виправлено в нових версіях (SE 6 та 7). Я відчуваю максимум 30 секунд кешування, коли запускаю такий фрагмент коду під час перегляду активності порту 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();
        }
    }
}

16
Так, Java 1.5 мала значення за замовчуванням нескінченного кешування. Java 1.6 та 1.7 мають 30 секунд за замовчуванням.
Майкл

7
У документації до 1.7 вказано, що це може бути правдою лише в тому випадку, коли диспетчера безпеки немає: "За замовчуванням поведінка кешується назавжди, коли встановлений диспетчер безпеки, і кешування протягом певного періоду реалізації, коли система безпеки менеджер не встановлений ". docs.oracle.com/javase/7/docs/technotes/guides/net/…
Бретт

1
@Michael хоче поділитися джерелом цієї інформації?
rustyx

4
@ і JDK 1.6 та 1.7 JDK Oracle мають це у jre / lib / security / java.security для networkaddress.cache.ttl: "# значення за замовчуванням назавжди (НАЗАВЖДИ). З міркувань безпеки це # кешування робиться назавжди, коли менеджер безпеки Якщо менеджер безпеки # не встановлений, поведінка за замовчуванням - кешування протягом 30 секунд. " Тож аплети та програми, розгорнуті через Java Web Start, все ще кешують назавжди, інакше це 30 секунд.
Michael

1
Ось покажчик коду на java.security OpenJDK 8, який говорить, що без менеджера безпеки TTL становить 30 с: hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/f940e7a48b72/src/share/… . Я тестував це на Mac OS X та Ubuntu 14.04.
tro

18

Щоб розширити відповідь Байрона, я вважаю, вам потрібно відредагувати файл 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
До того ж, під час використання tomcat6 мені довелося змінити файл lib / security, оскільки налаштування networkaddress.cache.ttl або sun.net.inetaddr.ttl ні програмно, ні через змінну JAVA_OPTS не працювали.
bramp

1
@bramp Дякую, брате, я теж стикаюся з тією ж проблемою, яку я вирішив, використовуючи твій коментар та відповіді +1 для коментарів та відповідей.
Бхавік Амбані,

7

Щоб підсумувати інші відповіді, <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 disableWSAddressCaching на true. Використовуйте цю властивість, щоб вимкнути кешування адрес для веб-служб. Якщо ваша система зазвичай працює з великою кількістю потоків клієнта, і ви стикаєтесь із суперечкою про блокування в кеші wsAddrCache, ви можете встановити для цього властивості значення true, щоб запобігти кешуванню даних веб-служб.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.