DNS预热:从可选到必选

46 阅读4分钟

引言:冷启动问题何时浮出水面

千万 QPS 的系统不是一夜之间建成的。从第一个用户到千万级并发,DNS 缓存会随着业务增长自然地被"养热"——LocalDNS 逐层建立缓存,应用内 DNS 缓存也趋于稳定。在这个渐进过程中,你几乎感受不到 DNS 的存在。

然而,当系统规模跨过某个临界点后,以下场景会让 DNS 冷启动问题突然暴露出来:

  • 大规模滚动发布:千万 QPS 系统通常有数万个服务实例,全量发布意味着数万个进程重启,每个进程启动时都需要重新解析 DNS
  • 故障恢复:机房级故障恢复后,所有实例的内存 DNS 缓存全部丢失
  • 弹性扩容:大促前快速扩容数千台机器,新实例没有任何 DNS 缓存
  • 灾备切换:主备切换后,备用集群的 DNS 缓存可能是完全冷的

这些场景在百万 QPS 时代可能只是监控图上的一个小毛刺,但在千万 QPS 时代,它们会变成足以影响系统稳定性的风险点。


本质差异:冷启动查询量的量级跃迁

理解这个问题,需要先建立一个简化模型。

假设一个后端服务需要访问 5 个下游依赖(数据库、缓存、消息队列等),每个依赖对应一个域名。在进程冷启动时,这 5 个域名都需要解析。

image.png

看起来只是 10 倍的差异?问题在于时间窗口。

百万 QPS 系统做滚动发布,2,000 个实例分 20 批,每批 100 个,间隔 30 秒。DNS 查询被均匀分散到 10 分钟内,峰值查询量约 500 次/批 × 2 批/分钟 = 1,000 QPS,企业内部 DNS 服务轻松承受。

千万 QPS 系统呢?20,000 个实例分 20 批,每批 1,000 个。即使同样的发布节奏,峰值查询量变成 5,000 次/批 × 2 批/分钟 = 10,000 QPS。如果发布窗口压缩(业务压力下很常见),这个数字还会成倍增长。

更棘手的是级联效应:当内部 DNS 服务响应变慢,应用启动超时,触发重试,DNS 压力进一步放大。在极端情况下,这种正反馈可能导致整个发布流程卡住。


预热策略:三层防线

DNS 预热的本质是将瞬时的查询压力分散到更长的时间窗口。根据缓存层级的不同,可以建立三层防线:

第一层:应用进程内缓存预热

大多数语言的 DNS 解析库都有内存缓存,但默认行为差异很大:

  • Go 的 net.Resolver 默认不缓存
  • Java 的 InetAddress 缓存 30 秒(可配置)
  • Node.js 的 dns 模块默认不缓存

在千万 QPS 系统中,推荐在应用启动阶段主动预热关键域名。

关键点:预热应在流量接入之前完成,避免首批请求承受 DNS 解析延迟。

第二层:本地 DNS 代理缓存

在每台机器上部署轻量级 DNS 代理(如 dnsmasq、CoreDNS),可以实现:

  • 进程间共享 DNS 缓存,避免同一台机器上多个进程重复查询
  • 自定义 TTL 策略,延长热点域名的缓存时间
  • 故障时提供 stale 缓存兜底

配置示例(dnsmasq):

最小缓存时间 300 秒,即使上游返回更短 TTL

min-cache-ttl=300

缓存条目数量

cache-size=10000

第三层:集中式 DNS 缓存集群

对于超大规模系统,可以在数据中心层面部署专用的 DNS 缓存集群:

image.png

这一层的关键价值是:在大规模冷启动前,可以通过预热脚本提前将热点域名加载到缓存集群。


预热时序控制:发布编排的艺术

仅有缓存还不够,发布时序的精细控制同样重要。以下是一个千万 QPS 系统发布前的 DNS 预热时序:

image.png 几个关键设计点:

  1. 预热先行:在第一个实例重启前 10 分钟完成缓存预热
  2. 批次间隔:确保上一批实例完全启动后再启动下一批,避免叠加
  3. 缓存续期:发布过程中持续刷新缓存,避免发布中途缓存过期
  4. 健康检查门控:只有通过健康检查的实例才允许接流量,避免 DNS 解析失败的实例被暴露

百万 vs 千万:预热策略对比

image.png


小结

DNS 预热从"可选优化"变成"必选流程",是千万 QPS 系统的一个典型特征。这不是简单的规模放大,而是系统运维模式的质变:

  • 百万 QPS:DNS 是透明的基础设施,几乎不需要关注
  • 千万 QPS:DNS 是需要精细管理的关键资源,预热是发布流程的标准环节

当你发现团队开始讨论"发布前要不要做 DNS 预热"时,说明系统可能正在跨越这个临界点。这是一个好信号——也是一个需要认真对待的信号。