¿Cómo hacer que Java respete el tiempo de espera de almacenamiento en caché de DNS?


101

Usamos GSLB para la distribución geográfica y el equilibrio de carga. A cada servicio se le asigna un nombre de dominio fijo. A través de la magia de DNS, el nombre de dominio se resuelve en una IP que es la más cercana al servidor con menor carga. Para que funcione el equilibrio de carga, el servidor de aplicaciones debe respetar el TTL de la respuesta de DNS y resolver el nombre de dominio nuevamente cuando se agota el tiempo de espera de la caché. Sin embargo, no pude encontrar una manera de hacer esto en Java.

La aplicación está en Java 5, ejecutándose en Linux (Centos 5).

Respuestas:


76

Según la respuesta de Byron, no puede establecer networkaddress.cache.ttlo networkaddress.cache.negative.ttlcomo Propiedades del sistema mediante el uso de la -Dmarca o la llamada System.setPropertyporque estas no son propiedades del sistema, son propiedades de seguridad .

Si desea usar una propiedad del sistema para activar este comportamiento (para que pueda usar la -Dmarca o la llamada System.setProperty), querrá establecer la siguiente propiedad del sistema :

-Dsun.net.inetaddr.ttl=0

Esta propiedad del sistema permitirá el efecto deseado.

Pero tenga en cuenta: si no usa la -Dbandera al iniciar el proceso de JVM y elige llamar a esto desde el código:

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

Este código debe ejecutarse antes de que cualquier otro código en la JVM intente realizar operaciones de red.

Esto es importante porque, por ejemplo, si llamó Security.setPropertya un archivo .war y lo implementó .war en Tomcat, esto no funcionaría: Tomcat usa la pila de redes Java para inicializarse mucho antes de que se ejecute su código .war. Debido a esta 'condición de carrera', generalmente es más conveniente usar la -Dbandera al iniciar el proceso de JVM.

Si no usa -Dsun.net.inetaddr.ttl=0o llama Security.setProperty, deberá editar $JRE_HOME/lib/security/java.securityy configurar esas propiedades de seguridad en ese archivo, por ejemplo

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

Pero preste atención a las advertencias de seguridad en los comentarios sobre esas propiedades. Solo haga esto si está razonablemente seguro de que no es susceptible a ataques de suplantación de DNS .


2
El FQN es java.security.Security(al menos en jdk7)
Pablo Fernandez

1
Solo un comentario, esas advertencias de seguridad están relacionadas principalmente con los administradores de seguridad y la carga remota. Para cualquier aplicación de servidor normal que confíe en el DNS hasta cierto punto, reducir el TTL está bien. (Sin embargo, no creo que 0 sea un buen mínimo y el valor predeterminado de 30 para los administradores que no son de seguridad está bien en la mayoría de los casos).
eckes

3
¿La propiedad del sistema también funciona con OpenJDK o es específica de Oracle?
mhlz

67

Java tiene un comportamiento de almacenamiento en caché de DNS realmente extraño. Lo mejor que puede hacer es desactivar el almacenamiento en caché de DNS o configurarlo en un número bajo, como 5 segundos.

networkaddress.cache.ttl (predeterminado: -1)
Indica la política de almacenamiento en caché para búsquedas de nombres exitosas desde el servicio de nombres. El valor se especifica como un número entero para indicar el número de segundos para almacenar en caché la búsqueda exitosa. Un valor de -1 indica "caché para siempre".

networkaddress.cache.negative.ttl (predeterminado: 10)
Indica la política de almacenamiento en caché para búsquedas de nombres no exitosas desde el servicio de nombres. El valor se especifica como un número entero para indicar el número de segundos para almacenar en caché el error para búsquedas fallidas. Un valor de 0 indica "nunca almacenar en caché". Un valor de -1 indica "caché para siempre".


7
Nota: esto no deshabilita todo el almacenamiento en caché de DNS en su sistema operativo. Simplemente deshabilita el almacenamiento en caché en memoria roto de Java en la biblioteca. Simplemente puede establecer estas propiedades en la línea de comandos cuando invoca la JVM.
Nelson

2
No sé si "roto" es válido. Java (por razones de seguridad) almacena en caché las entradas de DNS para siempre o hasta que se reinicia la JVM, lo que ocurra primero. Esto (por lo que puedo decir) fue por diseño. La configuración se puede realizar en el archivo de política java.security o en la línea de comandos. La configuración es diferente para cada uno. Referencia: rgagnon.com/javadetails/java-0445.html
Milner

4
Tenga en cuenta que no puede establecerlos como propiedades del sistema (es decir, utilizando los indicadores -D o System.setProperty) porque no son propiedades del sistema, son propiedades de seguridad.
Les Hazlewood

6
Esta documentación es ligeramente diferente en 1.7. Específicamente, el caché para siempre ahora solo ocurre cuando un administrador de seguridad está presente: "El comportamiento predeterminado es almacenar en caché para siempre cuando se instala un administrador de seguridad, y almacenar en caché durante un período de tiempo específico de implementación, cuando no hay un administrador de seguridad instalado". docs.oracle.com/javase/7/docs/technotes/guides/net/…
Brett Okken

1
@Michael ver System.getSecurityManager(). Documentos para Java 8: docs.oracle.com/javase/8/docs/api/java/lang/…
gesellix

22

Obviamente, esto se ha corregido en versiones más recientes (SE 6 y 7). Experimento un tiempo máximo de almacenamiento en caché de 30 segundos cuando ejecuto el siguiente fragmento de código mientras observo la actividad del puerto 53 usando 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
Sí, Java 1.5 tenía un valor predeterminado de almacenamiento en caché infinito. Java 1.6 y 1.7 tienen un valor predeterminado de 30 segundos.
Michael

7
La documentación para 1.7 indica que esto solo puede ser cierto en el caso de que un administrador de seguridad no esté presente: "El comportamiento predeterminado es almacenar en caché para siempre cuando se instala un administrador de seguridad, y almacenar en caché durante un período de tiempo específico de implementación, cuando administrador no está instalado ". docs.oracle.com/javase/7/docs/technotes/guides/net/…
Brett Okken

1
¿@Michael quiere compartir la fuente de esa información?
rustyx

4
El JDK 1.6 y 1.7 de @rustyx Oracle tiene esto en jre / lib / security / java.security para networkaddress.cache.ttl: "# el valor predeterminado es para siempre (PARA SIEMPRE). Por razones de seguridad, este # almacenamiento en caché se realiza para siempre cuando un administrador de seguridad está configurado. Cuando no se establece un administrador de # seguridad, el comportamiento predeterminado es almacenar en caché durante 30 segundos ". Por lo tanto, los applets y las aplicaciones implementadas a través de Java Web Start aún se almacenan en caché para siempre, de lo contrario, son 30 segundos.
Michael

1
Aquí hay un puntero de código a java.security de OpenJDK 8, que dice que sin un administrador de seguridad el TTL es 30s: hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/f940e7a48b72/src/share/… . Probé esto en Mac OS X y Ubuntu 14.04.
tro

18

Para ampliar la respuesta de Byron, creo que debe editar el archivo java.securityen el %JRE_HOME%\lib\securitydirectorio para realizar este cambio.

Aquí está la sección relevante:

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

Documentación en el java.securityarchivo aquí .


5
Para agregar a esto, cuando usaba tomcat6 tuve que modificar mi archivo lib / security, ya que configurar networkaddress.cache.ttl o sun.net.inetaddr.ttl ya sea programáticamente o mediante la variable JAVA_OPTS no funcionó.
bramp

1
@bramp Gracias hermano, también estoy enfrentando el mismo problema y lo resolví usando su comentario y respuestas +1 para comentarios y respuestas.
Bhavik Ambani

7

Para resumir las otras respuestas, en <jre-path>/lib/security/java.securitypuede establecer el valor de la propiedad networkaddress.cache.ttlpara ajustar cómo se almacenan en caché las búsquedas de DNS. Tenga en cuenta que esta no es una propiedad del sistema, sino una propiedad de seguridad. Pude configurar esto usando:

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

Esto también se puede establecer mediante la propiedad del sistema, -Dsun.net.inetaddr.ttlaunque no anulará una propiedad de seguridad si se establece en otro lugar.

También me gustaría agregar que si está viendo este problema con los servicios web en WebSphere, como yo, la configuración networkaddress.cache.ttlno será suficiente. Debe establecer la propiedad del sistema disableWSAddressCachingen true. A diferencia de la propiedad de tiempo de vida, esto se puede configurar como un argumento de JVM o mediante System.setProperty).

IBM tiene una publicación bastante detallada sobre cómo WebSphere maneja el almacenamiento en caché de DNS aquí . La pieza relevante de lo anterior es:

Para deshabilitar el almacenamiento en caché de direcciones para servicios web, debe establecer una propiedad personalizada de JVM adicional disableWSAddressCaching en true. Utilice esta propiedad para deshabilitar el almacenamiento en caché de direcciones para servicios web. Si su sistema normalmente se ejecuta con muchos subprocesos de cliente y encuentra contención de bloqueo en la caché de wsAddrCache, puede establecer esta propiedad personalizada en true para evitar el almacenamiento en caché de los datos de los servicios web.


2

De acuerdo con las propiedades oficiales de Oracle Java , sun.net.inetaddr.ttles una propiedad específica de implementación de Sun, que "puede que no sea compatible en versiones futuras". "la forma preferida es utilizar la propiedad de seguridad" networkaddress.cache.ttl.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.