Maneira Java preferida de executar ping em um URL HTTP para disponibilidade

157

Preciso de uma classe de monitor que verifique regularmente se um determinado URL HTTP está disponível. Eu posso cuidar da parte "regularmente" usando a abstração do Spring TaskExecutor, portanto esse não é o tópico aqui. A pergunta é: Qual é a maneira preferida de executar ping em um URL em java?

Aqui está o meu código atual como ponto de partida:

try {
    final URLConnection connection = new URL(url).openConnection();
    connection.connect();
    LOG.info("Service " + url + " available, yeah!");
    available = true;
} catch (final MalformedURLException e) {
    throw new IllegalStateException("Bad URL: " + url, e);
} catch (final IOException e) {
    LOG.info("Service " + url + " unavailable, oh no!", e);
    available = false;
}
  1. Isso é bom mesmo (vai fazer o que eu quero)?
  2. Preciso fechar a conexão de alguma forma?
  3. Suponho que seja um GETpedido. Existe uma maneira de enviar HEAD?
Sean Patrick Floyd
fonte

Respostas:

267

Isso é bom de alguma forma (fará o que eu quero?)

Você pode fazer isso. Outra maneira viável é usar java.net.Socket.

public static boolean pingHost(String host, int port, int timeout) {
    try (Socket socket = new Socket()) {
        socket.connect(new InetSocketAddress(host, port), timeout);
        return true;
    } catch (IOException e) {
        return false; // Either timeout or unreachable or failed DNS lookup.
    }
}

Há também o InetAddress#isReachable():

boolean reachable = InetAddress.getByName(hostname).isReachable();

No entanto, isso não testa explicitamente a porta 80. Você corre o risco de obter falsos negativos devido a um firewall bloqueando outras portas.


Preciso fechar a conexão de alguma forma?

Não, você não precisa explicitamente. É tratado e agrupado sob os capuzes.


Suponho que seja uma solicitação GET. Existe uma maneira de enviar HEAD?

Você pode converter o obtido URLConnectionem HttpURLConnectione, em seguida, usar setRequestMethod()para definir o método de solicitação. No entanto, é necessário levar em consideração que alguns aplicativos da Web pobres ou servidores domésticos podem retornar um erro HTTP 405 para um HEAD (ou seja, não disponível, não implementado, não permitido) enquanto um GET funciona perfeitamente. O uso de GET é mais confiável caso você pretenda verificar links / recursos, não domínios / hosts.


Testar a disponibilidade do servidor não é suficiente no meu caso, preciso testar o URL (o aplicativo da web pode não estar implantado)

De fato, conectar um host apenas informa se o host está disponível, não se o conteúdo está disponível. Pode acontecer que um servidor da Web tenha iniciado sem problemas, mas o aplicativo da Web falhou ao implantar durante o início do servidor. No entanto, isso geralmente não causa a inatividade do servidor inteiro. Você pode determinar isso verificando se o código de resposta HTTP é 200.

HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("HEAD");
int responseCode = connection.getResponseCode();
if (responseCode != 200) {
    // Not OK.
}

// < 100 is undetermined.
// 1nn is informal (shouldn't happen on a GET/HEAD)
// 2nn is success
// 3nn is redirect
// 4nn is client error
// 5nn is server error

Para obter mais detalhes sobre os códigos de status de resposta, consulte a seção 10 da RFC 2616 . A chamada connect()não é necessária se você estiver determinando os dados da resposta. Ele se conectará implicitamente.

Para referência futura, veja um exemplo completo do sabor de um método utilitário, também levando em consideração os tempos limite:

/**
 * Pings a HTTP URL. This effectively sends a HEAD request and returns <code>true</code> if the response code is in 
 * the 200-399 range.
 * @param url The HTTP URL to be pinged.
 * @param timeout The timeout in millis for both the connection timeout and the response read timeout. Note that
 * the total timeout is effectively two times the given timeout.
 * @return <code>true</code> if the given HTTP URL has returned response code 200-399 on a HEAD request within the
 * given timeout, otherwise <code>false</code>.
 */
public static boolean pingURL(String url, int timeout) {
    url = url.replaceFirst("^https", "http"); // Otherwise an exception may be thrown on invalid SSL certificates.

    try {
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        connection.setConnectTimeout(timeout);
        connection.setReadTimeout(timeout);
        connection.setRequestMethod("HEAD");
        int responseCode = connection.getResponseCode();
        return (200 <= responseCode && responseCode <= 399);
    } catch (IOException exception) {
        return false;
    }
}
BalusC
fonte
3
Obrigado pelos detalhes, respostas como estas são o que faz do SO um ótimo lugar. Testar a disponibilidade do servidor não é suficiente no meu caso, preciso testar a URL (o aplicativo da web pode não estar implantado), portanto, continuarei com a HttpURLConnection. Sobre o HEAD não ser um bom teste: é um bom método se eu souber que o URL de destino suporta HEAD, vou verificar isso.
Sean Patrick Floyd
1
É possível obter java.io.IOException: final inesperado do fluxo em alguns servidores. Para corrigi-lo, é necessário adicionar connection.setRequestProperty ("Accept-Encoding", "musixmatch"); É conhecido problema e relatado em code.google.com
Marcin Waśniowski 27/03
1
@BalusC Porque (200 <= responseCode && responseCode <= 399) será verdadeiro se e somente se (resposta <= 399), o que significa que a condição (200 <= responseCode) é redundante. Então eu pensei que era um erro.
metator 14/02
4
@metator: hein ??? Não é absolutamente redundante. Códigos de resposta inferiores a 200 não são considerados válidos.
BalusC
1
@ BalusC Im alguma situação, esse método parece que não funciona bem. Dê uma olhada aqui stackoverflow.com/questions/25805580/…
AndreaF 6/14
17

Em vez de usar URLConnection, use HttpURLConnection chamando openConnection () no seu objeto de URL.

Em seguida, use getResponseCode () fornecerá a resposta HTTP depois de ler a conexão.

aqui está o código:

    HttpURLConnection connection = null;
    try {
        URL u = new URL("http://www.google.com/");
        connection = (HttpURLConnection) u.openConnection();
        connection.setRequestMethod("HEAD");
        int code = connection.getResponseCode();
        System.out.println("" + code);
        // You can determine on HTTP return code received. 200 is success.
    } catch (MalformedURLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {
        if (connection != null) {
            connection.disconnect();
        }
    }

Verifique também uma pergunta semelhante Como verificar se existe um URL ou retorna 404 com Java?

Espero que isto ajude.

YoK
fonte
7

Você também pode usar HttpURLConnection , que permite definir o método de solicitação (para HEAD, por exemplo). Aqui está um exemplo que mostra como enviar uma solicitação, ler a resposta e desconectar.

Bill the Lizard
fonte
4

O código a seguir executa uma HEADsolicitação para verificar se o site está disponível ou não.

public static boolean isReachable(String targetUrl) throws IOException
{
    HttpURLConnection httpUrlConnection = (HttpURLConnection) new URL(
            targetUrl).openConnection();
    httpUrlConnection.setRequestMethod("HEAD");

    try
    {
        int responseCode = httpUrlConnection.getResponseCode();

        return responseCode == HttpURLConnection.HTTP_OK;
    } catch (UnknownHostException noInternetConnection)
    {
        return false;
    }
}
BullyWiiPlaza
fonte
4

aqui o escritor sugere o seguinte:

public boolean isOnline() {
    Runtime runtime = Runtime.getRuntime();
    try {
        Process ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8");
        int     exitValue = ipProcess.waitFor();
        return (exitValue == 0);
    } catch (IOException | InterruptedException e) { e.printStackTrace(); }
    return false;
}

Possíveis perguntas

  • Isso é realmente rápido o suficiente? Sim, muito rápido!
  • Eu não poderia simplesmente executar ping na minha própria página, que eu quero solicitar de qualquer maneira? Certo! Você pode até verificar os dois, se quiser diferenciar entre “conexão à Internet disponível” e seus próprios servidores, sendo alcançáveis ​​E se o DNS estiver inativo? O DNS do Google (por exemplo, 8.8.8.8) é o maior serviço de DNS público do mundo. A partir de 2013, atende 130 bilhões de solicitações por dia. Digamos que seu aplicativo que não está respondendo provavelmente não seria o assunto do dia.

leia o link. parece muito bom

EDIT: no meu exp de usá-lo, não é tão rápido quanto este método:

public boolean isOnline() {
    NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
    return netInfo != null && netInfo.isConnectedOrConnecting();
}

eles são um pouco diferentes, mas na funcionalidade de verificar apenas a conexão com a Internet, o primeiro método pode ficar lento devido às variáveis ​​de conexão.

Emad
fonte
2

Considere usar a estrutura Restlet, que possui ótima semântica para esse tipo de coisa. É poderoso e flexível.

O código pode ser tão simples quanto:

Client client = new Client(Protocol.HTTP);
Response response = client.get(url);
if (response.getStatus().isError()) {
    // uh oh!
}
Avi Linho
fonte