Wie kann Java das DNS-Caching-Timeout einhalten?

100

Wir verwenden GSLB für die Geoverteilung und den Lastausgleich. Jedem Dienst wird ein fester Domänenname zugewiesen. Durch etwas DNS-Magie wird der Domänenname in eine IP aufgelöst, die dem Server mit der geringsten Auslastung am nächsten liegt. Damit der Lastenausgleich funktioniert, muss der Anwendungsserver die TTL aus der DNS-Antwort berücksichtigen und den Domänennamen erneut auflösen, wenn der Cache abgelaufen ist. Ich konnte jedoch keinen Weg finden, dies in Java zu tun.

Die Anwendung befindet sich in Java 5 unter Linux (Centos 5).

ZZ Coder
quelle

Antworten:

76

Gemäß der Antwort von Byron können Sie keine networkaddress.cache.ttloder networkaddress.cache.negative.ttlals Systemeigenschaften festlegen, indem Sie das -DFlag oder den Aufruf verwenden, System.setPropertyda dies keine Systemeigenschaften sind - es handelt sich um Sicherheitseigenschaften .

Wenn Sie eine Systemeigenschaft verwenden möchten, um dieses Verhalten auszulösen (damit Sie das -DFlag oder den Aufruf verwenden können System.setProperty), müssen Sie die folgende Systemeigenschaft festlegen :

-Dsun.net.inetaddr.ttl=0

Diese Systemeigenschaft aktiviert den gewünschten Effekt.

Beachten Sie jedoch Folgendes: Wenn Sie das -DFlag beim Starten des JVM-Prozesses nicht verwenden und stattdessen den Code aufrufen:

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

Dieser Code muss ausgeführt werden, bevor ein anderer Code in der JVM versucht, Netzwerkvorgänge auszuführen.

Dies ist wichtig, da dies beispielsweise Security.setPropertynicht funktioniert , wenn Sie eine .war-Datei aufgerufen und diese .war für Tomcat bereitgestellt haben: Tomcat verwendet den Java-Netzwerkstapel, um sich viel früher zu initialisieren, als der .war-Code ausgeführt wird. Aufgrund dieser "Race-Bedingung" ist es normalerweise bequemer, das -DFlag zu verwenden, wenn der JVM-Prozess gestartet wird.

Wenn Sie nicht verwenden -Dsun.net.inetaddr.ttl=0oder aufrufen Security.setProperty, müssen Sie $JRE_HOME/lib/security/java.securitydiese Sicherheitseigenschaften in dieser Datei bearbeiten und festlegen, z

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

Beachten Sie jedoch die Sicherheitswarnungen in den Kommentaren zu diesen Eigenschaften. Tun Sie dies nur, wenn Sie einigermaßen sicher sind, dass Sie nicht anfällig für DNS-Spoofing-Angriffe sind .

Les Hazlewood
quelle
2
Der FQN ist java.security.Security(zumindest in jdk7)
Pablo Fernandez
1
Nur ein Kommentar, diese Sicherheitswarnungen beziehen sich hauptsächlich auf Sicherheitsmanager und das Fernladen. Für jede normale Serveranwendung, die dem DNS in gewissem Maße vertraut, ist das Reduzieren der TTL in Ordnung. (Ich denke jedoch nicht, dass 0 ein gutes Minimum ist und der Standardwert von 30 Sekunden für Nicht-Sicherheitsmanager in den meisten Fällen in Ordnung ist).
eckes
3
Funktioniert die Systemeigenschaft auch mit dem OpenJDK oder ist sie Oracle-spezifisch?
Mhlz
67

Java hat ein ernsthaft seltsames DNS-Caching-Verhalten. Am besten deaktivieren Sie das DNS-Caching oder stellen Sie eine niedrige Zahl wie 5 Sekunden ein.

networkaddress.cache.ttl (Standard: -1)
Gibt die Caching-Richtlinie für erfolgreiche Namenssuchen vom Namensdienst an. Der Wert wird als Ganzzahl angegeben, um die Anzahl der Sekunden anzugeben, in denen die erfolgreiche Suche zwischengespeichert werden soll. Ein Wert von -1 gibt "Cache für immer" an.

networkaddress.cache.negative.ttl (Standard: 10)
Gibt die Caching-Richtlinie für nicht erfolgreiche Namenssuchen vom Namensdienst an. Der Wert wird als Ganzzahl angegeben, um die Anzahl der Sekunden anzugeben, in denen der Fehler für nicht erfolgreiche Suchvorgänge zwischengespeichert werden soll. Der Wert 0 gibt "nie zwischenspeichern" an. Ein Wert von -1 gibt "Cache für immer" an.

Byron Whitlock
quelle
7
Hinweis: Dadurch wird nicht das gesamte DNS-Caching in Ihrem Betriebssystem deaktiviert. Deaktiviert einfach Javas eigenes defektes In-Memory-Caching in der Bibliothek. Sie können diese Eigenschaften einfach in der Befehlszeile festlegen, wenn Sie die JVM aufrufen.
Nelson
2
Ich weiß nicht, dass "kaputt" gültig ist. Java (aus Sicherheitsgründen) speichert DNS-Einträge für immer oder bis zum Neustart der JVM zwischen, je nachdem, was zuerst eintritt. Dies (soweit ich das beurteilen kann) war beabsichtigt. Die Einstellungen können in der Richtliniendatei java.security oder in der Befehlszeile vorgenommen werden. Die Einstellungen sind jeweils unterschiedlich. Referenz: rgagnon.com/javadetails/java-0445.html
Milner
3
Beachten Sie, dass Sie diese nicht als Systemeigenschaften festlegen können (dh mit den Flags -D oder System.setProperty), da es sich nicht um Systemeigenschaften handelt, sondern um Sicherheitseigenschaften.
Les Hazlewood
6
Diese Dokumentation unterscheidet sich geringfügig in 1.7. Insbesondere tritt der Cache jetzt nur für immer auf, wenn ein Sicherheitsmanager vorhanden ist: "Das Standardverhalten besteht darin, für immer zwischenzuspeichern, wenn ein Sicherheitsmanager installiert ist, und für einen implementierungsspezifischen Zeitraum zwischenzuspeichern, wenn kein Sicherheitsmanager nicht installiert ist." docs.oracle.com/javase/7/docs/technotes/guides/net/…
Brett Okken
1
@ Michael sehen System.getSecurityManager(). Docs für Java 8: docs.oracle.com/javase/8/docs/api/java/lang/…
gesellix
22

Dies wurde offensichtlich in neueren Versionen (SE 6 und 7) behoben. Beim Ausführen des folgenden Codeausschnitts, während die Aktivität von Port 53 mit tcpdump überwacht wird, tritt eine maximale Caching-Zeit von 30 Sekunden auf.

/**
 * 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();
        }
    }
}
user1050755
quelle
16
Ja, Java 1.5 hatte den Standardwert für unendliches Caching. Java 1.6 und 1.7 haben eine Standardeinstellung von 30 Sekunden.
Michael
7
Aus der Dokumentation zu 1.7 geht hervor, dass dies möglicherweise nur dann der Fall ist, wenn kein Sicherheitsmanager vorhanden ist: "Das Standardverhalten besteht darin, bei der Installation eines Sicherheitsmanagers für immer zwischenzuspeichern und bei einem Sicherheitsvorgang für einen implementierungsspezifischen Zeitraum zwischenzuspeichern Manager ist nicht installiert. " docs.oracle.com/javase/7/docs/technotes/guides/net/…
Brett Okken
1
@ Michael möchten die Quelle für diese Informationen teilen?
Rustyx
4
@rustyx Das JDK 1.6 und 1.7 von Oracle enthält Folgendes in jre / lib / security / java.security für networkaddress.cache.ttl: "# Standardwert ist für immer (FÜR IMMER). Aus Sicherheitsgründen wird dieses # Caching für immer durchgeführt, wenn ein Sicherheitsmanager ist festgelegt. Wenn kein Sicherheitsmanager # festgelegt ist, wird standardmäßig 30 Sekunden lang zwischengespeichert. " Applets und Apps, die über Java Web Start bereitgestellt werden, werden immer noch für immer zwischengespeichert, andernfalls sind es 30 Sekunden.
Michael
1
Hier ist ein Codezeiger auf java.security von OpenJDK 8, der besagt, dass die TTL ohne Sicherheitsmanager 30s beträgt: hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/f940e7a48b72/src/share/… . Ich habe dies unter Mac OS X und Ubuntu 14.04 getestet.
Tro
18

Um Byrons Antwort zu erweitern, müssen Sie meines Erachtens die Datei java.securityim %JRE_HOME%\lib\securityVerzeichnis bearbeiten , um diese Änderung zu bewirken.

Hier ist der relevante Abschnitt:

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

Dokumentation zur java.securityDatei hier .

matt b
quelle
5
Um dies hinzuzufügen, musste ich bei Verwendung von tomcat6 meine lib / security-Datei ändern, da das Festlegen von networkaddress.cache.ttl oder sun.net.inetaddr.ttl entweder programmgesteuert oder über die Variable JAVA_OPTS nicht funktionierte.
Bramp
1
@bramp Danke Bruder, ich stehe auch vor dem gleichen Problem und habe es gelöst, indem ich Ihren Kommentar und die Antworten +1 für Kommentar und Antwort verwendet habe.
Bhavik Ambani
7

Um die anderen Antworten zusammenzufassen, <jre-path>/lib/security/java.securitykönnen Sie in den Wert der Eigenschaft festlegen, networkaddress.cache.ttlum anzupassen, wie DNS-Lookups zwischengespeichert werden. Beachten Sie, dass dies keine Systemeigenschaft, sondern eine Sicherheitseigenschaft ist. Ich konnte dies einstellen mit:

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

Dies kann auch durch die Systemeigenschaft festgelegt werden, -Dsun.net.inetaddr.ttlobwohl dies eine Sicherheitseigenschaft nicht überschreibt, wenn sie an anderer Stelle festgelegt wird.

Ich möchte auch hinzufügen, dass die Einstellung networkaddress.cache.ttlnicht ausreicht , wenn Sie dieses Problem mit Webdiensten in WebSphere wie ich sehen . Sie müssen die Systemeigenschaft festlegen disableWSAddressCachingzu true. Im Gegensatz zur Time-to-Live-Eigenschaft kann dies als JVM-Argument oder über festgelegt werden System.setProperty.

IBM hat hier einen ziemlich detaillierten Beitrag darüber, wie WebSphere mit DNS-Caching umgeht . Das relevante Stück zu dem oben genannten ist:

Um das Adress-Caching für Webdienste zu deaktivieren, müssen Sie eine zusätzliche benutzerdefinierte JVM-Eigenschaft disableWSAddressCaching auf true setzen. Verwenden Sie diese Eigenschaft, um das Adress-Caching für Webdienste zu deaktivieren. Wenn Ihr System normalerweise mit vielen Client-Threads ausgeführt wird und im wsAddrCache-Cache Sperrenkonflikte auftreten, können Sie diese benutzerdefinierte Eigenschaft auf true setzen, um das Zwischenspeichern der Webdienstdaten zu verhindern.

die Quadratwurzel
quelle
2

Nach den offiziellen Oracle Java - Eigenschaften , sun.net.inetaddr.ttlist Sun Implementierung spezifische Eigenschaft, die „in zukünftigen Versionen möglicherweise nicht unterstützt“. "Der bevorzugte Weg ist die Verwendung der Sicherheitseigenschaft" networkaddress.cache.ttl.

CloudStax
quelle