DNS客户端缓存

DNS 客户端的缓存与 DNS 响应包中的 TTL 是否一致,取决于客户端的设计和配置,实际情况常常不完全一致。下面详细解释原因、保持一致的方法及常见注意事项。


1. 标准行为(“应一致”)

DNS 协议标准(如 RFC 1035)规定,递归 DNS 客户端(包括递归服务器和本地客户端)在收到响应后,应当按照响应包中的 TTL 对缓存计时,TTL 到期后删除缓存。


2. 实际实现中的差异

常见客户端行为

  • 大多数操作系统的 DNS Resolver(如 Windows DNS Client、systemd-resolved)
    遵循响应中的 TTL,缓存时间为 TTL 秒数。
  • 浏览器、JVM、部分编程语言运行时
    经常不直接遵守响应中的 TTL,而是用自己的配置参数。例如:
    • Java:networkaddress.cache.ttl,默认永久缓存。
    • Chrome/Firefox:有自己短周期缓存(如 60 秒),不管响应 TTL。
    • Go:默认无缓存,或自定义缓存行为。

操作系统缓存守护进程

  • nscd, systemd-resolved, dnsmasq 等,可以配置“最小/最大 TTL”,导致缓存周期与响应 TTL 不一致。

3. 保持一致的办法

1. 系统级(守护进程)

  • 确保 DNS 缓存服务(如 systemd-resolved, dnsmasq, nscd)的配置,不设置“最小/最大 TTL”,让其严格按响应包 TTL 缓存
  • 相关配置示例(以 dnsmasq 为例):
    # 不强制最小/最大 TTL
    min-cache-ttl=0
    max-cache-ttl=0
    

2. 应用级

  • 检查并配置应用的 DNS 缓存参数,让其读取系统 resolver 缓存,或设置其缓存时间和上游 TTL 保持一致。
    • Java:-Dnetworkaddress.cache.ttl 设为与期望 TTL 保持同步。
    • 浏览器:一般无法控制,需知晓其行为。

3. 递归 DNS 服务器

  • 递归服务器(如 Unbound、BIND、dnsmasq)一般会严格遵循权威服务器的 TTL,但有的支持配置最小/最大 TTL。

4. 注意事项

  • TTL 不等于实际缓存生效时间
    1. 缓存是收到包时开始计时,传递给下级客户端时会减去已消耗的秒数(叫“剩余 TTL”)。
    2. 多级缓存(本地、递归、权威)会导致最终客户端看到的 TTL 可能小于最初权威服务器设置的 TTL。
  • 特定场景(如 CDN、Cloudflare、运营商 DNS):运营商或中间层可能会强制修改 TTL 或缓存周期。
  • 应用层缓存:应用自身不遵守 TTL 时,必须依赖开发者自行同步配置。

5. 结论与建议

  • 理论上,客户端应严格按照 DNS 响应 TTL 缓存。
  • 实际情况需手动配置/验证:操作系统和应用级缓存都要检查和设置参数。
  • 多级缓存会导致 TTL 递减,不能简单认为客户端缓存时间等于权威 TTL

参考配置示例

# dnsmasq 严格按上游 TTL 缓存
min-cache-ttl=0
max-cache-ttl=0

# Java 严格按系统 resolver
-Dnetworkaddress.cache.ttl=-1

如需确保一致,建议:

  1. 配置本地/系统 DNS 缓存服务严格遵守 TTL。
  2. 配置应用程序 DNS 缓存参数为“读取系统”或与 TTL 保持同步。
  3. 定期测试和验证 TTL 生效行为。

如需针对某操作系统、应用或服务的具体配置方法,请说明详细环境。

Kona JDK

Kona JDK(腾讯基于OpenJDK优化的发行版)的DNS缓存机制遵循OpenJDK标准,其默认缓存行为及配置规则如下:


⚙️ 一、默认缓存策略

  1. 成功解析的DNS记录

    • 未显式配置时:默认缓存时间为 30秒(与标准OpenJDK行为一致)[citation:1][citation:2][citation:7]。
    • 逻辑依据
      • 若未启用SecurityManager且未配置networkaddress.cache.ttl参数,则使用30秒作为默认值[citation:1][citation:6]。
      • 若启用SecurityManager,则优先读取${JAVA_HOME}/jre/lib/security/java.security中的配置[citation:1]。
  2. 解析失败的DNS记录(负缓存)

    • 默认永久缓存:若域名解析失败(如UnknownHostException),JVM会永久缓存失败结果(即networkaddress.cache.negative.ttl默认值为-1)[citation:1][citation:7]。
    • 风险:可能导致后续解析请求始终失败,需显式配置负缓存时间避免此问题。

🔧 二、配置参数与优先级

通过以下参数调整缓存时间(优先级从高到低):

  1. java.security文件配置
    修改路径:${JAVA_HOME}/conf/security/java.security
    添加:

    networkaddress.cache.ttl=60       # 成功解析缓存60秒
    networkaddress.cache.negative.ttl=10 # 失败解析缓存10秒
    

    [citation:1][citation:7]

  2. JVM启动参数
    命令行覆盖:

    java -Dnetworkaddress.cache.ttl=300 -Dnetworkaddress.cache.negative.ttl=30 -jar app.jar
    

    [citation:2][citation:4]

  3. 程序代码动态设置

    java.security.Security.setProperty("networkaddress.cache.ttl", "60");
    

    (需在首次DNS查询前调用)[citation:1]

⚠️ 优先级规则
java.security > JVM启动参数 > 默认值
若配置为0,表示禁用缓存;-1表示永久缓存[citation:1][citation:6]。


📊 三、生产环境建议

场景 推荐配置
频繁变更IP的服务 成功缓存TTL≤60秒(如-Dnetworkaddress.cache.ttl=30
高稳定性环境 成功缓存TTL=300~600秒,负缓存TTL=60秒(平衡性能与容错)
需快速感知DNS故障切换 负缓存TTL≤30秒(避免永久阻塞重试)

🔍 四、验证缓存生效

  1. 查看当前配置
    System.out.println("Cache TTL: " + java.security.Security.getProperty("networkaddress.cache.ttl"));
    
  2. 测试缓存行为
    修改/etc/hosts文件模拟DNS变更,观察控制台输出是否在TTL后更新IP[citation:1]。

⚠️ 五、常见问题与规避

  • 容器环境问题:Kona JDK在容器中可能因默认配置导致缓存过长,建议显式设置TTL[citation:4]。
  • 云服务依赖:若使用腾讯云微服务框架(如TSF),结合Service Mesh时需同步调整服务发现的DNS缓存策略[citation:5]。
  • 负缓存超时务必显式配置networkaddress.cache.negative.ttl(如30秒),避免永久失败[citation:1][citation:7]。

💡 最佳实践:在云原生场景中,建议通过JVM参数统一管理DNS缓存(如-Dnetworkaddress.cache.ttl=60),而非依赖默认值,确保跨环境一致性[citation:4][citation:6]。

posted @ 2025-07-24 17:04  惜阳茕影  阅读(252)  评论(0)    收藏  举报