用代码实现DNS域名解析,本质是构造一条DNS查询请求发到指定服务器,再从响应包里取出A/AAAA/CNAME等记录;实践中最高效的解法是直接用Python的dnspython库,它把UDP交互、超时重试和记录解析都封装好了,十几行代码就能跑通。
DNS域名解析的原理:代码里到底在做什么
写代码之前先花两分钟把流程捋清楚,你访问一个网址时,浏览器不会直接拿域名去连服务器,而是先查这个域名对应哪个IP,整个过程可以概括为四步:客户端检查本地缓存和hosts文件,没命中就把查询发给本地DNS服务器,本地服务器再向根服务器、顶级域服务器、权威服务器逐级递归,最终把结果带回客户端。
代码层面对应的动作就很清晰了,你需要生成一个查询报文,指定域名和记录类型,通过UDP发给目标DNS服务器的53端口,UDP默认只有一次机会,丢包就要靠重试补齐,响应回来之后还要解析报文头部的状态码、Answer区的记录值和TTL,这些动作自己用socket写不是不行,但代码量立刻翻倍,而且要考虑字节序、压缩指针、多记录处理。
dnspython库把这些动作全部封装成了对象和函数,这也是行业共识认为最稳妥的Python选型,用比较朴素的socket库,你会接触到底层数据包结构,适合学协议但生产环境效率不高,dnspython则把复杂度收在内部,你只需要关心查询参数和返回对象。
记录类型这关也得过,A记录给IPv4,AAAA给IPv6,CNAME是别名跳转,MX记录管邮件服务器,代码里解析业务不同,查询类型自然不同,实现时按需传入参数即可。
用Python实现DNS域名解析的完整代码流程
先把环境准备好,安装依赖只需要一条命令:
pip install dnspython
最基础的A记录查询这样写:
import dns.resolver
res = dns.resolver.resolve("baidu.com", "A")
for item in res:
print(item.address)
三行代码,一个域名解析就完成了。dns.resolver.resolve会自动读取系统配置的DNS服务器,比如114.114.114.114或者运营商分配给你的地址。
但这只是起步,真实环境里你更关心的是稳定性和可控性,设置超时时间推荐3秒到5秒,网络抖动时,UDP查询丢包或者响应慢,程序会卡住,加上超时控制能避免主流程被拖死:
import dns.resolver
res = dns.resolver.resolve("baidu.com", "A", lifetime=5)
lifetime参数控制整个解析过程的最大耗时,超时还没拿到结果会抛异常,指定自己的DNS服务器也很常见,比如公司内网域名必须走内网DNS才能解析,或者你想绕开运营商DNS做对比排查:
import dns.resolver
res = dns.resolver.Resolver()
res.nameservers = ["8.8.8.8", "114.114.114.114"]
ans = res.resolve("example.com", "A")
把多个DNS服务器放进nameservers列表后,dnspython会按顺序自动切换,第一个不通就尝试第二个,这个机制对提升解析成功率很有用。
异常处理则是必须写的一段逻辑,解析失败时常见有这几种情况:
- NXDOMAIN:域名本身不存在
- NoAnswer:域名存在但没有对应类型的记录
- Timeout:DNS服务器无响应
对应的处理动作不同,NXDOMAIN直接返回失败;NoAnswer可以尝试换别的记录类型;Timeout就换备用DNS服务器重试,批量解析场景更容易踩坑,比如运维巡检时一次性解析上百个域名,建议用线程池并发执行,每个域名独立任务,互不阻塞,并发数控制在20到50之间,能明显缩短整体耗时。
用dig和nslookup快速验证DNS域名解析结果
写代码之前,先用命令行工具确认一下目标域名到底是什么状态,能省掉很多调试时间,dig是Linux和macOS上功能最完整的DNS查询工具,nslookup在Windows上自带,两边都能用。
dig的基础用法:
dig baidu.com +short
直接输出解析结果,比如220.181.38.148,不加short时能看完整响应,包括查询时间、报文大小、TTL值、权威服务器等,想看看一条域名从根服务器到权威服务器的完整链路,用+trace参数:
dig example.com +trace
这个命令很有价值,它会依次向根服务器、com顶级域服务器、example.com的权威服务器发起查询,把每一层返回的结果都打印出来,域名解析卡在哪一环,一跑就知道。
nslookup的交互模式简单直接,命令行输nslookup回车,进入提示符,再输域名就能得到结果,退出用exit,这些命令在你排查问题时作用很大,代码结果和命令行工具对不上时,优先检查是不是本地缓存或者hosts文件干扰,我实际项目里就遇到过,程序里解析出来一个IP,命令行里解析出另一个,最后排查发现是代码进程里DNS缓存没刷新。
DNS域名解析失败怎么排查:三个层面定位问题
解析失败是业务上线最常见的坑,代码写得好好的,上线后域名忽然解析不了,第一反应往往是改代码,但多数问题不在代码层。
先看本地层,查一下/etc/hosts(Windows是C:WindowsSystem32driversetchosts)里有没有手工指定的记录,有些运维会在hosts里临时加一行指向测试服务器的记录,时间一长忘了删,程序就始终访问旧IP,再检查系统DNS缓存,macOS上可以用sudo dscacheutil -flushcache清掉,Linux用systemd-resolved的则执行systemd-resolve --flush-caches。
再看网络层,DNS查询走UDP 53端口,有些云主机的安全组策略默认只放行TCP端口,UDP 53反而被拦了,本地用tcpdump抓包验证一下:
tcpdump -i eth0 udp port 53
抓包结果里能看到查询包发出去了,但一直没有响应包回来,那基本可以确定是链路被防火墙拦了。
最后看服务器层,本地和网络都正常,就换公共DNS服务器试,用dig @8.8.8.8、dig @114.114.114.114分别查一下,如果公共DNS能正常返回,说明你当前的本地DNS服务器有问题;如果所有DNS服务器都返回同一异常,就去查域名的权威服务器状态,这三种情况层层递进,每一层都有对应的验证手段,按这个顺序排查,效率比盲目改代码高得多。
域名解析到服务器后,代码中要关注的三种解析场景
域名解析到服务器,这只是第一步,上线之后你会遇到更多需要处理的细节场景。
第一,多IP轮询与负载均衡。 一个域名配置多条A记录是常见做法,DNS服务器会按顺序或随机返回其中一个IP,代码里拿到的是列表,你的业务逻辑要处理好这种情况,比如随机选一个IP建连,某个IP不通时自动换下一个。
第二,邮件服务器MX记录解析。 邮件服务要用MX查询而不是A记录,MX记录返回的是一组邮件服务器地址和优先级,数字越小优先级越高,代码里解析MX之后,需要按优先级排序,高优先级的服务器连不上再退到低优先级。
第三,CDN加速场景。 用CDN的域名默认是CNAME记录,比如你的cdn.example.com会指向某CDN厂商的域名,再由后者解析到最近的节点IP,代码里用A记录查这样的域名可能只能拿到中间结果,正确做法是先查CNAME,拿到目标后继续解析A记录。
每种场景都有特定的处理逻辑,写代码时提前考虑,比线上出问题再补救要省心。
DNS域名解析怎么设置:从代码调优到系统配置
会查记录只是基础,会配置解析策略才体现功力,这部分分别从代码和系统两个维度说。
代码层面,dnspython允许你灵活设置查询参数,除了前面说到的超时和nameservers,还有个容易被忽略的
use_edns参数,EDNS允许DNS报文携带更多附加信息,一些现代DNS服务商要求客户端启用EDNS才能获取最优结果,代码里加一行res.use_edns(0, 0, 4096)就能开启。
查询结果建议自己做一层短时缓存,每次请求都发起完整DNS查询,不仅慢,还可能因为查询频率过高被DNS服务器限流,把结果存在内存里,按照TTL时间刷新,TTL大于60秒的本地缓存30秒,TTL较短的就跟着TTL走,这是个合理折中。
系统层面,Linux服务器修改DNS配置直接编辑/etc/resolv.conf,一行:
nameserver 223.5.5.5
Windows则通过网络适配器的IPv4属性设置,不过某些云服务器有自带的网络管理工具会重置resolv.conf文件,改之前先确认你的环境有没有这类机制。
更进一步的方案是用DNS over HTTPS(DoH),传统DNS查询是明文传输,运营商和中间设备都能看到你查了什么域名,也存在被篡改回包的风险,DoH把查询封装在HTTPS请求里,既加密又隐蔽,Python里实现也简单,用requests库请求DoH服务商的API就行,简米云和DNSPod这类公共解析服务都提供DoH接口,从传统UDP查询转向DoH是公网解析的趋势,云厂商和浏览器厂商都在推,代码迁移成本不高,效果立竿见影。
就是核心要点的完整梳理,代码解析DNS域名没有想象中复杂,选对工具、处理异常、关注缓存,你就可以在业务里稳定使用。
用代码实现DNS域名解析常见问题解答
为什么代码里解析域名有时成功有时失败?
这个现象通常与DNS缓存和服务器波动有关,本地DNS服务器的缓存结果会有TTL过期时间,过期后重新向上级查询,一旦权威服务器响应变慢,当前查询就可能超时,代码层面建议设置多组nameservers并配置3到5秒的lifetime,超时后自动切换重试。
域名解析到服务器通常需要多久生效?
生效时间由每条DNS记录的TTL值决定,TTL是600秒时,本地DNS服务器最多缓存10分钟,大部分用户会在10分钟内看到新IP生效,不过全球各地的缓存服务器刷新步调不一致,域名解析到服务器后全部生效可能需要48小时左右,这是DNS体系的固有特性。
DoH解析和普通UDP解析在代码上有区别吗?
区别主要在传输层和返回值格式,普通UDP解析用dnspython直接发DNS报文,DoH则是向HTTPS接口发起POST或GET请求,返回JSON格式的数据,代码里需要requests库配合解析,一些公共DNS服务商的DoH接口还支持指定返回类型,用起来也算方便。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/625303.html




