应对大流量,通常不会单独的在某一层进行分流处理,可能很多层面都有相应的分流处理,以更好的适应大流量的访问。
# 客户端缓存
对于一些不常更改的资源,缓存到客户端本地,可以有效减少对服务器的访问。在 http 请求中,有以下这些常见的缓存方式:
- Expires 从 HTTP/1.0 开始提供的 Header,后面跟上过期时间。但受限于本地的时间正确性,可能导致缓存提前失效或超期持有,也无法描述‘不缓存’语义。
- Cache-Control HTTP/1.1 定义的 Header,与 Expires 冲突则以此为准,常见有 max-age=100 (以秒为单位)、public|private(区分是否私有)、no-cache(不允许缓存)
- Last-Modified/If-Modified-Since 通过告诉客户端最后修改时间,需要再次请求时,客户端通过 If-Modified-Since 带上最后修改时间给服务端,如果未发生修改,将返回 304 Not Modified,否则返回 200
- ETag/If-None-Match 通过对资源进行哈希计算得到并传递给客户端,客户端带上此标识,服务器将判断是否发生变化
# DNS
大多数情况下,访问的第一步就是解析域名,而在这里,就可以根据不同地区将域名解析到离用户最近的机房去,以便获得更快的访问速度。
# 解析方式
DNS 采用递归的解析方式,客户端首先将地址发送给本机操作系统配置的本地 DNS(手动或 DHCP 获得),例如对于 www.hual.in
,本地 DNS 收到请求后会按照 “是否有 www.hual.in 的权威服务器 --> 是否有 hual.in 的权威服务器 --> 是否有 in 的权威服务器” 的顺序,依次查询地址记录。如果都没有找到,会一致找到最后点号代表的根域名服务器为止。
查询时域名会被还原为
www.hual.in.
在结尾多了一个点,表示 .root 含义
权威服务器:负责翻译特定域名的 DNS 服务器
根域名服务器:固定的无需查询的顶级域名服务器,全球一共有 13 组根域名服务器
# DNS 分流
对于指定域名的 DNS 权威服务器,可以决定返回的 ip 结果,比如根据访问机器、网络链路、服务内容等信息,决定返回的内容是什么,让用户得到最适合的 DNS 结果。
# 缺陷
- 对于一个没有缓存过的域名,首次查询可能需要花费较长时间,由于需要不断的递归去查询其权威服务器。对于网页,可加入如下的标签,让浏览器提前进行预解释:
<link rel="dns-prefetch" href="//domain.hual.in" > |
- DNS 的分级查询,让每一级都有被劫持的风险,例如运营商自己也在干的事儿,劫持 DNS 在网页中注入广告牟利。针对此,出现了 HTTPDNS (DNS over HTTPS, DoH),通过程序代替操作系统直接从权威 DNS 或可靠的 DNS 服务通过 HTTPS 解析,防止被污染。
# 传输链路
当跳过了缓存,解析了 DNS,进行访问时,也会有诸多因素影响访问的速度。对于 HTTP 请求来说,由于其依托于 TCP 协议,TCP 的三次握手、慢启动等,使得建立一个 HTTP 请求的代价并不低。对于 HTTP 这种大量短而小的请求,TCP 的稳定可靠反而发挥不出来,甚至成了制约的因素。
因此也衍生出一些优化的策略:减少请求数(比如使用雪碧图、CSS/JS 文件合并 / 内联等),压缩传输(减小传输的数据大小)、扩大并发请求(使用不同子域名突破浏览器限制请求数)、连接复用(多个请求复用同一个 TCP 连接,降低建立连接的开销)等
但在 HTTP/1.x 中,连接复用是有缺陷的,比如 “队首阻塞” 问题,排在前面的请求如果耗时较长,则后面的请求就被阻塞。无论这个队列是建立在客户端,还是在服务端,都不能很好的解决。因为此时 HTTP 请求就是最小的粒度,HTTP/2 中,帧(Frame)才是最小单位,根据流 ID,可将不同的帧重组为正确的数据,这样不必等待一个请求完成才能进行下一个,可以避免排队请求阻塞的问题。
压缩
在连接复用的情况下,不能通过 TCP 连接的断开来判断一次数据是否传输完毕,而 Content-Length 在即时压缩的情况下,无法得知确切的长度,因此 HTTP/1.1 中,使用分块传输编码来判断资源传输结束。在 Header 中加入 “Transfer-Encoding: chunked”,Body 使用分块传输,其包含十六进制的长度和对应长度的数据内容,最后以长度为 0 的分块表示传输结束。
HTTP/3
“快速 UDP 网络连接”(Quick UDP Internet Connection,QUIC)是由谷歌开发的基于 UDP 的传输协议,它将是下一代 HTTP 协议标准。
由于 TCP 协议的可靠性保证,在传输时如果数据包发生丢失等问题,需要等待重传,在某些情况下将影响传输速度。而 UDP 则没有这个问题,但其可靠性得不到保障,所以需要在自身层面实现。同时 UDP 面向无连接的传输,当网络发生变更时,不需要重新建立连接这种耗时操作,降低延迟时间。
# 负载均衡
经过链路,数据到达服务端入口,此处将再次对数据进行分发,实现负载均衡的效果。常见的有四七层负载均衡:
- 四层负载均衡,工作在第二层(数据链路层,修改 MAC 地址)或第三层(网络层,改写 IP 地址),转发数据,性能高
- 七层负载均衡,工作在第七层,属于反向代理的一种,可以解析更加丰富的规则设置,但更加的消耗 CPU
OSI 七层模型参考
# | 层 | 数据单元 | 功能 |
---|---|---|---|
7 | 应用层 | 数据 | 为应用软件提供服务的接口,用于与其他应用软件之间的通信。典型协议有:HTTP、HTTPS、FTP、Telnet、SSH、SMTP 等 |
6 | 表现层 | 数据 | 把数据转换为能与接收者的系统格式兼容并适合传输的格式 |
5 | 会话层 | 数据 | 负责在数据传输中设置和维护计算机网络中两台计算机之间的通信连接 |
4 | 传输层 | 数据段 | 把传输表头加至数据后以形成数据包。传输表头包含所使用的协议等发送信息。典型协议有:TCP、UDP、RDP、SCTP、FCP 等 |
3 | 网络层 | 数据包 | 提供数据的传输路径选择和转发功能,将网络表头附加至数据段后以形成报文(即数据包)。典型协议有:IPv4/IPv6、IGMP、ICMP、EGP、RIP 等 |
2 | 数据链路层 | 数据帧 | 负责点对点的网络寻址,错误侦测和纠错。当表头和表尾被附加至数据包后,就形成数据帧(Frame)。典型协议有:Wi-Fi(802.11)、Ethernet(802.3)、PPP 等 |
1 | 物理层 | 比特流 | 在局域网上传送数据帧,负责管理电脑通信设备和网络媒体之间的互通。包括针脚、电压、线缆规范、集线器、中继器、网卡、主机接口卡等 |
# 数据链路层负载均衡
该层传输的内容是数据帧,其负载均衡所做的工作就是修改数据帧中的 MAC 目标地址 ,让原本发送给负载均衡器的数据被二层交换机根据新的 MAC 地址转发给目标服务器。
由于只更改了 MAC 地址,在第三层网络层看来其源 / 目标 IP 地址没变,因此要求真实服务器的 IP 地址和负载均衡器一样才能正确使用该数据,也因此,数据可以直接从真实服务器返回客户端,此时响应不经过负载均衡器:
# 网络层负载均衡
该层传输的分组数据包,其有两种模式:
- 新建一个数据包,将原数据包作为新数据包的 payload,并将新数据包的 IP 指向真实服务器,真实服务器收到后进行拆包,拿到原始数据。这种套娃模式也叫 “IP 隧道”,为了正确返回,它也需要将 IP 设置为与负载均衡器一样。
- 直接修改原数据包 Header 中的 IP,发送给真实服务器,真实服务器处理后需要经由负载均衡器返回客户端,才能正确使用,这也称作 “NAT 模式”,此时负载均衡器容易成为瓶颈。
# 应用层负载均衡
前面两种都属于 “转发”,在四层后的负载均衡模式就无法转发,只能代理:
客户端会先与负载均衡建立 TCP 连接,负载均衡器再根据策略选择真实服务器并与之建立 TCP 连接,因此存在两条不同的 TCP 连接通道,相对而言会更加消耗资源。
“代理” 根据哪一方能感知到的原则,可分为正向代理,反向代理和透明代理。
七层负载均衡属于反向代理的一种。它工作在应用层,可感知应用层通信具体内容,可做出更多处理,但也更加消耗性能。
# 均衡策略
- 轮询(Round Robin)
- 权重轮询(Weighted Round Robin)
- 随机 (Random)
- 权重随机(Weighted Random)
- 一致性哈希(Consistency Hash)
- 响应速度(Response Time)
- 最少连接数(Least Connection)