对于需要频繁切换后端 IP 的 Java 应用,最稳妥的方式是在 JVM 启动参数中显式设置 DNS 缓存过期时间,避免依赖默认策略导致流量切换滞后。
先说结论:修改 JVM 参数是控制 DNS 缓存最直接的方法,适合容器化或动态 IP 环境,但需权衡解析开销。
- 适合:Kubernetes 服务发现、负载均衡 IP 频繁变更的场景
- 先准备:确认当前 JVM 版本及启动脚本权限
- 验收:重启后验证缓存策略是否按预期生效
命令速用版
在 Java 启动命令中添加以下参数,将正解析缓存时间设为 30 秒(可根据实际需求调整):
-Dnetworkaddress.cache.ttl=30若需缩短失败缓存时间,可补充:
-Dnetworkaddress.cache.negative.ttl=10通常将参数加入 JAVA_OPTS 环境变量或容器启动命令中。
为什么会这样
Java 应用通过 InetAddress 类进行域名解析,默认会将解析结果缓存到本地。如果不显式配置,JVM 的安全策略可能导致缓存永久生效,或者不同 JDK 版本的默认行为不一致。当后端服务 IP 发生变化时,Java 应用仍在使用旧缓存,导致请求无法到达新节点。
分步处理
- 检查当前配置:查看启动脚本或容器定义,确认是否已存在相关参数。
- 修改启动参数:在
JAVA_OPTS或启动命令中加入-Dnetworkaddress.cache.ttl=30。 - 重启应用:DNS 缓存策略通常在 JVM 启动时加载,修改后需要重启进程。
- 回滚准备:保留原启动脚本备份,若发现 DNS 查询压力过大,可调大数值或恢复默认。
怎么验证是否生效
验证分为代码验证与运行时参数检查两种方式。
1. 代码验证
在应用启动初期添加以下逻辑,打印当前 TTL 配置:
String ttl = System.getProperty("networkaddress.cache.ttl");
System.out.println("DNS Cache TTL: " + (ttl != null ? ttl : "default"));2. JMX 或命令检查
通过 JMX 连接 JVM,查看 java.lang:type=Runtime 的 InputArguments 属性,确认启动参数中包含 -Dnetworkaddress.cache.ttl=30。或使用 jcmd <pid> VM.command_line 查看启动命令行。
操作系统缓存注意事项
JVM 参数仅控制 Java 层面的缓存,操作系统层面的 DNS 缓存可能产生干扰,导致修改无效。
- Linux:检查是否运行
nscd或systemd-resolved服务。测试期间可临时刷新缓存(如systemd-resolve `--flush-caches`)或调整其配置。 - Windows:DNS Client 服务会缓存解析结果。调试时可运行
ipconfig /flushdns清除本地缓存。 - 容器环境:Kubernetes 集群内 CoreDNS 也有缓存策略,需结合集群 DNS 配置综合评估。
常见坑
- 负缓存影响:如果域名解析失败,JVM 也会缓存失败结果,需同时关注
negative.ttl。 - 安全管理器:若启用了 SecurityManager,默认缓存策略可能变为永久,必须显式指定。
- 过度频繁:将 TTL 设为 0 会禁用缓存,可能增加 DNS 服务器负载,具体数值需根据业务 QPS 及 DNS 服务器承受能力通过压测确定。
参考来源
- Oracle Java Documentation, Networking Properties, https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/net/doc-files/net-properties.html