DNS 协议是什么?说说 DNS 完整的查询过程
问题解析(面试官考察点)
面试官通过此问题主要考察:
- 理解 DNS 的基本概念和作用
- 掌握 DNS 查询的完整流程
- 了解 DNS 缓存机制和优化手段
- 理解递归查询和迭代查询的区别
- 了解 DNS 记录类型和安全扩展
核心概念(基础知识点)
什么是 DNS
DNS(Domain Name System,域名系统)是互联网的一项核心服务,它作为将域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网,而不用记住能够被机器直接读取的 IP 地址。
DNS 的核心作用:
- 域名解析:将人类可读的域名转换为机器可读的 IP 地址
- 负载均衡:通过返回不同 IP 实现流量分配
- 邮件路由:MX 记录指定邮件服务器
域名层次结构
┌─────────────────────────────────────────────────────────────┐
│ DNS 层级结构 │
├─────────────────────────────────────────────────────────────┤
│ 根域名(Root) │
│ └── .(通常省略) │
│ ↓ │
├─────────────────────────────────────────────────────────────┤
│ 顶级域名(TLD - Top Level Domain) │
│ ├── 国家/地区:.cn .us .uk .jp │
│ ├── 通用:.com .net .org .edu .gov │
│ └── 新通用:.io .app .dev .cloud │
│ ↓ │
├─────────────────────────────────────────────────────────────┤
│ 二级域名(Second Level Domain) │
│ └── example.com │
│ ↓ │
├─────────────────────────────────────────────────────────────┤
│ 子域名(Subdomain) │
│ ├── www.example.com │
│ ├── blog.example.com │
│ └── api.example.com │
└─────────────────────────────────────────────────────────────┘
完整域名:www.example.com.
└── 从右到左层级递减
DNS 记录类型
| 记录类型 | 说明 | 示例 |
|---|---|---|
| A | IPv4 地址记录 | example.com. IN A 93.184.216.34 |
| AAAA | IPv6 地址记录 | example.com. IN AAAA 2606:2800:220:1:: |
| CNAME | 别名记录 | www.example.com. IN CNAME example.com. |
| MX | 邮件交换记录 | example.com. IN MX 10 mail.example.com. |
| NS | 域名服务器记录 | example.com. IN NS ns1.example.com. |
| TXT | 文本记录 | 用于 SPF、DKIM、验证等 |
| SOA | 起始授权记录 | 包含区域管理信息 |
| PTR | 反向解析记录 | 34.216.184.93.in-addr.arpa. IN PTR example.com. |
详细解答(代码示例)
DNS 完整查询流程
用户访问 www.example.com 的完整流程:
┌─────────┐
│ 用户 │
└────┬────┘
│ 1. 输入 www.example.com
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 1 层:浏览器缓存 │
│ - 检查是否最近访问过该域名 │
│ - TTL 过期前直接使用缓存 │
│ - Chrome: chrome://net-internals/#dns │
└────┬────────────────────────────────────────────────────────┘
│ 未命中
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 2 层:操作系统缓存 │
│ - Windows: ipconfig /displaydns │
│ - Linux: systemd-resolve --statistics │
│ - macOS: scutil --dns │
└────┬────────────────────────────────────────────────────────┘
│ 未命中
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 3 层:hosts 文件 │
│ - Windows: C:\Windows\System32\drivers\etc\hosts │
│ - Linux/macOS: /etc/hosts │
└────┬────────────────────────────────────────────────────────┘
│ 未命中
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 4 层:本地 DNS 服务器(递归查询) │
│ - 通常由 ISP 提供或配置为公共 DNS │
│ - 8.8.8.8 (Google) │
│ - 1.1.1.1 (Cloudflare) │
│ - 114.114.114.114 (国内) │
└────┬────────────────────────────────────────────────────────┘
│ 未命中,开始递归查询
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 5 层:根域名服务器(Root Nameserver) │
│ - 全球 13 组(字母 A-M),如 a.root-servers.net │
│ - 返回 .com 顶级域名服务器地址 │
└────┬────────────────────────────────────────────────────────┘
│ 返回 .com TLD 服务器地址
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 6 层:顶级域名服务器(TLD Nameserver) │
│ - 管理 .com 域名的服务器 │
│ - 返回 example.com 的权威 DNS 服务器地址 │
└────┬────────────────────────────────────────────────────────┘
│ 返回权威 DNS 服务器地址
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 7 层:权威域名服务器(Authoritative Nameserver) │
│ - example.com 的 DNS 服务器 │
│ - 返回 www.example.com 的 A 记录 IP 地址 │
└────┬────────────────────────────────────────────────────────┘
│ 返回 IP 地址
▼
┌─────────┐
│ 用户 │ ← 获得 IP 93.184.216.34,发起 HTTP 请求
└─────────┘
递归查询 vs 迭代查询
递归查询(Recursive Query):
┌─────────┐ ┌─────────────┐ ┌─────────────┐
│ 客户端 │◄───────►│ 本地 DNS │◄───────►│ 根/TLD/权威 │
└─────────┘ └─────────────┘ └─────────────┘
│ │ │
│ 查询 example.com │ │
│──────────────────►│ │
│ │ 帮我查 example.com │
│ │──────────────────────►│
│ │ │(递归查询其他服务器)
│ │ │
│ │ 返回结果 │
│ │◄──────────────────────│
│ 返回结果 │ │
│◄──────────────────│ │
特点:
- 客户端只发一次请求,等待最终结果
- 本地 DNS 服务器负责递归查询
- 对用户友好,减轻客户端负担
迭代查询(Iterative Query):
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 本地 DNS │◄───────►│ 根服务器 │◄───────►│ TLD 服务器 │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ 查询 example.com │ │
│─────────────────────►│ │
│ │ │
│ 我不知道,去问 .com │ │
│◄─────────────────────│ │
│ │ │
│ 查询 example.com │ │
│─────────────────────────────────────────────►│
│ │ │
│ 我不知道,去问 ns1.example.com │
│◄─────────────────────────────────────────────│
特点:
- 每次查询返回下一级服务器地址
- 查询者需要继续发起新查询
- 分散负载,提高系统可扩展性
DNS 报文格式
DNS 查询/响应报文结构:
┌─────────────────────────────────────────────────────────────┐
│ Header(12 字节) │
├─────────────────────────────────────────────────────────────┤
│ 标识(16) │ 标志(16) │ 问题数 │ 回答数 │ 授权数 │ 附加数 │
│ (ID) │(QR|Opcode|AA|TC|RD|RA|Z|RCODE) │
├─────────────────────────────────────────────────────────────┤
│ QR: 0=查询, 1=响应 │
│ Opcode: 操作码,0=标准查询 │
│ AA: 权威回答标志 │
│ TC: 截断标志 │
│ RD: 期望递归查询 │
│ RA: 支持递归查询 │
│ RCODE: 响应码,0=无错误 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Question Section │
├─────────────────────────────────────────────────────────────┤
│ 查询域名(变长)│ 查询类型(QTYPE) │ 查询类(QCLASS) │
│ 3www7example3com0 │ A(1) │ IN(1) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Answer Section │
├─────────────────────────────────────────────────────────────┤
│ 域名 │ 类型 │ 类 │ TTL │ 数据长度 │ 数据(IP 地址等) │
│ 指针 │ A(1) │ IN │ 300 │ 4 │ 93.184.216.34 │
└─────────────────────────────────────────────────────────────┘
编程操作 DNS
// Node.js DNS 模块
const dns = require('dns');
const { promisify } = require('util');
const dnsLookup = promisify(dns.lookup);
const dnsResolve = promisify(dns.resolve);
const dnsResolve4 = promisify(dns.resolve4);
const dnsResolveMx = promisify(dns.resolveMx);
// 1. 基础查询(使用系统配置)
async function basicLookup() {
try {
const { address, family } = await dnsLookup('www.example.com');
console.log(`IP: ${address}, IPv${family}`);
// 输出: IP: 93.184.216.34, IPv4
} catch (err) {
console.error('查询失败:', err);
}
}
// 2. 查询特定记录类型
async function queryRecords() {
// A 记录
const aRecords = await dnsResolve4('example.com');
console.log('A 记录:', aRecords);
// MX 记录
const mxRecords = await dnsResolveMx('example.com');
console.log('MX 记录:', mxRecords);
// 输出: [{ priority: 10, exchange: 'mail.example.com' }, ...]
// NS 记录
const nsRecords = await dns.resolveNs('example.com');
console.log('NS 记录:', nsRecords);
// TXT 记录
const txtRecords = await dns.resolveTxt('example.com');
console.log('TXT 记录:', txtRecords);
// CNAME 记录
const cnameRecords = await dns.resolveCname('www.example.com');
console.log('CNAME 记录:', cnameRecords);
}
// 3. 反向解析
async function reverseLookup() {
const hostnames = await dns.reverse('93.184.216.34');
console.log('反向解析:', hostnames);
// 输出: ['example.com']
}
// 4. 自定义 DNS 服务器
async function customDNS() {
const resolver = new dns.Resolver();
resolver.setServers(['8.8.8.8', '1.1.1.1']);
const resolve4 = promisify(resolver.resolve4.bind(resolver));
const addresses = await resolve4('example.com');
console.log('Google DNS 结果:', addresses);
}
// 5. DNS 性能监控
async function measureDNSPerformance() {
const domains = ['google.com', 'github.com', 'stackoverflow.com'];
for (const domain of domains) {
const start = performance.now();
try {
await dnsLookup(domain);
const duration = performance.now() - start;
console.log(`${domain}: ${duration.toFixed(2)}ms`);
} catch (err) {
console.log(`${domain}: 失败`);
}
}
}
// 执行查询
basicLookup();
# Python DNS 查询
import dns.resolver
import socket
# 1. A 记录查询
answers = dns.resolver.resolve('example.com', 'A')
for rdata in answers:
print(f'A 记录: {rdata.address}')
# 2. MX 记录查询
answers = dns.resolver.resolve('example.com', 'MX')
for rdata in answers:
print(f'MX 记录: {rdata.preference} {rdata.exchange}')
# 3. 自定义 DNS 服务器
resolver = dns.resolver.Resolver()
resolver.nameservers = ['8.8.8.8', '8.8.4.4']
answers = resolver.resolve('example.com', 'A')
# 4. 反向解析
from dns import reversename
addr = reversename.from_address('93.184.216.34')
answers = dns.resolver.resolve(addr, 'PTR')
for rdata in answers:
print(f'反向解析: {rdata.target}')
深入理解(原理剖析)
DNS 缓存机制
DNS 缓存层级和 TTL:
┌─────────────────────────────────────────────────────────────┐
│ 浏览器缓存 │
│ - TTL:由 DNS 响应中的 TTL 字段决定 │
│ - Chrome 缓存 1 分钟(无视 TTL) │
│ - 刷新:chrome://net-internals/#dns │
├─────────────────────────────────────────────────────────────┤
│ 操作系统缓存 │
│ - Windows:默认 24 小时 │
│ - Linux:由 nscd 或 systemd-resolved 管理 │
│ - macOS:由 mDNSResponder 管理 │
├─────────────────────────────────────────────────────────────┤
│ 本地 DNS 服务器缓存 │
│ - 遵循权威服务器的 TTL │
│ - 可配置最小/最大 TTL 覆盖 │
├─────────────────────────────────────────────────────────────┤
│ 递归 DNS 服务器缓存 │
│ - ISP 或公共 DNS 的缓存 │
│ - 大量用户共享,命中率高 │
└─────────────────────────────────────────────────────────────┘
TTL 策略:
- 短 TTL(几秒到几分钟):动态服务、故障转移
- 中 TTL(几小时):一般网站
- 长 TTL(几天):静态资源、不常变更
DNS 安全扩展(DNSSEC)
DNSSEC 解决的问题:
- DNS 欺骗/缓存投毒攻击
- 中间人篡改 DNS 响应
DNSSEC 原理:
┌─────────────────────────────────────────────────────────────┐
│ 数字签名链 │
│ │
│ 根密钥 │
│ │ 签名 .com 区域密钥 │
│ ▼ │
│ .com 密钥 │
│ │ 签名 example.com 区域密钥 │
│ ▼ │
│ example.com 密钥 │
│ │ 签名 www.example.com 记录 │
│ ▼ │
│ www.example.com A 93.184.216.34 + RRSIG(签名) │
├─────────────────────────────────────────────────────────────┤
│ 记录类型: │
│ - RRSIG:数字签名 │
│ - DNSKEY:公钥 │
│ - DS:委托签名者(父区域验证子区域) │
│ - NSEC/NSEC3:否认存在(防止否定回答欺骗) │
└─────────────────────────────────────────────────────────────┘
HTTPDNS
传统 DNS 的问题:
1. 本地 DNS 可能被劫持
2. 调度不精准(基于 LDNS 位置而非用户位置)
3. 缓存刷新不及时
HTTPDNS 解决方案:
┌─────────────────────────────────────────────────────────────┐
│ 客户端 ──HTTP/HTTPS──► HTTPDNS 服务器 │
│ │
│ 优势: │
│ 1. 绕过本地 DNS,防劫持 │
│ 2. 基于客户端 IP 精准调度 │
│ 3. 实时获取最新解析结果 │
│ 4. 支持自定义负载均衡策略 │
└─────────────────────────────────────────────────────────────┘
阿里云 HTTPDNS 示例:
https://203.107.1.1/{account_id}/d?host=www.example.com
响应:{"host":"www.example.com","ips":["1.2.3.4","5.6.7.8"],"ttl":300}
// HTTPDNS 客户端实现
class HTTPDNS {
constructor(accountId) {
this.accountId = accountId;
this.cache = new Map();
}
async resolve(hostname) {
// 检查缓存
const cached = this.cache.get(hostname);
if (cached && Date.now() < cached.expireTime) {
return cached.ips;
}
// 请求 HTTPDNS
try {
const response = await fetch(
`https://203.107.1.1/${this.accountId}/d?host=${hostname}`
);
const data = await response.json();
// 更新缓存
this.cache.set(hostname, {
ips: data.ips,
expireTime: Date.now() + data.ttl * 1000
});
return data.ips;
} catch (err) {
// 降级到系统 DNS
return this.fallbackResolve(hostname);
}
}
async fallbackResolve(hostname) {
// 使用系统 DNS
const dns = require('dns');
const { promisify } = require('util');
const lookup = promisify(dns.lookup);
const result = await lookup(hostname);
return [result.address];
}
}
// 使用示例
const httpdns = new HTTPDNS('your-account-id');
const ips = await httpdns.resolve('api.example.com');
// 使用返回的 IP 直接发起请求
最佳实践
1. DNS 配置优化
; BIND 配置文件示例 named.conf
zone "example.com" IN {
type master;
file "example.com.zone";
; 启用 DNSSEC
dnssec-policy default;
inline-signing yes;
};
; 区域文件 example.com.zone
$TTL 3600
@ IN SOA ns1.example.com. admin.example.com. (
2024010101 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Minimum TTL
IN NS ns1.example.com.
IN NS ns2.example.com.
IN A 93.184.216.34
; 负载均衡(轮询)
www IN A 1.2.3.4
www IN A 1.2.3.5
www IN A 1.2.3.6
; CDN CNAME
static IN CNAME cdn.example.com.
; 邮件服务器
IN MX 10 mail.example.com.
IN MX 20 backup-mail.example.com.
; SPF 记录(反垃圾邮件)
IN TXT "v=spf1 ip4:93.184.216.34 include:_spf.google.com ~all"
; DKIM 记录
selector._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqG..."
2. 高可用 DNS 架构
┌─────────────────────────────────────────────────────────────┐
│ 多 DNS 服务商架构 │
├─────────────────────────────────────────────────────────────┤
│ 域名注册商配置: │
│ NS 记录指向多个服务商 │
│ ├── ns1.aliyun.com │
│ ├── ns2.aliyun.com │
│ ├── ns1.dnspod.net │
│ └── ns2.dnspod.net │
├─────────────────────────────────────────────────────────────┤
│ 主服务商(阿里云): │
│ - A 记录:1.2.3.4(主服务器) │
│ - TTL:60 秒(快速故障转移) │
├─────────────────────────────────────────────────────────────┤
│ 备用服务商(DNSPod): │
│ - A 记录:5.6.7.8(备用服务器) │
│ - TTL:60 秒 │
├─────────────────────────────────────────────────────────────┤
│ 故障转移机制: │
│ - 健康检查主服务器 │
│ - 故障时修改 DNS 记录指向备用 │
│ - 或使用 Anycast IP 自动切换 │
└─────────────────────────────────────────────────────────────┘
3. 前端 DNS 优化
<!-- 1. DNS 预解析 -->
<!-- 提前解析可能访问的域名 -->
<link rel="dns-prefetch" href="//api.example.com">
<link rel="dns-prefetch" href="//cdn.example.com">
<link rel="dns-prefetch" href="//analytics.example.com">
<!-- 2. 预连接(DNS + TCP + TLS) -->
<link rel="preconnect" href="https://api.example.com" crossorigin>
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
<!-- 3. 页面内预加载关键资源 -->
<link rel="prefetch" href="https://example.com/next-page">
<link rel="prerender" href="https://example.com/next-page">
<!-- 4. 使用 Keep-Alive 减少重复解析 -->
<!-- HTTP/2 连接复用 -->
// 前端 DNS 性能监控
function monitorDNSPerformance() {
// 使用 Navigation Timing API
const timing = performance.timing;
const dnsTime = timing.domainLookupEnd - timing.domainLookupStart;
const connectTime = timing.connectEnd - timing.connectStart;
console.log(`DNS 查询时间: ${dnsTime}ms`);
console.log(`TCP 连接时间: ${connectTime}ms`);
// 使用 Resource Timing 监控各个资源的 DNS 时间
const resources = performance.getEntriesByType('resource');
resources.forEach(resource => {
if (resource.domainLookupEnd > 0) {
const dnsDuration = resource.domainLookupEnd - resource.domainLookupStart;
console.log(`${resource.name}: DNS ${dnsDuration}ms`);
}
});
}
// DNS 缓存预热
function prefetchDNS(urls) {
urls.forEach(url => {
const link = document.createElement('link');
link.rel = 'dns-prefetch';
link.href = url;
document.head.appendChild(link);
});
}
// 使用示例
prefetchDNS([
'//api.example.com',
'//cdn.example.com',
'//fonts.googleapis.com'
]);
4. 故障排查工具
# 1. dig 命令(最常用)
# 基本查询
dig example.com
dig example.com A
dig example.com MX
dig example.com NS
# 详细跟踪
dig +trace example.com
# 指定 DNS 服务器
dig @8.8.8.8 example.com
# 反向解析
dig -x 93.184.216.34
# 2. nslookup
nslookup example.com
nslookup -type=MX example.com
# 3. host 命令
host example.com
host -t MX example.com
# 4. 清除本地 DNS 缓存
# macOS
sudo killall -HUP mDNSResponder
# Windows
ipconfig /flushdns
# Linux (systemd-resolved)
sudo systemd-resolve --flush-caches
# 5. 检查 DNS 传播
# 使用在线工具:https://www.whatsmydns.net/
面试要点
-
DNS 查询流程
- 浏览器缓存 -> 操作系统缓存 -> hosts -> 本地 DNS -> 根 -> TLD -> 权威
- 递归查询 vs 迭代查询的区别
- 每个层级的缓存机制和 TTL
-
DNS 记录类型
- A/AAAA:IP 地址记录
- CNAME:别名记录
- MX:邮件记录
- NS:域名服务器记录
- TXT:文本记录(SPF、DKIM)
-
DNS 优化手段
- DNS 预解析(dns-prefetch)
- 预连接(preconnect)
- HTTPDNS(绕过本地 DNS)
- 合理的 TTL 设置
-
DNS 安全
- DNS 劫持和投毒攻击
- DNSSEC:数字签名验证
- DoH(DNS over HTTPS)和 DoT(DNS over TLS)
-
面试高频问题
- 为什么 DNS 使用 UDP?(速度快,数据小,可重试)
- 什么时候使用 TCP?(区域传输、大数据包、DNSSEC)
- 什么是 DNS 负载均衡?(轮询、权重、地理位置)
- 如何实现 DNS 故障转移?(短 TTL、健康检查、多服务商)
- 什么是 CDN 的 CNAME 调度?