Предпочтительный способ Java для проверки связи с URL-адресом HTTP для доступности - PullRequest
151 голосов
/ 27 августа 2010

Мне нужен класс монитора, который регулярно проверяет, доступен ли данный HTTP-URL. Я могу позаботиться о «регулярной» части, используя абстракцию Spring TaskExecutor, так что это не тема здесь. Вопрос: Каков предпочтительный способ пропинговать 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 ]

252 голосов
/ 27 августа 2010

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

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

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


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

Действительно, подключение хоста только информирует о доступности хоста, а не о доступности контента. Также может случиться так, что веб-сервер запустился без проблем, но веб-приложение не удалось развернуть во время запуска сервера. Это, однако, обычно не приводит к отказу всего сервера. Это можно определить, проверив, равен ли код ответа 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;
    }
}
17 голосов
/ 27 августа 2010

Вместо использования 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?

Надеюсь, это поможет.

7 голосов
/ 27 августа 2010

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

4 голосов
/ 04 апреля 2015

Следующий код выполняет запрос 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;
    }
}
3 голосов
/ 29 августа 2017

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

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 миллиардов запросов в день.Скажем так, ваше приложение не отвечает, вероятно, не будет темой дня.

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

РЕДАКТИРОВАТЬ: в моем опыте использования, это не так быстро, как этот метод:

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

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

2 голосов
/ 27 августа 2010

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

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

Client client = new Client(Protocol.HTTP);
Response response = client.get(url);
if (response.getStatus().isError()) {
    // uh oh!
}
0 голосов
/ 19 января 2013

в отношении 2 .: лучше закрыть его. Однако это может зависеть от конкретной реализации URLConnection, которая используется. Я только что закончил отслеживать утечку ресурсов в нашей системе именно из-за этого. Приложение генерировало множество зависших соединений (согласно lsof; мы запускаем его на JDK1.6), и причина была в том, что мы использовали именно тот фрагмент кода, который вы показали. Соединения TCP не были закрыты, например, вернулся в бассейн и т. д. - они были оставлены в УСТАНОВЛЕННОМ состоянии. В этом случае правильным сценарием является сценарий, показанный YoK - приведите его к (HttpURLConnection) и вызовите .disconnect ().

...