Cara Java yang disukai untuk melakukan ping ke URL HTTP untuk ketersediaan

157

Saya memerlukan kelas monitor yang secara teratur memeriksa apakah URL HTTP yang diberikan tersedia. Saya bisa menangani bagian "teratur" menggunakan abstraksi Spring TaskExecutor, jadi itu bukan topik di sini. Pertanyaannya adalah: Apa cara yang disukai untuk melakukan ping URL di java?

Ini kode saya saat ini sebagai titik awal:

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. Apakah ini baik sama sekali (akankah ia melakukan apa yang saya inginkan)?
  2. Apakah saya harus menutup koneksi?
  3. Saya kira ini GETpermintaan. Apakah ada cara untuk mengirim HEAD?
Sean Patrick Floyd
sumber

Jawaban:

267

Apakah ini ada gunanya sama sekali (akankah itu melakukan apa yang saya inginkan?)

Anda bisa melakukannya. Cara layak lainnya adalah menggunakan 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.
    }
}

Ada juga InetAddress#isReachable():

boolean reachable = InetAddress.getByName(hostname).isReachable();

Namun ini tidak secara eksplisit menguji port 80. Anda berisiko mendapatkan negatif palsu karena Firewall memblokir port lain.


Apakah saya harus menutup koneksi?

Tidak, Anda tidak perlu secara eksplisit. Ini ditangani dan dikumpulkan di bawah tenda.


Saya kira ini adalah permintaan GET. Apakah ada cara untuk mengirim KEPALA sebagai gantinya?

Anda bisa melemparkan yang diperoleh URLConnectionke HttpURLConnectiondan kemudian gunakan setRequestMethod()untuk mengatur metode permintaan. Namun, Anda perlu mempertimbangkan bahwa beberapa aplikasi web atau server yang buruk dapat mengembalikan kesalahan HTTP 405 untuk HEAD (yaitu tidak tersedia, tidak diterapkan, tidak diizinkan) sementara GET berfungsi dengan baik. Menggunakan GET lebih dapat diandalkan jika Anda berniat memverifikasi tautan / sumber daya bukan domain / host.


Menguji ketersediaan server tidak cukup dalam kasus saya, saya perlu menguji URL (webapp mungkin tidak digunakan)

Memang, menghubungkan host hanya memberi tahu jika host tersedia, bukan jika konten tersedia. Sebaiknya server web mulai tanpa masalah, tetapi aplikasi web gagal untuk digunakan saat server mulai. Namun ini biasanya tidak menyebabkan seluruh server turun. Anda dapat menentukan itu dengan memeriksa apakah kode respons HTTP adalah 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

Untuk detail lebih lanjut tentang kode status respons, lihat RFC 2616 bagian 10 . Ngomong connect()-ngomong, panggilan tidak diperlukan jika Anda menentukan data respons. Secara implisit akan terhubung.

Untuk referensi di masa mendatang, berikut adalah contoh lengkap dalam hal metode utilitas, juga memperhitungkan batas waktu:

/**
 * 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
sumber
3
Terima kasih atas perinciannya, jawaban seperti inilah yang menjadikan SO tempat yang bagus. Menguji ketersediaan server tidak cukup dalam kasus saya, saya perlu menguji URL (webapp mungkin tidak digunakan), jadi saya akan tetap menggunakan HttpURLConnection. Tentang HEAD bukan tes yang baik: ini adalah metode yang baik jika saya tahu URL target mendukung HEAD, saya akan memeriksanya.
Sean Patrick Floyd
1
Dimungkinkan untuk mendapatkan java.io.IOException: akhir aliran yang tidak terduga pada beberapa server, untuk memperbaikinya Anda perlu menambahkan koneksi.setRequestProperty ("Terima-Pengkodean", "musixmatch"); Masalahnya diketahui dan dilaporkan di code.google.com
Marcin Waśniowski
1
@BalusC Karena (200 <= responseCode && responseCode <= 399) akan benar jika dan hanya jika (response <= 399), yang berarti bahwa kondisi (200 <= responseCode) berlebihan. Jadi saya pikir itu kesalahan.
metator
4
@metator: ya ??? Sama sekali tidak berlebihan. Kode respons yang lebih rendah dari 200 tidak dianggap valid.
BalusC
1
@ BalusC Saya beberapa situasi metode ini tampaknya tidak berfungsi dengan baik. lihat di sini stackoverflow.com/questions/25805580/…
AndreaF
17

Alih-alih menggunakan URLConnection, gunakan HttpURLConnection dengan memanggil openConnection () pada objek URL Anda.

Kemudian gunakan getResponseCode () akan memberi Anda respons HTTP setelah Anda membaca dari koneksi.

di sini adalah kode:

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

Juga periksa pertanyaan serupa. Bagaimana memeriksa apakah URL ada atau mengembalikan 404 dengan Java?

Semoga ini membantu.

YoK
sumber
7

Anda juga dapat menggunakan HttpURLConnection , yang memungkinkan Anda untuk mengatur metode permintaan (ke HEAD misalnya). Berikut ini contoh yang menunjukkan cara mengirim permintaan, membaca respons, dan memutuskan sambungan.

Bill the Lizard
sumber
4

Kode berikut melakukan HEADpermintaan untuk memeriksa apakah situs web tersedia atau tidak.

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
sumber
4

di sini penulis menyarankan ini:

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

Kemungkinan Pertanyaan

  • Apakah ini cukup cepat? Ya, sangat cepat!
  • Tidak bisakah saya hanya ping halaman saya sendiri, yang ingin saya minta? Tentu! Anda bahkan dapat memeriksa keduanya, jika Anda ingin membedakan antara "koneksi internet yang tersedia" dan server Anda sendiri yang dapat dijangkau Bagaimana jika DNSnya turun? Google DNS (mis. 8.8.8.8) adalah layanan DNS publik terbesar di dunia. Per 2013 melayani 130 miliar permintaan sehari. Katakan saja, aplikasi Anda yang tidak merespons mungkin tidak akan menjadi pembicaraan saat ini.

baca tautannya. sepertinya sangat bagus

EDIT: dalam exp saya menggunakannya, itu tidak secepat metode ini:

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

mereka sedikit berbeda tetapi dalam fungsi hanya memeriksa koneksi ke internet metode pertama mungkin menjadi lambat karena variabel koneksi.

Emad
sumber
2

Pertimbangkan untuk menggunakan kerangka kerja Restlet, yang memiliki semantik hebat untuk hal semacam ini. Ini kuat dan fleksibel.

Kode dapat sesederhana:

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