Бажаний спосіб Java ввести URL-адресу HTTP для наявності


157

Мені потрібен клас монітора, який регулярно перевіряє, чи доступна вказана 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;
}
  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

Детальніше про коди статусу відповіді див. У розділі 10 RFC 2616 . Дзвінки 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;
    }
}

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) буде істинним, якщо і лише тоді (відповідь <= 399), це означає, що умова (200 <= відповідь) є зайвим. Тому я подумав, що це помилка.
метатор

4
@metator: так ??? Це абсолютно не зайве. Коди відповідей нижче 200 не вважаються дійсними.
BalusC

1
@BalusC Im Якась ситуація, здається, цей метод не працює добре. дати подивитися тут 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?

Сподіваюся, це допомагає.



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

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

прочитайте посилання. здається, дуже добре

EDIT: в моєму досвіді його використання, це не так швидко, як цей метод:

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!
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.