Preferowany sposób pingowania adresu URL HTTP w celu sprawdzenia dostępności

157

Potrzebuję klasy monitora, która regularnie sprawdza, czy podany adres URL HTTP jest dostępny. Mogę zająć się „regularnie” częścią za pomocą abstrakcji Spring TaskExecutor, więc nie o to tutaj chodzi. Pytanie brzmi: Jaki jest preferowany sposób pingowania adresu URL w Javie?

Oto mój obecny kod jako punkt wyjścia:

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. Czy to w ogóle coś dobrego (czy zrobi to, co chcę)?
  2. Czy muszę jakoś zamknąć połączenie?
  3. Przypuszczam, że to GETprośba. Czy jest sposób na wysłanie HEADzamiast tego?
Sean Patrick Floyd
źródło

Odpowiedzi:

267

Czy to w ogóle jest dobre (czy zrobi to, co chcę?)

Możesz to zrobić. Innym możliwym sposobem jest użycie 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.
    }
}

Jest też InetAddress#isReachable():

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

Nie oznacza to jednak jawnego testowania portu 80. Istnieje ryzyko uzyskania fałszywych wyników negatywnych, ponieważ zapora blokuje inne porty.


Czy muszę jakoś zamknąć połączenie?

Nie, nie potrzebujesz tego wyraźnie. Jest obsługiwany i gromadzony pod maskami.


Przypuszczam, że jest to żądanie GET. Czy istnieje sposób, aby zamiast tego wysłać HEAD?

Możesz rzutować otrzymane URLConnectionna, HttpURLConnectiona następnie użyć, setRequestMethod()aby ustawić metodę żądania. Należy jednak wziąć pod uwagę, że niektóre słabe aplikacje internetowe lub własne serwery mogą zwracać błąd HTTP 405 dla HEAD (tj. Niedostępny, nie zaimplementowany, niedozwolony), podczas gdy GET działa doskonale. Korzystanie z GET jest bardziej niezawodne w przypadku, gdy zamierzasz weryfikować linki / zasoby, a nie domeny / hosty.


Testowanie serwera pod kątem dostępności w moim przypadku nie wystarczy, muszę przetestować adres URL (aplikacja internetowa może nie zostać wdrożona)

Rzeczywiście, podłączenie hosta informuje tylko o tym, czy host jest dostępny, a nie o tym, czy zawartość jest dostępna. Równie dobrze może się zdarzyć, że serwer WWW uruchomił się bez problemów, ale aplikacja internetowa nie została wdrożona podczas uruchamiania serwera. Zwykle jednak nie powoduje to awarii całego serwera. Możesz to ustalić, sprawdzając, czy kod odpowiedzi HTTP to 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

Więcej informacji na temat kodów stanu odpowiedzi można znaleźć w dokumencie RFC 2616, sekcja 10 . Nawiasem connect()mówiąc, dzwonienie nie jest potrzebne, jeśli określasz dane odpowiedzi. Połączy się niejawnie.

Dla przyszłego odniesienia, oto pełny przykład metody użytkowej, uwzględniający również limity czasu:

/**
 * 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
źródło
3
Dzięki za szczegóły, takie odpowiedzi sprawiają, że TAK jest wspaniałym miejscem. Testowanie serwera pod kątem dostępności w moim przypadku nie wystarczy, muszę przetestować adres URL (aplikacja internetowa może nie zostać wdrożona), więc pozostanę przy HttpURLConnection. O HEAD nie jest dobrym testem: to dobra metoda, jeśli wiem, że docelowy adres URL obsługuje HEAD, sprawdzę to.
Sean Patrick Floyd
1
Możliwe jest uzyskanie wyjątku java.io.IO: nieoczekiwany koniec strumienia na niektórych serwerach, aby to naprawić, musisz dodać connection.setRequestProperty ("Accept-Encoding", "musixmatch"); Znany problem i zgłoszony na code.google.com
Marcin Waśniowski
1
@BalusC Ponieważ (200 <= responseCode && responseCode <= 399) będzie prawdziwe wtedy i tylko wtedy, gdy (response <= 399), co oznacza, że ​​warunek (200 <= responseCode) jest redundantny. Więc pomyślałem, że to pomyłka.
metator
4
@metator: huh ??? To absolutnie nie jest zbędne. Kody odpowiedzi mniejsze niż 200 nie są uważane za ważne.
BalusC
1
@BalusC Im jakaś sytuacja wydaje się, że ta metoda nie działa dobrze. spójrz tutaj stackoverflow.com/questions/25805580/…
AndreaF
17

Zamiast używać URLConnection, użyj HttpURLConnection , wywołując openConnection () na swoim obiekcie URL.

Następnie użyj getResponseCode (), aby otrzymać odpowiedź HTTP po odczytaniu połączenia.

oto kod:

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

Sprawdź również podobne pytanie Jak sprawdzić, czy adres URL istnieje lub zwraca 404 w Javie?

Mam nadzieję że to pomoże.

YoK
źródło
7

Możesz także użyć HttpURLConnection , który pozwala ustawić metodę żądania (na przykład na HEAD). Oto przykład, który pokazuje, jak wysłać żądanie, przeczytać odpowiedź i rozłączyć się.

Bill the Lizard
źródło
4

Poniższy kod wykonuje HEADżądanie sprawdzenia, czy witryna jest dostępna, czy nie.

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
źródło
4

tutaj autor sugeruje to:

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

Możliwe pytania

  • Czy to naprawdę wystarczająco szybko? Tak, bardzo szybko!
  • Czy nie mogę po prostu pingować mojej własnej strony, o którą mimo wszystko chcę poprosić? Pewnie! Możesz nawet sprawdzić oba, jeśli chcesz odróżnić „dostępne połączenie internetowe” od dostępności własnych serwerów. A jeśli DNS nie działa? Google DNS (np. 8.8.8.8) to największa publiczna usługa DNS na świecie. Od 2013 r. Obsługuje 130 miliardów żądań dziennie. Powiedzmy, że Twoja aplikacja nie odpowiada prawdopodobnie nie będzie tematem rozmów.

przeczytaj link. wydaje się bardzo dobry

EDYCJA: w moim doświadczeniu korzystania z niej nie jest tak szybka jak ta metoda:

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

są nieco inne, ale w funkcji sprawdzania połączenia z Internetem pierwsza metoda może stać się wolna z powodu zmiennych połączenia.

Emad
źródło
2

Rozważ użycie frameworka Restlet, który ma świetną semantykę do tego typu rzeczy. Jest potężny i elastyczny.

Kod mógłby być tak prosty, jak:

Client client = new Client(Protocol.HTTP);
Response response = client.get(url);
if (response.getStatus().isError()) {
    // uh oh!
}
Avi Flax
źródło