返回首页

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 查询/响应报文结构:

┌─────────────────────────────────────────────────────────────┐
│                      Header12 字节)                        │
├─────────────────────────────────────────────────────────────┤
│  标识(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 │ 300493.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/

面试要点

  1. DNS 查询流程

    • 浏览器缓存 -> 操作系统缓存 -> hosts -> 本地 DNS -> 根 -> TLD -> 权威
    • 递归查询 vs 迭代查询的区别
    • 每个层级的缓存机制和 TTL
  2. DNS 记录类型

    • A/AAAA:IP 地址记录
    • CNAME:别名记录
    • MX:邮件记录
    • NS:域名服务器记录
    • TXT:文本记录(SPF、DKIM)
  3. DNS 优化手段

    • DNS 预解析(dns-prefetch)
    • 预连接(preconnect)
    • HTTPDNS(绕过本地 DNS)
    • 合理的 TTL 设置
  4. DNS 安全

    • DNS 劫持和投毒攻击
    • DNSSEC:数字签名验证
    • DoH(DNS over HTTPS)和 DoT(DNS over TLS)
  5. 面试高频问题

    • 为什么 DNS 使用 UDP?(速度快,数据小,可重试)
    • 什么时候使用 TCP?(区域传输、大数据包、DNSSEC)
    • 什么是 DNS 负载均衡?(轮询、权重、地理位置)
    • 如何实现 DNS 故障转移?(短 TTL、健康检查、多服务商)
    • 什么是 CDN 的 CNAME 调度?