Como fazer o Java respeitar o tempo limite de cache do DNS?

101

Usamos GSLB para geodistribuição e balanceamento de carga. Cada serviço é atribuído a um nome de domínio fixo. Por meio de alguma mágica do DNS, o nome de domínio é resolvido em um IP mais próximo do servidor com menos carga. Para que o balanceamento de carga funcione, o servidor de aplicativos precisa respeitar o TTL da resposta DNS e resolver o nome de domínio novamente quando o tempo limite do cache se esgotar. No entanto, não consegui descobrir uma maneira de fazer isso em Java.

A aplicação está em Java 5, rodando em Linux (Centos 5).

ZZ Coder
fonte

Respostas:

76

De acordo com a resposta de Byron, você não pode definir networkaddress.cache.ttlou networkaddress.cache.negative.ttlcomo Propriedades do sistema usando o -Dsinalizador ou chamando System.setPropertyporque essas não são propriedades do sistema - são propriedades de segurança .

Se você deseja usar uma propriedade do sistema para acionar esse comportamento (para que você possa usar o -Dsinalizador ou chamada System.setProperty), você desejará definir a seguinte propriedade do sistema :

-Dsun.net.inetaddr.ttl=0

Esta propriedade do sistema habilitará o efeito desejado.

Mas esteja ciente: se você não usar o -Dsinalizador ao iniciar o processo JVM e optar por chamar isso a partir do código:

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

Este código deve ser executado antes que qualquer outro código na JVM tente realizar operações de rede.

Isso é importante porque, por exemplo, se você chamasse Security.setPropertyum arquivo .war e implantasse esse .war no Tomcat, isso não funcionaria: o Tomcat usa a pilha de rede Java para inicializar a si mesmo muito antes da execução do código .war. Por causa dessa 'condição de corrida', geralmente é mais conveniente usar o -Dsinalizador ao iniciar o processo JVM.

Se você não usar -Dsun.net.inetaddr.ttl=0ou chamar Security.setProperty, precisará editar $JRE_HOME/lib/security/java.securitye definir essas propriedades de segurança nesse arquivo, por exemplo

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

Mas preste atenção aos avisos de segurança nos comentários sobre essas propriedades. Só faça isso se você estiver razoavelmente confiante de que não é suscetível a ataques de spoofing de DNS .

Les Hazlewood
fonte
2
O FQN é java.security.Security(pelo menos em jdk7)
Pablo Fernandez
1
Apenas um comentário, esses avisos de segurança são principalmente em relação aos gerenciadores de segurança e carregamento remoto. Para qualquer aplicativo de servidor normal que confie no DNS até certo ponto, reduzir o TTL é adequado. (No entanto, não acho que 0 seja um mínimo bom e o padrão de 30s para gerenciadores que não são de segurança é adequado na maioria dos casos).
eckes de
3
A propriedade do sistema também funciona com o OpenJDK ou é específica do Oracle?
mhlz de
67

Java tem um comportamento de cache dns seriamente estranho. Sua melhor aposta é desligar o cache de dns ou configurá-lo para um número baixo, como 5 segundos.

networkaddress.cache.ttl (padrão: -1)
Indica a política de armazenamento em cache para pesquisas de nome bem-sucedidas do serviço de nomes. O valor é especificado como inteiro para indicar o número de segundos para armazenar em cache a pesquisa bem-sucedida. Um valor de -1 indica "cache para sempre".

networkaddress.cache.negative.ttl (padrão: 10)
Indica a política de armazenamento em cache para pesquisas de nomes malsucedidas do serviço de nomes. O valor é especificado como um inteiro para indicar o número de segundos para armazenar em cache a falha para pesquisas malsucedidas. Um valor de 0 indica "nunca armazenar em cache". Um valor de -1 indica "cache para sempre".

Byron Whitlock
fonte
7
Nota: isso não desativa todo o cache DNS em seu sistema operacional. Apenas desativa o cache in-memory do Java quebrado na biblioteca. Você pode simplesmente definir essas propriedades na linha de comando ao chamar a JVM.
Nelson
2
Não sei se "quebrado" é válido. Java (por razões de segurança) armazena em cache as entradas DNS para sempre ou até que o JVM seja reiniciado, o que ocorrer primeiro. Isso (pelo que posso dizer) foi intencional. As configurações podem ser feitas no arquivo de política java.security ou na linha de comando. As configurações são diferentes para cada um. Referência: rgagnon.com/javadetails/java-0445.html
Milner
4
Observe que você não pode defini-las como propriedades do sistema (ou seja, usando os sinalizadores -D ou System.setProperty) porque não são propriedades do sistema - são propriedades de segurança.
Les Hazlewood de
6
Esta documentação é um pouco diferente em 1.7. Especificamente, o cache para sempre agora só acontece quando um gerenciador de segurança está presente: "O comportamento padrão é fazer o cache para sempre quando um gerenciador de segurança é instalado e para um período específico de implementação, quando um gerenciador de segurança não está instalado." docs.oracle.com/javase/7/docs/technotes/guides/net/…
Brett Okken
1
@Michael see System.getSecurityManager(). Documentos para Java 8: docs.oracle.com/javase/8/docs/api/java/lang/…
gesellix
22

Obviamente, isso foi corrigido nas versões mais recentes (SE 6 e 7). Eu experimento um tempo máximo de armazenamento em cache de 30 segundos ao executar o trecho de código a seguir enquanto observo a atividade da porta 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();
        }
    }
}
user1050755
fonte
16
Sim, Java 1.5 tinha um valor padrão de cache infinito. Java 1.6 e 1.7 têm um padrão de 30 segundos.
Michael
7
A documentação para 1.7 indica que isso só pode ser verdade no caso em que um gerenciador de segurança não está presente: "O comportamento padrão é armazenar em cache para sempre quando um gerenciador de segurança é instalado e armazenar em cache por um período específico de implementação, quando um segurança gerenciador não está instalado. " docs.oracle.com/javase/7/docs/technotes/guides/net/…
Brett Okken
1
@Michael deseja compartilhar a fonte dessas informações?
rustyx
4
@rustyx Oracle's 1.6 e 1.7 JDK tem isso em jre / lib / security / java.security para networkaddress.cache.ttl: "# valor padrão é para sempre (PARA SEMPRE). Por razões de segurança, este # cache é feito para sempre quando um gerenciador de segurança está definido. Quando um gerenciador # de segurança não está definido, o comportamento padrão é armazenar em cache por 30 segundos. " Assim, applets e aplicativos implantados via Java Web Start ainda armazenam em cache para sempre, caso contrário, são 30 segundos.
Michael
1
Aqui está um ponteiro de código para java.security do OpenJDK 8, que diz que sem um gerenciador de segurança o TTL é 30s: hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/f940e7a48b72/src/share/… . Eu testei isso no Mac OS X e Ubuntu 14.04.
tro de
18

Para expandir a resposta de Byron, acredito que você precisa editar o arquivo java.securityno %JRE_HOME%\lib\securitydiretório para efetuar essa alteração.

Aqui está a seção 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 

Documentação no java.securityarquivo aqui .

matt b
fonte
5
Para adicionar a isso, ao usar tomcat6 eu tive que modificar meu arquivo lib / security, pois a configuração de networkaddress.cache.ttl ou sun.net.inetaddr.ttl tanto programaticamente quanto via variável JAVA_OPTS não funcionou.
bramp de
1
@bramp Obrigado mano, também estou enfrentando o mesmo problema e resolvi usando seu comentário e respostas +1 para comentário e resposta.
Bhavik Ambani
7

Para resumir as outras respostas, em <jre-path>/lib/security/java.securityvocê pode definir o valor da propriedade networkaddress.cache.ttlpara ajustar como as pesquisas DNS são armazenadas em cache. Observe que esta não é uma propriedade do sistema, mas uma propriedade de segurança. Consegui definir isso usando:

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

Isso também pode ser definido pela propriedade do sistema, -Dsun.net.inetaddr.ttlembora não substitua uma propriedade de segurança se for definida em outro lugar.

Também gostaria de acrescentar que, se você está vendo esse problema com serviços da Web no WebSphere, como eu estava, a configuração networkaddress.cache.ttlnão será suficiente. Você precisa definir a propriedade do sistema disableWSAddressCachingcomo true. Ao contrário da propriedade time-to-live, isso pode ser definido como um argumento JVM ou via System.setProperty).

A IBM tem uma postagem bem detalhada sobre como o WebSphere lida com o cache DNS aqui . A peça relevante para o acima é:

Para desativar o armazenamento em cache de endereço para serviços da Web, é necessário configurar uma propriedade customizada JVM adicional disableWSAddressCaching como true. Use esta propriedade para desativar o cache de endereços para serviços da web. Se o seu sistema normalmente é executado com muitos encadeamentos do cliente e você encontra contenção de bloqueio no cache wsAddrCache, é possível definir esta propriedade customizada como true, para evitar o armazenamento em cache dos dados de serviços da web.

thesquaregroot
fonte
2

De acordo com as propriedades oficiais do oracle java , sun.net.inetaddr.ttlé uma propriedade específica da implementação da Sun, que "pode ​​não ser suportada em versões futuras". "a forma preferida é usar a propriedade de segurança" networkaddress.cache.ttl.

CloudStax
fonte