Forma preferida de Java para hacer ping a una URL HTTP para disponibilidad

157

Necesito una clase de monitor que verifique regularmente si una URL HTTP dada está disponible. Puedo ocuparme de la parte "regularmente" usando la abstracción Spring TaskExecutor, así que ese no es el tema aquí. La pregunta es: ¿Cuál es la forma preferida de hacer ping a una URL en Java?

Aquí está mi código actual como punto 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. ¿Es esto algo bueno en absoluto (hará lo que quiero)?
  2. ¿Tengo que cerrar de alguna manera la conexión?
  3. Supongo que esto es una GETsolicitud. ¿Hay alguna forma de enviar en su HEADlugar?
Sean Patrick Floyd
fuente

Respuestas:

267

¿Es esto bueno en absoluto (¿hará lo que quiero?)

Puedes hacerlo Otra forma factible es 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.
    }
}

También está el InetAddress#isReachable():

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

Sin embargo, esto no prueba explícitamente el puerto 80. Corre el riesgo de obtener falsos negativos debido a que un cortafuegos bloquea otros puertos.


¿Tengo que cerrar de alguna manera la conexión?

No, no lo necesitas explícitamente. Se maneja y agrupa bajo las capuchas.


Supongo que esta es una solicitud GET. ¿Hay alguna forma de enviar HEAD en su lugar?

Puede emitir el obtenido URLConnectiona HttpURLConnectiony luego usar setRequestMethod()para establecer el método de la petición. Sin embargo, debe tener en cuenta que algunas aplicaciones web pobres o servidores locales pueden devolver el error HTTP 405 para un HEAD (es decir, no disponible, no implementado, no permitido) mientras que un GET funciona perfectamente bien. El uso de GET es más confiable en caso de que tenga la intención de verificar enlaces / recursos, no dominios / hosts.


Probar la disponibilidad del servidor no es suficiente en mi caso, necesito probar la URL (la aplicación web puede no estar implementada)

De hecho, conectar un host solo informa si el host está disponible, no si el contenido está disponible. Puede suceder que un servidor web haya comenzado sin problemas, pero la aplicación web no se pudo implementar durante el inicio del servidor. Sin embargo, esto generalmente no hará que todo el servidor se caiga. Puede determinar eso comprobando si el código de respuesta HTTP es 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 obtener más detalles sobre los códigos de estado de respuesta, consulte RFC 2616 sección 10 . connect()Por cierto, no es necesario llamar si está determinando los datos de respuesta. Se conectará implícitamente.

Para referencia futura, aquí hay un ejemplo completo del sabor de un método de utilidad, que también tiene en cuenta los tiempos de espera:

/**
 * 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
fuente
3
Gracias por los detalles, respuestas como estas son las que hacen de SO un lugar excelente. Probar la disponibilidad del servidor no es suficiente en mi caso, necesito probar la URL (es posible que la aplicación web no esté implementada), por lo que me quedaré con la conexión HttpURLConnection. Sobre HEAD no es una buena prueba: es un buen método si sé que la URL de destino es compatible con HEAD, lo comprobaré.
Sean Patrick Floyd el
1
Es posible obtener java.io.IOException: final inesperado de la transmisión en algunos servidores, para solucionarlo necesita agregar connection.setRequestProperty ("Accept-Encoding", "musixmatch"); Es un problema conocido e informado en code.google.com
Marcin Waśniowski el
1
@BalusC Porque (200 <= responseCode && responseCode <= 399) será verdadero si y solo si (response <= 399), lo que significa que la condición (200 <= responseCode) es redundante. Entonces pensé que era un error.
metator
44
@metator: ¿eh? No es absolutamente redundante. Los códigos de respuesta inferiores a 200 no se consideran válidos.
BalusC
1
@BalusC En alguna situación, este método parece que no funciona bien. echa un vistazo aquí stackoverflow.com/questions/25805580/…
AndreaF
17

En lugar de usar URLConnection, use HttpURLConnection llamando a openConnection () en su objeto URL.

Luego, use getResponseCode () le dará la respuesta HTTP una vez que haya leído desde la conexión.

aquí está el 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();
        }
    }

También verifique una pregunta similar ¿ Cómo verificar si una URL existe o devuelve 404 con Java?

Espero que esto ayude.

YoK
fuente
7

También puede usar HttpURLConnection , que le permite establecer el método de solicitud (en HEAD, por ejemplo). Aquí hay un ejemplo que muestra cómo enviar una solicitud, leer la respuesta y desconectarse.

Bill el lagarto
fuente
4

El siguiente código realiza una HEADsolicitud para verificar si el sitio web está disponible o no.

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

Aquí el escritor sugiere esto:

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

Posibles preguntas

  • ¿Es realmente lo suficientemente rápido? ¡Sí, muy rápido!
  • ¿No podría simplemente hacer ping a mi propia página, que quiero solicitar de todos modos? ¡Por supuesto! Incluso puede verificar ambos, si desea diferenciar entre "conexión a Internet disponible" y sus propios servidores accesibles ¿Qué pasa si el DNS está caído? Google DNS (por ejemplo, 8.8.8.8) es el servicio de DNS público más grande del mundo. A partir de 2013 atiende 130 mil millones de solicitudes por día. Digamos que su aplicación que no responde probablemente no sea la conversación del día.

lee el enlace. parece muy bueno

EDITAR: en mi exp de usarlo, no es tan rápido como este método:

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

son un poco diferentes, pero en la funcionalidad de solo verificar la conexión a Internet, el primer método puede ser lento debido a las variables de conexión.

Emad
fuente
2

Considere usar el marco Restlet, que tiene una gran semántica para este tipo de cosas. Es potente y flexible.

El código podría ser tan simple como:

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