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

157

Мне нужен класс монитора, который регулярно проверяет, доступен ли данный 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вместо?
Шон Патрик Флойд
источник

Ответы:

267

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

Вы можете сделать это. Другой возможный способ использования 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 для 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;
    }
}
BalusC
источник
3
Спасибо за детали, ответы, подобные этим, делают SO великолепным местом. В моем случае недостаточно проверить доступность сервера, мне нужно протестировать URL-адрес (веб-приложение может быть не развернуто), поэтому я буду придерживаться HttpURLConnection. О том, что HEAD не является хорошим тестом: это хороший метод, если я знаю, что целевой URL поддерживает HEAD, я проверю это.
Шон Патрик Флойд
1
Возможно получить java.io.IOException: неожиданный конец потока на некоторых серверах, чтобы исправить это, вам нужно добавить connection.setRequestProperty ("Accept-Encoding", "musixmatch"); Это известная проблема, о которой сообщается на code.google.com
Marcin Waśniowski
1
@BalusC Потому что (200 <= responseCode && responseCode <= 399) будет истинным тогда и только тогда, когда (response <= 399), что означает, что условие (200 <= responseCode) является избыточным. Поэтому я подумал, что это ошибка.
Метатор
4
@ метатор: да ??? Это абсолютно не избыточно. Коды ответов ниже 200 не считаются действительными.
BalusC
1
@BalusC В какой-то ситуации этот метод кажется неэффективным. посмотрите здесь stackoverflow.com/questions/25805580/…
AndreaF
17

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

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

Билл Ящерица
источник
4

Следующий код выполняет 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;
    }
}
BullyWiiPlaza
источник
4

здесь автор предлагает это:

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

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

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

Client client = new Client(Protocol.HTTP);
Response response = client.get(url);
if (response.getStatus().isError()) {
    // uh oh!
}
Ави Лен
источник