如果你在国内做过网站加速,大概率遇到过这样的现象:同一个域名,北京用户解析出来的IP和广州用户解析出来的IP完全不同。这不是DNS出了故障,而是CDN服务商刻意为之的结果。实现这一能力的核心技术就叫GEO DNS,也就是基于地理位置的域名解析调度。它看起来只是DNS系统的一个小功能,背后却牵扯到IP地理库、递归DNS行为、节点负载评估等多个环节,本文就把这套机制彻底讲清楚。

GEO DNS的完整解析流程是怎么走的
要理解GEO DNS,先得明白一次普通的DNS查询是怎么完成的。当用户在浏览器里输入域名后,操作系统会向本地配置的递归DNS服务器(也就是LDNS,通常是运营商的DNS或者公共DNS如114.114.114.114)发起查询。递归服务器如果缓存中没有记录,就会从根服务器开始,一路问到域名的权威DNS服务器。关键点在于:权威DNS服务器看到的请求来源,不是用户本人的IP,而是递归服务器的出口IP。
GEO DNS正是利用了这一点。权威DNS在收到查询请求时,会先取出请求源IP(即递归DNS的出口IP),然后拿这个IP去GeoIP地理数据库里查询,得到一个大致的地理位置,比如省份、城市或者运营商信息。接着根据这个位置,从预先配置好的调度策略表里挑选一个最合适的边缘节点IP,作为DNS应答返回给递归服务器,最终递归服务器把这个IP交给用户浏览器。
整个流程可以用一段伪代码概括:
def geo_dns_resolve(domain, client_ip):
# client_ip 实际上是递归DNS的出口IP
location = geoip_lookup(client_ip) # 查询GeoIP库,如返回 "广东省-电信"
node = schedule_policy.match(domain, location) # 按调度策略选出边缘节点
if node is None:
node = default_node # 兜底节点
ttl = node.ttl or 60 # 通常设置较短TTL便于快速切换
return dns_answer(node.ip, ttl)值得注意的是返回的TTL值。GEO DNS通常会故意把TTL设置得比较短,常见的是30秒到60秒。原因很简单:如果某个边缘节点出现故障需要摘除,短TTL能保证用户的DNS缓存尽快过期,重新解析到健康节点。但TTL太短也会增加权威DNS的查询压力,这是一个需要权衡的取舍。
就近调度背后的策略远不止距离一个维度
很多人以为GEO DNS就是简单地找离用户物理距离最近的节点,实际上距离只是其中一个因素。真实的调度决策通常是多维度综合的结果。第一个维度是地理与运营商归属,国内CDN普遍按省份加运营商的粒度划分调度区域,因为跨运营商访问的延迟往往比跨省同运营商还高。第二个维度是节点实时负载,如果上海节点已经打满带宽,即使上海用户离它最近,也可能被调度到负载较低的杭州节点。第三个维度是节点健康状态,CDN的健康检查系统会持续探测所有边缘节点,故障节点会被自动从调度池里剔除。
在实现方式上,主流的智能DNS软件(如BIND的View功能、PowerDNS、国内常见的dnspod-sr)都支持按来源IP返回不同应答。以BIND为例,通过view和match-clients配置即可实现分区解析:
// BIND 按来源IP分视图解析示例
acl "south_users" { 113.0.0.0/8; 119.0.0.0/8; };
acl "north_users" { 111.0.0.0/8; 123.0.0.0/8; };
view "south" {
match-clients { "south_users"; };
zone "example.net" in {
type master;
file "south.zone"; // 南方用户解析到华南节点
};
};
view "north" {
match-clients { "north_users"; };
zone "example.net" in {
type master;
file "north.zone"; // 北方用户解析到华北节点
};
};这种基于ACL的粗粒度划分实现简单,但维护成本高,因为运营商的IP段经常调整。商业CDN一般采用更细粒度的方案,把GeoIP库查询、实时负载数据、节点健康度都接入一个中心调度系统,动态生成应答,而不是静态的zone文件。
调度不准的常见原因和Anycast方案的对比
GEO DNS最大的软肋在于它定位的其实是递归DNS的位置,不是用户本人的位置。举个典型场景:一个深圳用户把DNS改成了北京的公共DNS,比如某些企业内网统一转发DNS查询,那么权威DNS看到的来源IP是北京的,会把用户调度到华北节点,访问体验反而变差。这种误差在公共DNS大行其道的今天相当普遍,尤其像谷歌的8.8.8.8这类全球Anycast部署的DNS,出口位置更是难以预测。缓解手段包括让公共DNS通过EDNS Client Subnet扩展协议把用户真实IP的前缀带给权威服务器,目前主流公共DNS和CDN厂商大多已支持ECS,但仍有部分场景覆盖不到。
除了ECS,另一种思路是干脆放弃DNS调度,改用Anycast。Anycast通过BGP把同一个IP地址从多个节点同时向外宣告,用户的请求会被网络路由自动引导到路由意义上最近的节点,不需要DNS参与位置判断。两种方案的差异可以简单对比如下:
| 对比维度 | GEO DNS | Anycast |
|---|---|---|
| 调度依据 | 递归DNS出口IP的地理位置 | BGP路由距离 |
| 调度粒度 | 可精确到城市和运营商 | 取决于网络拓扑,粒度较粗 |
| 故障切换 | 依赖TTL过期,需几十秒 | 路由收敛,通常更快 |
| 流量控制 | 策略灵活,可按比例分配 | 难以精细控制流量比例 |
| 实施门槛 | 普通DNS软件即可实现 | 需要ASN和BGP广播能力 |
实践中大型CDN往往两者结合:入口层用GEO DNS做粗粒度地域调度,节点内部再用Anycast或者302重定向做二次调度。对于中小团队自建场景,如果没有多节点需求,直接用云厂商DNS提供的线路解析功能就够用了;如果确实要多地域部署,建议优先做好ECS支持和健康检查,这两点是GEO DNS稳定运行的根基。