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

Мне нужен класс монитора, который регулярно проверяет, доступен ли данный URL-адрес HTTP. Я могу позаботиться о "регулярной" части, используя абстракцию 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;
}
  • Является ли это вообще хорошим (будет ли он делать то, что я хочу)?
  • Должен ли я как-то закрыть соединение?
  • Я предполагаю, что это запрос GET. Есть ли способ отправить HEAD вместо этого?

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

Вы также можете использовать 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;
    }
}

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

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

Ответ 7

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