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

Java 清理 DNS 緩存:InetAddress 緩存機制深度解析與四種清理方案

     編輯:Java 相關知識

問題背景:DNS 變更後 Java 應用不生效

Java 為提高解析性能,在 InetAddress 內部維護 DNS 緩存。默認策略:

  • 解析成功:緩存 永久(networkaddress.cache.ttl = -1,JDK 11+ 默認 30 秒);
  • 解析失敗:緩存 10 秒(networkaddress.cache.negative.ttl = 10)。

導致域名 IP 變更(如藍綠部署、災備切換)後,Java 進程需重啟才能生效。

InetAddress 緩存內部結構(JDK 8/11/17 通用)


// 簡化版源碼結構
class InetAddress {
    // 緩存類
    static class Cache {
        CacheType type;           // 緩存類型
        Map cache; // key=hostname, value=InetAddress[] 或異常
    }
    static Cache addressCache;    // 成功緩存
    static Cache negativeCache;   // 失敗緩存
}

方案一:啟動參數全局禁用/縮短 TTL(推薦生產)


# 完全禁用緩存(性能損耗大)
-Dsun.net.inetaddr.ttl=0
-Dsun.net.inetaddr.negative.ttl=0

# 或設置較短 TTL,如 30 秒
-Dsun.net.inetaddr.ttl=30
-Dsun.net.inetaddr.negative.ttl=5

優點:零代碼侵入、進程級生效;缺點:需重啟、影響所有解析性能。

方案二:Security 屬性動態設置(需在首次解析前)


// 必須在 InetAddress.getByName() 等首次調用前執行
java.security.Security.setProperty("networkaddress.cache.ttl", "30");
java.security.Security.setProperty("networkaddress.cache.negative.ttl", "5");

限制:類加載時初始化緩存策略,動態修改對已緩存條目無效,僅影響後續新條目。

方案三:修改 JRE 安全配置文件(永久生效)


# 文件:$JAVA_HOME/conf/security/java.security (JDK 9+) 或 jre/lib/security/java.security (JDK 8)
networkaddress.cache.ttl=30
networkaddress.cache.negative.ttl=5

優點:機器級生效,無需修改啟動腳本;缺點:影響該 JRE 所有應用,需運維權限。

方案四:反射強制清理已緩存條目(熱修復、慎用)


public final class DnsCacheCleaner {
    private DnsCacheCleaner() {}

    /** 清除指定主機名的緩存 */
    public static void clearHost(String hostname) {
        try {
            Class clazz = InetAddress.class;
            // 1. 獲取 addressCache 字段
            Field cacheField = clazz.getDeclaredField("addressCache");
            cacheField.setAccessible(true);
            Object cache = cacheField.get(null);

            // 2. 獲取內部 Map 字段
            Field mapField = cache.getClass().getDeclaredField("cache");
            mapField.setAccessible(true);
            @SuppressWarnings("unchecked")
            Map map = (Map) mapField.get(cache);

            // 3. 移除指定 key
            Object removed = map.remove(hostname);
            if (removed != null) {
                System.out.printf("DNS cache cleared for: %s%n", hostname);
            }
        } catch (ReflectiveOperationException e) {
            throw new IllegalStateException("Clear DNS cache failed: " + e.getMessage(), e);
        }
    }

    /** 清空所有緩存 */
    public static void clearAll() {
        try {
            Class clazz = InetAddress.class;
            Field cacheField = clazz.getDeclaredField("addressCache");
            cacheField.setAccessible(true);
            Object cache = cacheField.get(null);
            Field mapField = cache.getClass().getDeclaredField("cache");
            mapField.setAccessible(true);
            @SuppressWarnings("unchecked")
            Map map = (Map) mapField.get(cache);
            map.clear();
            System.out.println("All DNS cache cleared.");
        } catch (ReflectiveOperationException e) {
            throw new IllegalStateException("Clear all DNS cache failed", e);
        }
    }
}

⚠️ 風險提示:

  • 依賴 JDK 內部私有字段(addressCache、cache),版本升級可能失效;
  • JDK 9+ 模組化封裝,需添加 --add-opens java.base/java.net=ALL-UNNAMED;
  • 併發環境下直接操作 Map 非線程安全,可能導致緩存損壞。

方案對比與選型建議

方案生效範圍是否需重啟對現有緩存影響推薦場景
啟動參數進程是無(新策略僅對新條目)容器化部署、藍綠發布
Security 屬性進程否(但需極早執行)無應用啟動階段配置
修改 java.securityJRE 全局是無基礎設施統一治理
反射清理進程否立即清除指定/所有條目緊急故障熱修復、測試驗證

最佳實踐:藍綠部署/服務發現場景

  1. 應用啟動參數設置 -Dsun.net.inetaddr.ttl=30(30 秒平衡性能與感知速度);
  2. 關鍵服務調用方接入 服務發現(Nacos/Consul/Eureka),直接獲取實時 IP 列表,繞過 DNS;
  3. 若必須依賴 DNS,關鍵路徑代碼捕獲 UnknownHostException 後調用 DnsCacheCleaner.clearHost() 重試一次;
  4. 監控 DNS 解析延遲與錯誤率,異常時告警。

總結

Java DNS 緩存默認永久生效,導致 IP 變更不感知。生產環境首選啟動參數設置短 TTL(30-60s);緊急情況下可用反射強制清理,但需注意模組化限制與線程安全。長期方案應遷移至服務發現機制,從根本上解耦 DNS 依賴。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved