计算机网络分层详解

计算机网络把复杂的通信问题拆成若干层次,每一层只关心自己的职责,并向上层提供服务。理解分层是排查网络问题的前提:知道问题出在哪一层,才知道该用哪个工具、看哪个指标。

本文先概述分层模型与性能指标,再自下而上逐层展开:物理层 → 数据链路层 → 网络层(含 IP 地址)→ 传输层 → 应用层。

网络分层模型

OSI 七层模型

层次 职责
应用层 为计算机用户提供接口和服务
表示层 数据处理(编码、加密、压缩)
会话层 管理通信会话
传输层 管理端到端的通信连接
网络层 数据路由,决定数据在网络中的路径(路由器只到此层)
数据链路层 管理相连节点之间的通信
物理层 数据通讯的光电特性

TCP/IP 四层模型

实际工程中更常用的是 TCP/IP 四层模型,它把 OSI 的若干层做了合并:

  1. 应用层(对应 OSI 的应用层、表示层、会话层):HTTP、DNS、DHCP
  2. 传输层:TCP、UDP
  3. 网络层:IP、ICMP
  4. 网络接口层(对应 OSI 的数据链路层 + 物理层):ARP、RARP

TCP/IP 收发流程

数据在发送端自上而下逐层封装,每层加上自己的头部;到接收端再自下而上逐层解封装。

网络收发流程-tcpip示例

网络性能指标

带宽:表示链路的最大传输速率,单位通常为 b/s(比特/秒)。注意比特与字节的换算,100Mbps = 100Mbit/s = 12.5MB/s

吞吐量:表示单位时间内成功传输的数据量,单位通常为 b/s(比特/秒)或者 B/s(字节/秒)。吞吐量受带宽限制,吞吐量除以带宽即为该网络的使用率。

延时:表示从网络请求发出后,一直到收到远端响应,所需要的时间延迟。总时延由四部分组成:

1
总时延 = 发送时延 + 排队时延 + 传播时延 + 处理时延

ping 命令可以查看 RTT,也就是报文在端到端来回一次的时间。

PPS:是 Packet Per Second(包/秒)的缩写,表示以网络包为单位的传输速率。

除此之外,评估一个网络还会关注:网络的可用性(能否正常通信)、并发连接数(TCP 连接数量)、丢包率(丢包百分比)、重传率(重新传输的网络包比例)等。

物理层

物理层是分层模型的最底层,它不关心数据的含义,只负责把比特流从一台设备送到另一台设备。

作用

物理层负责连接不同的物理设备,传输比特流,传输介质分为有线介质与无线介质两类。它只管比特流的传输,无法感知也无法控制传输过程中是否出错。

信道与通信方式

信道是往一个方向传送信息的媒体。一条通讯电路包含一个接收信道和一个发送信道。按通信方向可分为三类:

  1. 单工通信信道:只能单向传输,如有线电视、无线收音机
  2. 半双工通信信道:可以双向传输,但双方不能同时发送或接收
  3. 全双工通信信道:双方都可以同时接收和发送信息,如网线

为了让一条物理链路承载多路信号,物理层还会使用复用与分用技术,常见的有频分复用、时分复用和光纤上使用的波分复用:发送端把多路信号复用到同一信道,接收端再分用还原。

数据链路层

有了比特流之后,还需要有人来划分边界、检查错误,这就是数据链路层在相邻节点之间要做的事。

主要作用

物理层只管比特流,出错了也不知道,所以需要数据链路层在相邻节点之间做三件事:

  1. 封装成帧
  2. 透明传输
  3. 查错检测

查错检测

一般使用循环冗余校验码 CRC。发送方按约定的生成多项式算出校验码附在帧尾,接收方重新计算并比对,不一致就丢弃该帧。

以太网协议

Ethernet 是一种应用于数据链路层的局域网技术,使相邻设备完成数据帧的传输,寻址依据是网卡的 MAC 地址。

MTU

MTU 是数据链路层能承载的载荷上限,以太网一般为 1500 字节(注意这说的是载荷;加上 14 字节帧头和 4 字节 FCS,以太网帧最大是 1518 字节)。超过这个尺寸的 IP 数据包要分片才能传——分片发生在 IP 层,不是数据链路层,具体机制见下面「IP 协议」一节。

一条路径的 MTU 由链路中所有 MTU 的最小值决定,因此不建议随意更改本机 MTU。用 ifconfig 可以看到网卡当前的 MTU

1
2
3
4
5
6
7
8
9
root@R7000:~# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.20.108.198 netmask 255.255.240.0 broadcast 172.20.111.255
inet6 fe80::215:5dff:fee6:3127 prefixlen 64 scopeid 0x20<link>
ether 00:15:5d:e6:31:27 txqueuelen 1000 (Ethernet)
RX packets 12934 bytes 1578453 (1.5 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 10292 bytes 2760086 (2.7 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

网络层

数据链路层解决的是相邻节点之间的传输,而网络层要解决的是跨越多个网络、把数据包从源主机送到目的主机的问题。

IP 协议

IP 协议提供的是无连接、不可靠的尽力交付服务:它不建立连接,不保证数据包按序到达、不保证不丢失,只负责根据目的 IP 地址查路由表逐跳转发。可靠性由上层的 TCP 来补齐。数据包超过链路 MTU 时,IP 层负责分片与重组,靠首部里三个字段配合完成:标识(同一个原始包的所有分片共用一个 ID,用于重组)、片偏移(这一片的数据在原始包里的位置,以 8 字节为单位)、以及 DF 位(Don’t Fragment,置 1 表示”不许分片”)。重组只在最终目的主机进行,中间路由器不会替你拼回去;任何一片丢了,整个原始包都得重传。

路径 MTU 发现(PMTUD)

DF 位是整条机制的关键。发送方把 DF=1,中间路由器遇到”包太大又不许分片”时,会丢包并回一个 ICMP Type 3 Code 4(Destination Unreachable / Fragmentation Needed),而且在这个 ICMP 报文里带上自己下一跳的 MTU。发送方收到后按这个值调小,再试。这样反复几轮就探出了整条路径的最小 MTU——这就是 PMTUD。

Linux 默认就在做这件事:

1
2
sysctl net.ipv4.ip_no_pmtu_disc      # 0 表示启用 PMTUD
ip route get 8.8.8.8 # 缓存下来的 path MTU 会显示在这里

MTU 黑洞:运维现场最难定位的问题之一

PMTUD 完全依赖那个 ICMP 报文能回来。而现实里大量网络出于”安全”考虑无脑封掉 ICMP,于是:路由器丢了大包、发了 ICMP,ICMP 被防火墙吃掉,发送方什么都没收到——它只知道包没被确认,于是一直重传同样大小的包,一直被丢

症状非常有辨识度,也非常容易误判:

  • ping 通(ICMP echo 是小包),TCP 三次握手也成功(SYN/ACK 都是小包)
  • 一旦开始传数据就卡死,curl 挂在那里不动,最后超时
  • 小文件正常、大文件必挂;或者请求正常、响应大了就挂

排查手段正好回扣本文前面的命令——用 pingDF 位逐步加大载荷,找到卡住的尺寸:

1
2
3
4
5
6
# -M do 置 DF 位,-s 指定载荷(不含 28 字节的 IP+ICMP 头)
ping -M do -s 1472 8.8.8.8 # 1472+28=1500,标准以太网刚好能过
ping -M do -s 1473 8.8.8.8 # 过不去就说明 1500 是上限
# 二分找到实际能过的最大值,再 +28 就是 path MTU

tracepath 8.8.8.8 # 直接给出各跳的 MTU 变化

最后一层:TCP 之所以很少踩这个坑,是因为它在握手时就用 MSS 选项协商了单个报文段的上限(通常 = 本地 MTU − 40),从源头避免分片,出问题时还能靠 net.ipv4.tcp_mtu_probing=1 自己往下探。而 UDP 没有这套机制——所以 QUIC 必须自己实现 MTU 探测,早期不少 VPN、隧道(GRE/IPsec 额外占掉几十字节头部)的疑难问题都出在这里。

IP 地址

IP 地址就是网络中的地址信息,网络层依靠它来定位主机所在的网络。

IPv4 与 IPv6

IPv4 地址长 32 位,格式为 A.B.C.D,每段取值范围 0 ~ 255,地址空间为 2^32。

IPv6 地址长 128 位,格式为 A:B:C:D:E:F:G:H,用十六进制表示,地址空间为 2^128。

公网 IP 和私网 IP

公网 IP 地址由 inter NIC 负责分配,公有地址全球唯一。私有地址是非注册地址,供组织机构内部使用,不由 internet 分配,也不会出现在 internet 的路由中。

按传统的分类编址,各类地址范围与其中的私有地址段如下:

  1. A 类 IP 地址:1.0.0.0-126.255.255.255(私有地址 10.0.0.0 ~ 10.255.255.255
  2. B 类 IP 地址:128.0.0.0-191.255.255.254(私有地址 172.16.0.0 ~ 172.31.255.255
  3. C 类 IP 地址:192.0.0.0-223.255.255.254(私有地址 192.168.0.0 ~ 192.168.255.255

私有地址要访问外部网络,需要先转换成公网 IP 地址(也就是下文的 NAT)。内网主机都是借用路由器上的那个公网 IP 去连接 internet 的。因此,私网地址访问互联网地址很方便,但互联网地址主动访问私有地址很困难。

同一个局域网内 IP 地址是唯一的,但不同局域网之间 IP 地址可以重复。

网段与广播地址

192.168.1.0/24 为例,/24 表示前 24 位是网络号,后 8 位是主机号:

  1. 主机 ID 全为 0 的地址 192.168.1.0:特指某个网段
  2. 主机 ID 全为 1 的地址 192.168.1.255:特指该网段的全部主机,即广播地址

localhost、127.0.0.1 和 0.0.0.0 的区别

  1. localhost:是一个域名,windows 默认将 localhost 指向 127.0.0.1
  2. 127.0.0.1:回环地址,凡是 127 开头的 IP 地址都是回环地址。发送给回环地址的数据由本机自己接收,根本不会传出去
  3. 0.0.0.0:并不是一个真实的 IP 地址,它表示本机所有的 IPv4 地址,服务监听 0.0.0.0 就是监听本机所有 IP 上的该端口

ARP 与 RARP 协议

IP 地址用于跨网络寻址,但真正在链路上发帧要靠 MAC 地址,两者之间的转换就由这一对协议完成:

  • arp:地址解析协议,根据 IP 寻找 MAC 地址
  • rarp:逆地址解析协议,根据 MAC 寻找 IP 地址

ARP 到底属于哪一层

本文开头的 TCP/IP 四层模型里把 ARP/RARP 划在了网络接口层,这一节却挂在网络层下面——这个”矛盾”本身就值得说清楚,因为教材的分歧正源于此:

  • 从封装看它在链路层:ARP 报文由以太网帧直接承载(帧类型 0x0806),根本不经过 IP 首部。所以它不是”IP 之上的协议”。
  • 从功能看它服务于网络层:它存在的唯一目的是把 IP 地址翻译成 MAC 地址,没有 IP 就没有 ARP 的意义。

所以两种说法都有道理,取决于按”怎么封装”还是按”为谁服务”来分类。知道这一点比背下某本书的归属更有用。

顺着 ARP 有几个实战点很值得串起来:

缓存与老化ip neigh(或 arp -n)看的是 ARP 缓存,条目有老化时间;换了网卡、改了 IP 之后连不通,十有八九是邻居的缓存还没过期,ip neigh flush allarp -d <ip> 清掉即可。

无偿 ARP(gratuitous ARP)。主动广播”这个 IP 现在是我的 MAC”,没人问也发。这是 VIP 漂移能生效的关键一步:keepalived/VRRP 切主时,新的 master 必须发无偿 ARP 让交换机更新 MAC 表、让同网段的邻居更新 ARP 缓存,否则流量还会往老的 MAC 送,表现为”VIP 已经飘过来了但服务还是不通”,等缓存自然老化才恢复。

ARP 欺骗。ARP 没有任何认证,谁都能声称某个 IP 是自己的,这就是同网段中间人攻击的原理。防御手段是 arp -s 静态绑定、交换机侧的 DAI(Dynamic ARP Inspection),或者干脆做二层隔离。

代理 ARP(proxy ARP)。路由器替别的网段的主机应答 ARP,让两台其实不在同一网段的机器”看起来像在同一个二层里”。老式网络合并、以及某些 VPN/容器网络方案会用到它。

查看本机 ARP 缓存:

1
2
3
4
5
6
7
8
9
10
11
12
13
PS C:\Users\imwl> arp -a

接口: 10.0.0.100 --- 0xb
Internet 地址 物理地址 类型
10.0.0.1 xx-xx-xx-xx-xx-xx 动态
10.0.0.2 xx-xx-xx-xx-xx-xx 动态
10.0.0.3 xx-xx-xx-xx-xx-xx 动态
224.0.0.22 01-00-5e-00-00-16 静态
224.0.0.251 01-00-5e-00-00-fb 静态
224.0.0.252 01-00-5e-00-00-fc 静态
233.82.96.53 01-00-5e-52-60-35 静态
239.255.255.250 01-00-5e-7f-ff-fa 静态
255.255.255.255 ff-ff-ff-ff-ff-ff 静态

需要注意的是,IP 地址在转发过程中通常保持不变,而 MAC 地址每经过一个网关都必然会变化。

NAT 技术

NAT 即网络地址转换,由 NAT 网关完成,目的是解决 IP 地址不够用的问题,让内网多个主机使用同一个公网 IP 访问互联网。

以内网 192.168.1.12192.168.1.13 共用公网地址 <PUBLIC_IP> 为例,网关通过端口来区分不同的内网连接:

1
2
192.168.1.12:6667 → <PUBLIC_IP>:16667
192.168.1.13:6668 → <PUBLIC_IP>:16668

与之相对的是转发网关:数据经过它时 IP 地址不会被改写。

ICMP 协议

ICMP 一般配合 IP 协议使用,用来传递差错报告和控制信息,pingtraceroute 都建立在它之上。

ping 可以按由近及远的顺序定位问题:

  1. ping 127.0.0.1 不通:问题出在本机这一侧。常见原因是 lo 接口被 down 掉、防火墙规则误封了回环,或者容器/netns 里的 lo 没有启用,先用 ip link show loip addr show lo 看状态,必要时 ip link set lo up,再检查一下有没有规则挡了回环,重装系统不是常规手段
  2. ping 网关地址不通:检查电脑到路由器之间是否连通,比如网线、网卡配置
  3. ping 远程地址不通:本地都正常,需要联系运营商(ISP

traceroute 则利用逐跳返回的 ICMP 报文,把数据包沿途经过的路由器一跳一跳打印出来,用于判断链路在哪一跳中断或变慢。

路由

  1. 静态路由:在路由器上一条一条配置规则,一般适用于小型网络
  2. 动态路由:根据路由算法自动生成路由表

主要的路由算法有两类:

  1. 距离矢量路由算法,如 BGPRIP
  2. 链路状态路由算法,如 OSPF

整个因特网实际上由很多机构管理,每个机构管理自己的网络,它们有权决定采用什么协议和网络控制策略。这样在同一个机构管理下的网络称为一个自治系统(autonomous systems,AS),也就是说,因特网实际上是由很多自治系统构成的。因此路由协议也分内外两种:

  • 自治系统内部协议:RIPOSPF
  • 自治系统外部协议:BGP

传输层

网络层把数据包送到了目的主机,但一台主机上有很多进程,传输层要解决的就是进程与进程之间(可跨设备)的通信,靠端口号来区分进程。

UDP

UDP(User Datagram Protocol,用户数据报协议)非常简单,几乎只是在 IP 之上加了端口和校验。

UDP协议1

UDP 首部构造:

UDP协议2

特点:

  1. 无连接协议
  2. 不保证可靠的交付数据
  3. 面向报文传输
  4. 没有拥塞控制
  5. 首部开销小

正因为它简单、可控,HTTP 3.0 这类应用层协议才选择用 UDP 作为底层技术,然后在 UDP 基础上自己解决可靠性问题。

TCP

TCP(Transmission Control Protocol,传输控制协议)与 UDP 相反,用复杂的机制换取了可靠性。

TCP协议1

TCP 首部构造:

TCP协议2

TCP标志

特点:

  1. 面向连接协议
  2. 点到点通信
  3. 提供可靠的传输服务
  4. 提供全双工的通信
  5. 面向字节流的协议(可能对用户数据进行拆分、合并等操作)

可靠传输的基本原理

不可靠的信道会带来三类问题:发送的消息丢失、确认的消息丢失、确认的消息很久才到。解决思路有两种:

  1. 停止等待协议:最简单,发一个等一个,但对信道的利用率不高
  2. 连续 ARQ 协议:滑动窗口 + 累计确认,可以连续发送多个报文

这里的窗口,指明了允许对方发送的数据量。

TCP 的可靠传输在此基础上做了三点强化:

  1. 基于连续 ARQ 协议
  2. 滑动窗口以字节为单位
  3. 支持选择重传

TCP可靠传输

发送、接收窗口的大小可以用来控制 TCP 协议的流速。窗口越大,同时可以发送、接收的数据就越多,支持的吞吐量也就越大。当然,窗口越大,如果数据发生错误,损失也就越大,因为需要重传越多的数据。

TCP 拆包的作用是将任务拆分处理,降低整体任务出错的概率,以及减小底层网络处理的压力。拆包过程需要保证数据经过网络的传输后,又能恢复到原始的顺序,这个保证来自首部里的序列号和确认号:序列号标定的是每个字节在整个字节流中的位置,接收方据此重排、去重。

需要区分的是,「粘包」并不是 TCP 的某个功能或设计目的。TCP 面向的是字节流,它本身就没有「包」的边界概念,应用层要自己用长度前缀或分隔符来划分消息边界,缺了这层边界才会出现所谓的粘包、拆包现象。发送侧把多个小数据合并起来发的机制另有其名,叫 Nagle 算法,需要低延迟时可以用 TCP_NODELAY 关掉。

配合可靠传输的还有超时定时器:报文发出后若在定时器超时前没有收到确认,就触发重传。

流量控制

流量控制针对的是点对点的通信量,主要是抑制发送端发送数据的速率,以便使接收端来得及接收。TCP 通过让接收方指明希望从发送方接收的数据字节数(即窗口大小)来实现。

坚持定时器

当接收方通告窗口为 0,发送方就停下来等待;如果后续那个“窗口已打开”的通知丢失,双方就会互相等待形成死锁。为此引入坚持定时器:收到 0 窗口通告后就启动它,每隔一段时间发送一个窗口探测报文。

拥塞控制

流量控制只看通信双方,拥塞控制要考虑的是整个网络。一条数据链路会经过非常多的设备,其中各个部分都有可能成为网络传输的瓶颈。

拥塞控制的目标是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。中间设备通常不会主动告知拥塞,所以 TCP 只能自己推断——而推断的依据不止”超时”一种。

整套机制围绕两个变量展开:cwnd(拥塞窗口,我最多能有多少字节在路上没被确认)和 ssthresh(慢启动阈值,指数增长和线性增长的分界)。真正的发送窗口取 min(cwnd, 接收方通告的窗口)——这也是拥塞控制和上面流量控制的交汇点。

  1. 慢启动cwnd 从 1 个 MSS 起,每收到一个 ACK 就翻倍,实际是指数增长。”慢”指的是起点低,不是涨得慢。
  2. 拥塞避免cwnd 涨到 ssthresh 之后转为线性增长(每个 RTT 加 1 个 MSS),小心试探上限。

关键在第三步——两种丢包信号的处置完全不同

  1. 超时(重传定时器到期):判定为重度拥塞,ssthresh 减半,cwnd 退回 1 重新慢启动。代价极大。
  2. 收到 3 个重复 ACK:说明后续报文还在通,只是中间少了一个,判定为轻度乱序/单包丢失。于是快重传(不等超时立刻补发那一个)+ 快恢复ssthresh 减半,cwnd 设为新的 ssthresh 而不是 1,直接进入线性增长)。

第 3、4 条的分野是 TCP Reno 相对 Tahoe 的核心改进,也是这一节最该记住的地方:同样是丢包,判断依据不同,代价差了一个数量级。所以”TCP 把报文超时当作拥塞的信号”这句话是不完整的——超时只是最坏的那条路径,正常情况下绝大多数丢包由重复 ACK 发现。

再往前一步,现代内核的默认算法已经不是 Reno 了:

  • CUBIC(Linux 2.6.19 起的默认值):用三次函数替代线性增长——刚降窗后涨得快,接近上次的丢包点时放缓,越过去之后再次加速。这样在高带宽时延积(长肥管道)的链路上能更快吃满带宽,而 Reno 的线性增长在这种链路上要爬很久。
  • BBR:干脆不把丢包当拥塞信号,而是主动测量瓶颈带宽和最小 RTT,按这两个值算发送速率。这就解释了一个很实际的现象:跨境链路上常有与拥塞无关的随机丢包,Reno/CUBIC 会误判成拥塞而反复降窗,换成 BBR 立刻见效。
1
2
sysctl net.ipv4.tcp_congestion_control      # 看当前算法
sysctl net.ipv4.tcp_available_congestion_control

三次握手和四次挥手

先看几个标志位的含义:

  • 如果一个 Host 主动向另一个 Host 发起连接,称为 SYN(Synchronization),请求同步
  • 如果一个 Host 主动断开请求,称为 FIN(Finish),请求完成
  • 如果一个 Host 给另一个 Host 发送数据,称为 PSH(Push),数据推送

以上 3 种情况,接收方收到数据后都需要给发送方一个 ACK(Acknowledgement)响应。请求/响应的模型是可靠性的要求,如果一个请求没有响应,发送方可能会认为自己需要重发这个请求。

三次握手

为什么不能两次握手建立连接:如果只有两次握手,某个连接请求很慢时,发送方会以为丢包而重发,导致建立两次连接、发送重复数据。三次握手能防止已经失效的连接请求报文传送到对方而引起错误。

四次挥手

为什么建立连接三次、关闭连接却要四次:当 Server 端收到 Client 端的 SYN 连接请求报文后,可以直接把用于应答的 ACK 和用于同步的 SYN 合并成一个 SYN+ACK 报文发出。但是关闭连接时,Server 端收到 FIN 报文,很可能并不会立即关闭 SOCKET,所以只能先回复一个 ACK 报文,告诉 Client 端“你发的 FIN 报文我收到了”;只有等到 Server 端所有的报文都发送完了,才能发送自己的 FIN 报文,因此不能合并,故需要四步。

几个计时器

连接在等待状态中是不会释放的,端口依然被占用。等待计时器(2MSL)的作用有两个:

  1. 确保最后一个 ACK 能到达对端。如果这个 ACK 丢了,被动关闭方迟迟等不到确认,会重发 FIN,主动关闭方留在 TIME_WAIT 就是为了还能再补一次 ACK
  2. 确保当前连接的所有报文都已过期,不会干扰新连接

保活计时器(Keepalive Timer):主要是为了防止两个 TCP 连接出现长时间的空闲。当客户端与服务器端建立 TCP 连接后,很长时间内客户端都没有向服务器端发送数据,此时很有可能是客户端出现故障,而服务器端会一直处于等待状态,保活计时器就是解决这种问题而生的。工作原理是:每当服务器端收到客户端的数据,都将保活计时器重新设置(通常设置为 2 小时);过了 2 小时后如果没有收到客户端的数据,会发送探测报文段给客户端,并且每隔 75 秒发送一个,当连续发送 10 次以后仍没有收到对端的来信,则服务器端认为客户端出现故障,并会终止连接。

应用层

有了端到端的可靠通道,剩下的就是各类具体业务协议了。下面挑几个和日常运维关系最密切的来看:DNS 负责把域名变成 IPDHCP 负责给主机分配 IP,两者共同支撑了“插上网线就能上网”这件事。

DNS

DNS(Domain Name System,域名系统)运行在 UDP 之上,使用 53 端口,作用是把域名翻译成 IP 地址:

1
2
3
4
root@R7000:~# ping tj.imwl.ml
PING tj.imwl.ml (<PUBLIC_IP>) 56(84) bytes of data.
64 bytes from <PUBLIC_IP> (<PUBLIC_IP>): icmp_seq=1 ttl=63 time=0.261 ms
64 bytes from <PUBLIC_IP> (<PUBLIC_IP>): icmp_seq=2 ttl=63 time=0.250 ms

域名是分层的,从右往左逐级细化,例如 tj.imwl.cf

  • 根域:/
  • 顶级域:cfuscomgov
  • 二级域:imwlqqaliyun
  • 三级域:tjwww

与之对应的服务器也分为根域名服务器、顶级域名服务器和权威域名服务器。

一次域名解析的查找顺序是:浏览器缓存 → 本地 hosts 文件 → 本地 DNS 域名服务器 → 根域名服务器(再由它按迭代或递归的方式往下查)。

由于同一个域名可以返回不同的 IPDNS 也常被用来做负载均衡,分为内部负载均衡和全局负载均衡两种。

HttpDNS

传统 DNS 有两类毛病:一是解析慢、更新不及时;二是缓存、转发、NAT 等问题会导致服务端误判客户端所在的位置和运营商,从而影响流量的调度。

httpDNS 通过客户端 SDK 加服务端配合,用 HTTP 直接调用解析 DNS,绕过传统 DNS 的上述缺点,实现了智能调度。

DHCP

DHCP 是动态主机设置协议,一个使用 UDP 的局域网协议,服务端端口为 67,作用是给客户端分配一个临时 IP 和对应的租期。

分配过程如下:

  1. Client 使用 UDP 协议广播 DHCP 发现报文
  2. DHCP 服务器发出 DHCP 提供报文
  3. Client 向 DHCP 服务器发出 DHCP 请求报文
  4. DHCP 服务器回应并提供 IP 地址

除首次分配外,DHCP Client 重新登录和更新租约也走类似的报文交互。

A、AAAA、CNAME、NS 记录

域名解析的结果由权威服务器上的一条条记录决定,常见的有以下几类。

A

IPv4 地址与域名的对应关系:

1
2
; 定义 www.example.com 的 ip 地址
www.example.com. IN A <PUBLIC_IP>;

AAAA

IPv6 地址与域名的对应关系:

1
www.example.com.  IN  AAAA    1080::8:800:200C:417A

CNAME

CNAME 定义了 a.example.comb.example.com 的别名。用户在浏览器中输入 a.example.com 时,通过 DNS 查询会知道它是 b.example.com 的别名,因此需要实际 IP 的时候,会去拿 b.example.comA 记录。

1
2
; 定义 www.example.com 的别名
a.example.com. IN CNAME b.example.com.

因为走的是 DNS 查询的路径,速度很快(因为有缓存),不需要 HTTP 重定向等操作。把一个网站迁移到新域名而旧域名仍然保留时,或者把静态资源放到 CDN 上时,CNAME 就非常有用。

NS

NS 记录指明某个域由哪些权威域名服务器负责解析,是把子域的解析权下放出去的方式,也是递归查询能一级级往下走的依据。

常见问题

分层理清之后,日常遇到的问题基本都能对应到某一层。下面是几个高频场景的排查记录。

域名无法访问

现象是域名 ping 不通、nslookup 也一直超时,但直接 ping IP 是通的 —— 这一条是关键,说明网络层本身没问题,故障只能定位在应用层的域名解析上:/etc/resolv.conf 里配置的域名解析服务器有问题。换成一个可用的 nameserver 就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@e5388c88162b:/# ping www.baidu.com
ping: unknown host

root@e5388c88162b:/# nslookup www.baidu.com
;; connection timed out; no servers could be reached

root@e5388c88162b:/# ping 1.1.1.1
PING 1.1.1.1 (1.1.1.1): 56 data bytes
64 bytes from 1.1.1.1: icmp_seq=0 ttl=57 time=2.412 ms
64 bytes from 1.1.1.1: icmp_seq=1 ttl=57 time=2.375 ms

root@e5388c88162b:/# echo "nameserver 1.1.1.1" > /etc/resolv.conf
root@e5388c88162b:/# ping www.baidu.com
PING www.wshifen.com (119.63.197.139): 56 data bytes
64 bytes from 119.63.197.139: icmp_seq=0 ttl=57 time=1.980 ms
64 bytes from 119.63.197.139: icmp_seq=1 ttl=57 time=2.108 ms

域名解析过慢

可能需要更换 DNS 解析服务器,或者开启 dns 缓存。