Bevorzugte Java-Methode zum Pingen einer HTTP-URL für die Verfügbarkeit

157

Ich benötige eine Monitorklasse, die regelmäßig überprüft, ob eine bestimmte HTTP-URL verfügbar ist. Ich kann mich mit der Spring TaskExecutor-Abstraktion um den "regulären" Teil kümmern, daher ist das hier nicht das Thema. Die Frage ist: Was ist der bevorzugte Weg, um eine URL in Java zu pingen?

Hier ist mein aktueller Code als Ausgangspunkt:

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. Ist das überhaupt gut (wird es tun, was ich will)?
  2. Muss ich die Verbindung irgendwie schließen?
  3. Ich nehme an, das ist eine GETBitte. Gibt es eine Möglichkeit, HEADstattdessen zu senden ?
Sean Patrick Floyd
quelle

Antworten:

267

Ist das überhaupt gut (wird es tun, was ich will?)

Sie können dies tun. Ein anderer möglicher Weg ist die Verwendung 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.
    }
}

Es gibt auch die InetAddress#isReachable():

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

Dies testet Port 80 jedoch nicht explizit. Es besteht die Gefahr, dass aufgrund einer Firewall, die andere Ports blockiert, falsche Negative angezeigt werden.


Muss ich die Verbindung irgendwie schließen?

Nein, das brauchen Sie nicht explizit. Es wird unter den Hauben gehandhabt und gepoolt.


Ich nehme an, dies ist eine GET-Anfrage. Gibt es eine Möglichkeit, stattdessen HEAD zu senden?

Sie können das erhaltene URLConnectionumwandeln HttpURLConnectionund dann verwenden setRequestMethod(), um die Anforderungsmethode festzulegen. Sie müssen jedoch berücksichtigen, dass einige schlechte Webanwendungen oder selbst entwickelte Server möglicherweise einen HTTP 405-Fehler für einen HEAD zurückgeben (dh nicht verfügbar, nicht implementiert, nicht zulässig), während ein GET einwandfrei funktioniert. Die Verwendung von GET ist zuverlässiger, wenn Sie Links / Ressourcen überprüfen möchten, nicht Domänen / Hosts.


Das Testen des Servers auf Verfügbarkeit reicht in meinem Fall nicht aus. Ich muss die URL testen (die Webanwendung wird möglicherweise nicht bereitgestellt).

In der Tat informiert das Verbinden eines Hosts nur, wenn der Host verfügbar ist, nicht, wenn der Inhalt verfügbar ist. Es kann genauso gut passieren, dass ein Webserver ohne Probleme gestartet wurde, die Webanwendung jedoch beim Start des Servers nicht bereitgestellt werden konnte. Dies führt jedoch normalerweise nicht dazu, dass der gesamte Server ausfällt. Sie können dies feststellen, indem Sie überprüfen, ob der HTTP-Antwortcode 200 lautet.

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

Weitere Informationen zu Antwortstatuscodes finden Sie in Abschnitt 10 von RFC 2616 . Ein Anruf connect()ist übrigens nicht erforderlich, wenn Sie die Antwortdaten ermitteln. Es wird implizit eine Verbindung hergestellt.

Zum späteren Nachschlagen finden Sie hier ein vollständiges Beispiel für eine Dienstprogrammmethode, das auch Zeitüberschreitungen berücksichtigt:

/**
 * 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
quelle
3
Vielen Dank für die Details. Antworten wie diese machen SO zu einem großartigen Ort. Das Testen des Servers auf Verfügbarkeit reicht in meinem Fall nicht aus. Ich muss die URL testen (die Webanwendung wird möglicherweise nicht bereitgestellt), daher bleibe ich bei der HttpURLConnection. Über HEAD ist kein guter Test: Es ist eine gute Methode, wenn ich weiß, dass die Ziel-URL HEAD unterstützt. Ich werde das überprüfen.
Sean Patrick Floyd
1
Es ist möglich, java.io.IOException zu erhalten : Unerwartetes Ende des Streams auf einigen Servern. Um dies zu beheben, müssen Sie connection.setRequestProperty ("Accept-Encoding", "musixmatch") hinzufügen. Es ist ein bekanntes Problem und wird unter code.google.com
Marcin Waśniowski
1
@BalusC Weil (200 <= responseCode && responseCode <= 399) genau dann wahr ist, wenn (response <= 399), was bedeutet, dass die Bedingung (200 <= responseCode) redundant ist. Also dachte ich, es sei ein Fehler.
Metator
4
@metator: huh ??? Es ist absolut nicht überflüssig. Antwortcodes unter 200 gelten nicht als gültig.
BalusC
1
@ BalusC In einigen Situationen scheint diese Methode nicht gut zu funktionieren. Werfen
Sie
17

Verwenden Sie anstelle von URLConnection HttpURLConnection, indem Sie openConnection () für Ihr URL-Objekt aufrufen.

Wenn Sie dann getResponseCode () verwenden , erhalten Sie die HTTP-Antwort, sobald Sie von der Verbindung gelesen haben.

Hier ist Code:

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

Überprüfen Sie auch eine ähnliche Frage. Wie kann ich überprüfen, ob eine URL vorhanden ist oder 404 mit Java zurückgibt?

Hoffe das hilft.

YoK
quelle
4

Der folgende Code führt eine HEADAnforderung aus, um zu überprüfen, ob die Website verfügbar ist oder nicht.

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
quelle
4

hier schlägt der Schriftsteller Folgendes vor:

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

Mögliche Fragen

  • Ist das wirklich schnell genug? Ja, sehr schnell!
  • Könnte ich nicht einfach meine eigene Seite anpingen, die ich sowieso anfordern möchte? Sicher! Sie können sogar beide überprüfen, wenn Sie zwischen "Internetverbindung verfügbar" und Ihren eigenen Servern unterscheiden möchten, die erreichbar sind. Was ist, wenn der DNS ausgefallen ist? Google DNS (z. B. 8.8.8.8) ist der größte öffentliche DNS-Dienst der Welt. Ab 2013 werden täglich 130 Milliarden Anfragen bearbeitet. Sagen wir einfach, Ihre App, die nicht reagiert, ist wahrscheinlich nicht das Thema des Tages.

Lesen Sie den Link. es scheint sehr gut zu sein

EDIT: In meiner Erfahrung ist es nicht so schnell wie diese Methode:

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

Sie sind etwas anders, aber in der Funktionalität zum Überprüfen der Internetverbindung kann die erste Methode aufgrund der Verbindungsvariablen langsam werden.

Emad
quelle
2

Erwägen Sie die Verwendung des Restlet-Frameworks, das eine hervorragende Semantik für diese Art von Dingen bietet. Es ist leistungsstark und flexibel.

Der Code könnte so einfach sein wie:

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