Java 為提高解析性能,在 InetAddress 內部維護 DNS 緩存。默認策略:
networkaddress.cache.ttl = -1,JDK 11+ 默認 30 秒);networkaddress.cache.negative.ttl = 10)。導致域名 IP 變更(如藍綠部署、災備切換)後,Java 進程需重啟才能生效。
// 簡化版源碼結構
class InetAddress {
// 緩存類
static class Cache {
CacheType type; // 緩存類型
Map cache; // key=hostname, value=InetAddress[] 或異常
}
static Cache addressCache; // 成功緩存
static Cache negativeCache; // 失敗緩存
}
# 完全禁用緩存(性能損耗大) -Dsun.net.inetaddr.ttl=0 -Dsun.net.inetaddr.negative.ttl=0 # 或設置較短 TTL,如 30 秒 -Dsun.net.inetaddr.ttl=30 -Dsun.net.inetaddr.negative.ttl=5
優點:零代碼侵入、進程級生效;缺點:需重啟、影響所有解析性能。
// 必須在 InetAddress.getByName() 等首次調用前執行
java.security.Security.setProperty("networkaddress.cache.ttl", "30");
java.security.Security.setProperty("networkaddress.cache.negative.ttl", "5");
限制:類加載時初始化緩存策略,動態修改對已緩存條目無效,僅影響後續新條目。
# 文件:$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);
}
}
}
⚠️ 風險提示:
addressCache、cache),版本升級可能失效;--add-opens java.base/java.net=ALL-UNNAMED;| 方案 | 生效範圍 | 是否需重啟 | 對現有緩存影響 | 推薦場景 |
|---|---|---|---|---|
| 啟動參數 | 進程 | 是 | 無(新策略僅對新條目) | 容器化部署、藍綠發布 |
| Security 屬性 | 進程 | 否(但需極早執行) | 無 | 應用啟動階段配置 |
| 修改 java.security | JRE 全局 | 是 | 無 | 基礎設施統一治理 |
| 反射清理 | 進程 | 否 | 立即清除指定/所有條目 | 緊急故障熱修復、測試驗證 |
-Dsun.net.inetaddr.ttl=30(30 秒平衡性能與感知速度);UnknownHostException 後調用 DnsCacheCleaner.clearHost() 重試一次;Java DNS 緩存默認永久生效,導致 IP 變更不感知。生產環境首選啟動參數設置短 TTL(30-60s);緊急情況下可用反射強制清理,但需注意模組化限制與線程安全。長期方案應遷移至服務發現機制,從根本上解耦 DNS 依賴。