首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 檢測 URL 連線狀態的實作方法與最佳實踐

     編輯:Java 相關知識

前言

在分散式系統、微服務架構或爬蟲開發中,檢測遠端 URL 是否可達、回應狀態碼是否正常,是常見的需求。本文將介紹 Java 中檢測 URL 連線的多種方法,並提供生產環境可用的完整程式碼。

核心 API 介紹

Java 標準庫提供 java.net.URL 和 java.net.HttpURLConnection 兩個核心類別來處理 HTTP 連線:

  • URL:代表統一資源定位器,負責解析協定、主機、埠號、路徑等
  • HttpURLConnection:HTTP 專用的連線類別,支援 GET、POST 等方法,可設定超時、重導向、請求標頭等

基本連線測試實作

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

public class UrlConnectivityChecker {

    /**
     * 檢測 URL 是否可正常連線
     * @param urlString 目標 URL 字串
     * @param timeoutMillis 連線超時時間(毫秒)
     * @param readTimeoutMillis 讀取超時時間(毫秒)
     * @return HTTP 狀態碼,連線失敗回傳 -1
     */
    public static int checkUrlStatus(String urlString, int timeoutMillis, int readTimeoutMillis) {
        HttpURLConnection connection = null;
        try {
            URL url = new URL(urlString);
            connection = (HttpURLConnection) url.openConnection();
            
            // 關鍵設定
            connection.setRequestMethod("HEAD");        // 僅請求標頭,不下載內文,較輕量
            connection.setConnectTimeout(timeoutMillis); // 連線階段超時
            connection.setReadTimeout(readTimeoutMillis); // 讀取階段超時
            connection.setInstanceFollowRedirects(true); // 自動追蹤重導向
            connection.setUseCaches(false);              // 不使用快取
            
            // 發送請求並取得狀態碼
            int responseCode = connection.getResponseCode();
            return responseCode;
            
        } catch (IOException e) {
            // 網路異常、DNS 解析失敗、連線被拒等情況
            System.err.println("URL 檢測失敗: " + urlString + " - " + e.getMessage());
            return -1;
        } finally {
            if (connection != null) {
                connection.disconnect(); // 釋放連線資源
            }
        }
    }

    /**
     * 判斷 URL 是否健康(回傳 2xx 或 3xx)
     */
    public static boolean isUrlHealthy(String urlString) {
        int code = checkUrlStatus(urlString, 3000, 5000);
        return code >= 200 && code < 400;
    }

    public static void main(String[] args) {
        String[] testUrls = {
            "https://www.google.com",
            "https://httpbin.org/status/200",
            "https://httpbin.org/status/404",
            "https://httpbin.org/status/500",
            "https://invalid-domain-xyz-123.com"
        };

        for (String url : testUrls) {
            int status = checkUrlStatus(url, 3000, 5000);
            String statusDesc = (status >= 200 && status < 300) ? "成功" : 
                               (status >= 300 && status < 400) ? "重導向" :
                               (status >= 400 && status < 500) ? "客戶端錯誤" :
                               (status >= 500) ? "伺服器錯誤" : "連線失敗";
            System.out.printf("%-40s %4d %s%n", url, status, statusDesc);
        }
    }
}

進階:帶重試機制的健康檢查

生產環境中,網路抖動可能導致暫時性失敗,建議加入重試邏輯:

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

public class RobustUrlChecker {

    /**
     * 帶指數退避重試的 URL 檢測
     * @param urlString 目標 URL
     * @param maxRetries 最大重試次數
     * @param baseDelayMs 基礎延遲(毫秒)
     * @param timeoutMs 連線/讀取超時
     * @return HTTP 狀態碼,失敗回傳 -1
     */
    public static int checkWithRetry(String urlString, int maxRetries, int baseDelayMs, int timeoutMs) {
        int attempt = 0;
        IOException lastException = null;

        while (attempt <= maxRetries) {
            HttpURLConnection conn = null;
            try {
                URL url = new URL(urlString);
                conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("HEAD");
                conn.setConnectTimeout(timeoutMs);
                conn.setReadTimeout(timeoutMs);
                conn.setInstanceFollowRedirects(true);
                conn.setUseCaches(false);

                int responseCode = conn.getResponseCode();
                
                // 5xx 錯誤視為可重試
                if (responseCode >= 500 && responseCode < 600) {
                    throw new IOException("Server error: " + responseCode);
                }
                return responseCode;

            } catch (IOException e) {
                lastException = e;
                attempt++;
                if (attempt <= maxRetries) {
                    // 指數退避:1s, 2s, 4s, 8s...
                    long delay = (long) (baseDelayMs * Math.pow(2, attempt - 1));
                    try {
                        Thread.sleep(delay);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            } finally {
                if (conn != null) conn.disconnect();
            }
        }
        
        System.err.println("重試耗盡: " + urlString + " - " + lastException.getMessage());
        return -1;
    }
}

非同步批量檢測(高效能場景)

需同時檢測大量 URL 時,可使用 CompletableFuture 並行處理:

import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

public class AsyncUrlChecker {

    private static final ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void checkUrlsAsync(List urls) {
        List> futures = urls.stream()
            .map(url -> CompletableFuture.supplyAsync(() -> checkUrl(url), executor))
            .collect(Collectors.toList());

        // 等待所有完成並收集結果
        List results = futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());

        results.forEach(r -> System.out.println(r));
    }

    private static UrlCheckResult checkUrl(String url) {
        long start = System.currentTimeMillis();
        int status = UrlConnectivityChecker.checkUrlStatus(url, 3000, 5000);
        long elapsed = System.currentTimeMillis() - start;
        return new UrlCheckResult(url, status, elapsed);
    }

    static class UrlCheckResult {
        final String url;
        final int statusCode;
        final long latencyMs;

        UrlCheckResult(String url, int statusCode, long latencyMs) {
            this.url = url;
            this.statusCode = statusCode;
            this.latencyMs = latencyMs;
        }

        @Override
        public String toString() {
            return String.format("%-50s %4d %6dms", url, statusCode, latencyMs);
        }
    }
}

HTTP 狀態碼快速參考

範圍分類常見碼含義
1xx資訊回應100 Continue請求已接收,繼續處理
2xx成功200 OK, 201 Created, 204 No Content請求成功處理
3xx重導向301 Moved Permanently, 302 Found, 304 Not Modified需進一步動作完成請求
4xx客戶端錯誤400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found請求語法錯誤或無權限
5xx伺服器錯誤500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout伺服器處理失敗

最佳實踐總結

  1. 使用 HEAD 請求:僅檢測可達性時,HEAD 比 GET 更輕量,不下載回應主體
  2. 設定合理超時:連線超時 3-5 秒,讀取超時 5-10 秒,避免長時間阻塞
  3. 處理重導向:預設開啟自動追蹤,特殊需求可關閉手動處理
  4. 釋放資源:finally 區塊必須呼叫 disconnect()
  5. 區分錯誤類型:網路異常(-1)vs HTTP 錯誤碼(4xx/5xx),採取不同策略
  6. 加入重試:針對 5xx、網路抖動實作指數退避重試
  7. 並行處理:批量檢測時使用執行緒池或虛擬執行緒提升吞吐量

常見問題排查

Q: 為什麼某些網站回傳 403 或 405?
A: 伺服器可能封鎖無 User-Agent 的請求,或不支援 HEAD 方法。解決:設定 User-Agent 標頭,或改用 GET 方法。
Q: HTTPS 網站憑證驗證失敗怎麼辦?
A: 測試環境可建立信任所有憑證的 TrustManager(僅限測試);生產環境應正確配置 CA 憑證。
Q: 如何檢測 TCP 連線而不發送 HTTP 請求?
A: 使用 Socket 直接連線指定埠號,適合檢測非 HTTP 服務(如資料庫、快取伺服器)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved