Modo Java preferito per eseguire il ping di un URL HTTP per la disponibilità


157

Ho bisogno di una classe di monitoraggio che controlli regolarmente se un determinato URL HTTP è disponibile. Posso occuparmi della parte "regolarmente" usando l'astrazione Spring TaskExecutor, quindi non è questo l'argomento. La domanda è: qual è il modo preferito per eseguire il ping di un URL in Java?

Ecco il mio codice attuale come punto di partenza:

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. È buono (farà quello che voglio)?
  2. Devo in qualche modo chiudere la connessione?
  3. Suppongo che questa sia una GETrichiesta. C'è un modo per inviare HEADinvece?

Risposte:


267

È buono (farà quello che voglio?)

Puoi farlo. Un altro modo fattibile sta usando 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.
    }
}

C'è anche il InetAddress#isReachable():

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

Questo, tuttavia, non verifica esplicitamente la porta 80. Rischi di ottenere falsi negativi a causa di un firewall che blocca altre porte.


Devo in qualche modo chiudere la connessione?

No, non hai esplicitamente bisogno. È gestito e raggruppato sotto i cappucci.


Suppongo che questa sia una richiesta GET. C'è un modo per inviare HEAD invece?

È possibile trasmettere il ottenuto URLConnectiona HttpURLConnectione quindi utilizzare setRequestMethod()per impostare il metodo di richiesta. Tuttavia, è necessario tenere conto del fatto che alcune applicazioni web o server poveri possono restituire l' errore HTTP 405 per un HEAD (ovvero non disponibile, non implementato, non consentito) mentre un GET funziona perfettamente. L'uso di GET è più affidabile nel caso in cui si intenda verificare collegamenti / risorse non domini / host.


Testare la disponibilità del server non è abbastanza nel mio caso, devo testare l'URL (la webapp potrebbe non essere distribuita)

In effetti, la connessione di un host informa solo se l'host è disponibile, non se il contenuto è disponibile. Può anche succedere che un server web sia stato avviato senza problemi, ma la webapp non è stata distribuita all'avvio del server. Questo, tuttavia, di solito non causa l'interruzione dell'intero server. Puoi determinarlo controllando se il codice di risposta 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

Per maggiori dettagli sui codici di stato della risposta, consultare la sezione 10 di RFC 2616 . La chiamata non connect()è assolutamente necessaria se si determinano i dati di risposta. Si collegherà implicitamente.

Per riferimento futuro, ecco un esempio completo nel sapore di un metodo di utilità, tenendo conto anche dei timeout:

/**
 * 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;
    }
}

3
Grazie per i dettagli, risposte come queste sono ciò che rende SO un ottimo posto. Testare la disponibilità del server non è abbastanza nel mio caso, ho bisogno di testare l'URL (la webapp potrebbe non essere distribuita), quindi continuerò con la connessione HttpURLC. Informazioni su HEAD non è un buon test: è un buon metodo se so che l'URL di destinazione supporta HEAD, lo verificherò.
Sean Patrick Floyd,

1
È possibile ottenere java.io.IOException: fine imprevista dello streaming su alcuni server, per risolverlo è necessario aggiungere connection.setRequestProperty ("Accept-Encoding", "musixmatch"); È un problema noto e segnalato su code.google.com
Marcin Waśniowski il

1
@BalusC Perché (200 <= responseCode && responseCode <= 399) sarà vero se e solo se (response <= 399), il che significa che la condizione (200 <= responseCode) è ridondante. Quindi ho pensato che fosse un errore.
contatore

4
@metator: eh ??? Non è assolutamente ridondante. I codici di risposta inferiori a 200 non sono considerati validi.
BalusC

1
@BalusC In alcune situazioni questo metodo sembra non funzionare bene. dare un'occhiata qui stackoverflow.com/questions/25805580/...
AndreaF

17

Invece di usare URLConnection usa HttpURLConnection chiamando openConnection () sul tuo oggetto URL.

Quindi usa getResponseCode () ti darà la risposta HTTP dopo aver letto dalla connessione.

ecco il codice:

    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();
        }
    }

Verifica anche domande simili Come verificare se esiste un URL o restituisce 404 con Java?

Spero che questo ti aiuti.



4

Il codice seguente esegue una HEADrichiesta per verificare se il sito Web è disponibile o meno.

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;
    }
}

4

qui lo scrittore suggerisce questo:

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;
}

Possibili domande

  • È davvero abbastanza veloce? Sì, molto veloce!
  • Non potrei semplicemente eseguire il ping della mia pagina, che voglio comunque richiedere? Sicuro! Potresti anche controllare entrambi, se vuoi distinguere tra "connessione Internet disponibile" e i tuoi server sono raggiungibili Cosa succede se il DNS è inattivo? Google DNS (ad es. 8.8.8.8) è il più grande servizio DNS pubblico al mondo. A partire dal 2013 serve 130 miliardi di richieste al giorno. Diciamo solo che la tua app non risponde probabilmente non sarebbe il discorso del giorno.

leggi il link. sembra molto buono

EDIT: nella mia esperienza di usarlo, non è veloce come questo metodo:

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

sono un po 'diversi ma nella funzionalità di controllo della connessione a Internet il primo metodo potrebbe rallentare a causa delle variabili di connessione.


2

Prendi in considerazione l'utilizzo del framework Restlet, che ha un'ottima semantica per questo genere di cose. È potente e flessibile.

Il codice potrebbe essere semplice come:

Client client = new Client(Protocol.HTTP);
Response response = client.get(url);
if (response.getStatus().isError()) {
    // uh oh!
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.