2010-08-27 2 views
126

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

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

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?

ответ

215

Является ли это какой-то толк (это будет делать то, что я хочу?)

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

Действительно, подключение хост только информирует если хост доступен, а не если контент доступен. Вполне возможно, что веб-сервер запущен без проблем, но 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 section 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

Спасибо за детали, ответы, подобные этим, делают SO прекрасным местом. Тестирование сервера для доступности недостаточно в моем случае, мне нужно проверить URL-адрес (webapp не может быть развернут), поэтому я буду придерживаться HttpURLConnection. О HEAD не является хорошим тестом: это хороший метод, если я знаю, что целевой URL поддерживает HEAD, я проверю это. –

+0

Если это функциональное требование, то «HttpURLConnection» - лучший способ. Вам также хотелось бы определить код ответа. Вы также можете сделать это с помощью 'Socket', но это только добавляет многословие и требует знания спецификации HTTP. – BalusC

+1

Можно получить ** java.io.IOException: неожиданный конец потока ** на некоторых серверах, чтобы исправить его, вам нужно добавить connection.setRequestProperty («Accept-Encoding», «musixmatch»); Известная проблема и сообщается на [code.google.com] (https://code.google.com/p/android/issues/detail?id=24672) –

6

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

16

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

Также проверьте аналогичный вопрос How to check if a URL exists or returns 404 with Java?

Надеется, что это помогает.

1

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

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

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

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

+0

Это должен был комментарий, а не ответ. Подумайте о том, что вы узнали, комментируя соответствующие ответы или сам вопрос. – Khez

+0

Ваша проблема заключалась в том, что вы никогда не закрывали входной поток. Вызов 'disconnect()' предотвращает появление пула соединений, что нежелательно вообще. – EJP

2

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

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

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

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