Предпочтительный способ Java для ping HTTP URL для доступности

мне нужен класс монитора, который регулярно проверяет, доступен ли данный HTTP-URL. Я могу позаботиться о "регулярной" части, используя абстракцию Spring TaskExecutor, так что это не тема здесь. Вопрос: каков предпочтительный способ ping URL в java?

вот мой текущий код в качестве отправной точки:

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. это вообще хорошо (будет ли это делать то, что я хочу)?
  2. Я должен как-то закрыть связь?
  3. Я полагаю, это GET запрос. Есть ли способ отправить HEAD вместо?

7 ответов


это вообще хорошо (будет ли это делать то, что я хочу?)

вы можете сделать это. Другим возможным способом является использование 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.
    }
}

есть еще InetAddress#isReachable():

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

это, однако, явно не тестирует порт 80. Вы рискуете получить ложные негативы из-за брандмауэра, блокирующего другие порты.


я должен как-то закрыть связь?

нет, вам явно не нужно. Он управляется и собирается под капотами.


я полагаю, это запрос GET. Есть ли способ отправить голову вместо этого?

вы можете бросить полученный URLConnection to HttpURLConnection и затем использовать setRequestMethod() для установки метода запроса. Однако вам нужно учитывать, что некоторые плохие webapps или доморощенные серверы могут возвращение ошибка HTTP 405 для головы (т. е. недоступно, не реализовано, не разрешено), в то время как GET работает отлично. Использование GET более надежно, если вы собираетесь проверять ссылки / ресурсы, а не Домены/хосты.


тестирование сервера на доступность недостаточно в моем случае, мне нужно проверить URL (веб-приложение может не быть развернуто)

действительно, подключение хоста только сообщает, если хост доступен, нет, если контент доступен. Может случиться так, что веб-сервер запустился без проблем, но webapp не удалось развернуть во время запуска сервера. Это, однако, обычно не приводит к тому, что весь сервер идет вниз. Вы можете определить это, проверив, является ли код ответа 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

более подробно о кодах состояния см. RFC 2616 раздел 10. Зову connect() кстати, не требуется, если вы определяете данные ответа. Оно будет неявное соединение.

для дальнейшего использования, вот полный пример в вкусе метода утилиты, также с учетом тайм-аутов:

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

вместо использования URLConnection используйте HttpURLConnection вызывая openConnection () на вашем объекте URL.

затем использовать getResponseCode() даст вам ответ HTTP, как только вы прочитаете из соединения.

вот код:

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

также проверьте аналогичный вопрос Как проверить, существует ли URL-адрес или возвращает 404 с Java?

надеюсь, что это помогает.


вы также можете использовать HttpURLConnection, что позволяет установить метод запроса (например, HEAD). вот пример это показывает, как отправить запрос, прочитать ответ и отключить.


следующий код выполняет HEAD запрос на проверку наличия или отсутствия веб-сайта.

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

здесь автор предлагает следующее:

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

Возможные Вопросы

  • это действительно достаточно быстро?Да, очень быстро!
  • не мог бы я просто пинговать свою собственную страницу, которую я хочу просить в любом случае? Конечно! Вы даже можете проверить оба, если хотите. различайте "доступное подключение к интернету" и свое собственное серверы доступны, Что делать, если DNS не работает? Google DNS (например 8.8.8.8) является крупнейшим публичным DNS-сервисом в мире. Как в 2013 году он обслуживает 130 миллиардов запросов в день. Скажем так, ваше приложение не ответ, вероятно, не будет обсуждаться весь день.

читать по ссылке. его, кажется, очень хорошо

изменить: в моем exp использования его, это не так быстро, как этот метод:

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

они немного отличаются, но в функциональности для просто проверки подключения к интернету первый метод может стать медленным из-за переменных подключения.


рассмотрите возможность использования структуры Restlet, которая имеет большую семантику для такого рода вещей. Он мощный и гибкий.

код может быть таким же простым, как:

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

WRT на 2.- лучше закрой его. Однако, это может зависеть от конкретной реализации URLConnection, который используется. Я только что закончил отслеживать утечки ресурсов в нашей системе только из-за этого. Приложение создало множество висячих соединений (согласно lsof; мы запускаем его на JDK1.6) и причина заключалась в том, что мы использовали именно тот фрагмент кода, который вы показали. TCP-соединения не были закрыты, например, возвращены в пул и т. д. - они остались в стабильном состоянии. В в этом случае правильным сценарием является тот, который показан YoK-cast it to (HttpURLConnection) и invoke .разъединять.)(