Java默认通过InetAddress缓存DNS解析结果,正向缓存默认30秒(JDK 8u171+),负向缓存默认10秒,由networkaddress.cache.ttl和networkaddress.cache.negative.ttl控制。

Java 程序中默认会对 DNS 解析结果进行缓存,这在多数场景下能提升性能,但在服务端 IP 变更、灰度发布、负载均衡切换或本地调试时,容易导致 ConnectException: Connection refused 或连接到旧地址等问题。要避免因缓存引发的频繁错误解析,关键不是“完全禁用 DNS 查询”,而是控制缓存行为,让解析更及时、可控。
Java 的 DNS 缓存机制在哪里生效
JVM 通过 java.security.Security 系统属性控制 DNS 缓存策略,主要影响 InetAddress.getByName() 和 InetAddress.getAllByName() 等标准解析方法。缓存由 sun.net.InetAddressCachePolicy 管理,默认行为如下:
- 成功解析结果缓存 30 秒(JDK 8u171+ 默认值)
- 失败解析(如 NXDOMAIN)缓存 10 秒
- 这些值可通过系统属性覆盖
如何合理配置 DNS 缓存策略
根据运行环境选择合适方式,不建议全局设为 0(尤其生产环境),但开发/测试阶段可临时调整:
-
启动时设置 JVM 参数(推荐):
立即学习“Java免费学习笔记(深入)”;
-Dnetworkaddress.cache.ttl=5 # 成功结果最多缓存 5 秒 -Dnetworkaddress.cache.negative.ttl=3 # 失败结果缓存 3 秒
-
代码中动态设置(需在应用初始化早期执行,且仅对后续解析生效):
JavaStoryDevelop下载你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
java.security.Security.setProperty("networkaddress.cache.ttl", "5"); java.security.Security.setProperty("networkaddress.cache.negative.ttl", "3");
⚠️ 注意:该设置对已启动的线程中正在使用的
InetAddress实例无效;只影响之后调用getByName等方法的新解析请求。
需要绕过缓存的典型场景及替代方案
当业务明确要求每次获取最新 IP(例如对接动态 DNS、K8s Service、云厂商 SLB),可考虑以下补充手段:
-
手动触发刷新(适用于已知域名变更):
InetAddress.getByName("api.example.com"); // 触发一次解析,缓存会按新 TTL 更新 -
使用
InetAddress.getAllByName()获取全部 A/AAAA 记录,并自行做健康探测或轮询:InetAddress[] addrs = InetAddress.getAllByName("backend.service"); // 按需选择可用 IP,或结合 HTTP client 的重试机制 -
在 HTTP 客户端层控制(如 OkHttp、Apache HttpClient):
- OkHttp 支持自定义
Dns接口,可实现无缓存或带 TTL 的 DNS 封装 - Apache HttpClient 可配置
SystemDefaultDnsResolver并覆写resolve()方法
- OkHttp 支持自定义
不推荐但常见误用的做法
- 在循环中反复调用
InetAddress.getByName()而不做缓存或复用:造成不必要的重复解析和阻塞 - 把
networkaddress.cache.ttl=0当成万能解,在高并发客户端场景下可能引发 DNS 服务器压力激增或延迟升高 - 忽略 negative cache(失败缓存),导致域名短暂不可达期间持续报错,而非快速失败重试
DNS 解析本身开销小,真正影响稳定性的往往是缓存策略与服务变更节奏不匹配。合理设置 TTL + 按需刷新 + 客户端容错,比单纯“避免查询”更可靠。


















