DNS 解析优化:从“域名到 IP”的每一毫秒

说实话,很多 SDK 开发者容易忽视 DNS 这个环节。你想想,每次网络请求前都要先查一次域名,这步要是慢了,整个请求都得等着。我见过有的团队把优化精力全花在 HTTP 协议上,结果 DNS 解析占了 200ms —— 白忙活。

DNS 查询流程——你在等一个“电话本”

DNS 本质上是个分布式电话本。你的 App 问:“api.example.com 的 IP 是多少?”系统会先看本地缓存,没有就去问递归 DNS 服务器。递归 DNS 再依次问根、TLD(顶级域)、权威服务器。

我习惯把这几个角色画成一张图:

DNS 解析优化核心路径 App / SDK 请求 本地 DNS 缓存 传统递归 DNS HTTPDNS 接口 根 → TLD → 权威 直连 IP / 防劫持 Domain Fronting(SNI 混淆) 缓存在每个阶段都可能失效,优化就是让“命中率”尽可能高

从上图能看出来,传统 DNS 要走好几跳。我做过一个 SDK,发现每次启动都要完整解析域名,而且运营商 DNS 还经常抽风 —— 一次查询能拖到 800ms。后来加了本地缓存,瞬间降到 <10ms。

我的习惯:在 App 启动时先做一次 DNS 预解析,把常用域名的 IP 缓存到内存。配合系统 DNS 缓存,能避免绝大多数重复查询。

缓存 TTL 设置——别让“过期”害了你

TTL(Time To Live)是 DNS 记录里告诉客户端“这个 IP 你最多能缓存多久”的字段。我见过有人把 TTL 设成 300 秒,结果服务器 IP 变了一直没刷新,用户连不上又不知道原因。

反过来,TTL 设得太大也不行。有一次我把 TTL 设成 86400(一天),结果半夜线上做了 CDN 切换,直到第二天中午才全量生效,线上报了一堆连接超时。

那怎么设?我一般按场景区别对待:

场景 推荐 TTL 理由
核心 API 域名(频繁变更) 30 ~ 60 秒 快速响应 IP 变更,但会增加 DNS 查询频率
CDN 静态资源域名 600 ~ 3600 秒 IP 稳定,减少解析开销,提升首屏速度
SDK 后端服务(多机房容灾) 60 ~ 300 秒 平衡容灾及时性和缓存效率
本地开发环境 10 秒或关闭缓存 方便调试,不怕改 IP 不生效
我曾经踩过坑:把 API 网关的 TTL 设成 5 秒,结果每个请求都要查一次 DNS,后端压力暴增。嗯,后来发现运营商 DNS 还限速了。改回 60 秒才好。

你还可以在 SDK 里实现“主动刷新”逻辑。当检测到某个域名解析异常时,强制清除缓存重新查询。不要干等 TTL 到期。

HTTPDNS——绕过运营商“劫持”

传统 DNS 最大的问题是什么?一是慢(递归路径长),二是不安全(中间人篡改)。我自己被坑过很多次 —— 某些运营商把你不存在的域名解析到他们的广告页上,导致 SDK 请求全部失败。

HTTPDNS 的原理很简单:直接用 HTTP 请求从专门的服务器获取 IP。不用走系统 DNS,直接绕过运营商。SDK 接入时,大概像这样:

// 伪代码示意
func resolve(domain string) -> string {
    url := "https://dns.tencent.com/d?dn=" + domain
    resp := httpGet(url)
    ip := parseResponse(resp)
    return ip
}

我习惯在 SDK 里把它做成“兜底策略”:先走系统缓存,缓存失效或失败时再走 HTTPDNS。另外,HTTPDNS 的服务器也要容灾,可以内置两个以上厂商的地址。

实际收益:我在一个日活千万的 SDK 里集成 HTTPDNS 后,DNS 解析平均耗时从 260ms 降到 35ms,成功率从 96.2% 提升到 99.8%。因为绕过了很多辣鸡 Local DNS。

Domain Fronting——一种规避审查的技巧

这个技术有点“灰色”,但作为网络协议专家,我还是要跟你讲清楚。Domain Fronting 利用了 CDN 的“多域名共用一个 IP”的特性:客户端在 TLS SNI(Server Name Indication)里写一个“合法的白名单域名”,但实际 HTTP Host 头里写上真正想访问的域名。CDN 看到 SNI 放行,但内部路由时根据 Host 转发。

我曾在一个海外项目里用过它来绕过某些国家的 DNS 封锁。效果很好,但也面临风险:

  • CDN 厂商可能禁止:AWS、Cloudflare 都明确声明不允许 Domain Fronting,一旦发现直接封账号。
  • TLS 握手协议限制:HTTP/2 和 HTTP/3 对 SNI 校验更严格,很多场景已经不适用。
  • 法律合规问题:在中国大陆、俄罗斯等地使用可能违反网络安全管理法规。
建议:如果只是为了优化国内运营商劫持,用 HTTPDNS 就够了,别碰 Domain Fronting。除非你的业务有明确的合规审查需求和律师背书。

总而言之,DNS 优化不是一招鲜。我个人推荐“三级缓存 + HTTPDNS 兜底”的方案:
本地内存缓存 → 文件持久化缓存 → 系统 DNS 缓存。当全部失效时,再用 HTTPDNS 取一次。这样既保证速度,又保证正确性。

好了,这一章的核心就这么多。记住:每省下 50ms DNS 耗时,你的 SDK 用户体验就上一个台阶。