网络篇

作者: shaokang 时间: February 23, 2022字数:6164

RESTful

URL 定位资源,用 HTTP 动词(GET,POST,DELETE)描述操作。

浏览器地址栏输入网址整个过程

(1)解析 URL:分析传输协议和资源路径,判断协议或主机名是否合法,不合法将内容传递给搜索引擎。没问题的话检查是否出现非法字符,并对其转义。
(2)缓存判断:强缓存和协商缓存。
(3)DNS 解析:先查本地是否有域名的 ip 地址缓存(浏览器、hosts 文件)。若没有会想本地 DNS 服务器发起请求,检查是否存在缓存。若还是没有,解析想根域名服务器发起请求。依次查找顶级域名服务器、权威域名服务器,直到拿到 IP 地址返回给用户。(用户向本地 DNS 请求输入递归请求,本地 DNS 向各级域名请求属于迭代请求)
(4)获取 MAC 地址:当浏览器得到 IP 地址后,数据传输还需要知道目的主机 MAC 地址,因为应用层下发数据给传输层,TCP 协议会指定源端口号和目的端口号,然后下发给网络层。网络层会将本机地址作为源地址,获取的 IP 地址作为目的地址。然后将下发给数据链路层,数据链路层的发送需要加入通信双方的 MAC 地址,本机的 MAC 地址作为源 MAC 地址,目的 MAC 地址需要分情况处理。通过将 IP 地址与本机的子网掩码相与,可以判断是否与请求主机在同一个子网里,如果在同一个子网里,可以使用 APR 协议获取到目的主机的 MAC 地址,如果不在一个子网里,那么请求应该转发给网关,由它代为转发,此时同样可以通过 ARP 协议来获取网关的 MAC 地址,此时目的主机的 MAC 地址应该为网关的地址.
(5)TCP 三次握手:首先客户端向服务器发送一个 SYN 连接请求报文段和一个随机序号,服务端接收到请求后向服务器端发送一个 SYN ACK 报文段,确认连接请求,并且也向客户端发送一个随机序号。客户端接收服务器的确认应答后,进入连接建立的状态,同时向服务器也发送一个 ACK 确认报文段,服务器端接收到确认后,也进入连接建立状态,此时双方的连接就建立起来了。
(6)HTTPS 握手:如果使用的是 HTTPS 协议,在通信前还存在 TLS 的一个四次握手的过程。首先由客户端向服务器端发送使用的协议的版本号、一个随机数和可以使用的加密方法。服务器端收到后,确认加密的方法,也向客户端发送一个随机数和自己的数字证书。客户端收到后,首先检查数字证书是否有效,如果有效,则再生成一个随机数,并使用证书中的公钥对随机数加密,然后发送给服务器端,并且还会提供一个前面所有内容的 hash 值供服务器端检验。服务器端接收后,使用自己的私钥对数据解密,同时向客户端发送一个前面所有内容的 hash 值供客户端检验。这个时候双方都有了三个随机数,按照之前所约定的加密方法,使用这三个随机数生成一把秘钥,以后双方通信前,就使用这个秘钥对数据进行加密后再传输。

HTTP

讲的不错 https://www.xiaolincoding.com/network/2_http/http_interview.html

HTTPS 加密原理

see: https://zhuanlan.zhihu.com/p/43789231

准备工作(数字证书认证机构的业务流程)

  1. 服务器的运营人员向第三方机构 CA 提交公钥、组织信息、个人信息(域名)等信息并申请认证。
  2. CA 通过线上、线下等多种手段验证申请者提供信息的真实性。
  3. CA 审核通过后,向申请者签发证书。证书包含了申请者的公钥、申请者的组织信息和个人信息、CA 的信息、有效时间、证书序列号等信息的明文,同时包含一个签名(使用散列函数计算出公开的明文信息的信息摘要,然后通过 CA 的私钥对其加密得到签名)。

请求过程

  1. 客户端发起请求。
  2. 服务端将之前的签发的证书返回给客户端。
  3. 客户端验证该证书:证书是否再有效期内,证书的用途是否匹配客户端请求站点,是否被吊销、上一级证书是否有效…。
  4. 客户端使用 CA 提前植入到浏览器的公钥,解密获得信息摘要。然后用 HASH 函数对原文再重新生成一个摘要信息,与解密得到的信息进行对比,如果相同则该信息没有被修改过。
  5. 客户端使用伪随机数生成器生成对称密钥,然后用服务端的公钥对其加密发送给服务端。
  6. 服务端通过私钥解密获得对称密钥,使用该密钥对内容进行加密。
  7. 客户端使用密钥对内容进行解密。

HTTP 各版本差异

HTTP 各版本

HTTP 1.0

短链接,每次请求都会建立一个 TCP 连接,服务器处理完之后就会断开连接。

HTTP 1.1

改进之处(较于 1.0):

  • 默认使用长连接,可以减少建立和断开连接的次数。
  • 支持管道网络传输,只要第一个请求发出去了,不必等其回来,就可以发送第二个请求。

不足之处:

  • 请求和响应头未经过压缩,首部信息越多延迟越大。
  • 发送冗长的首部,每次发送相同首部浪费较多。
  • 服务器是按请求的顺序来响应的,无法并行处理请求(队头阻塞)。
  • 没有请求优先级控制。
  • 请求只能从客户端开始,服务器不能主动推送数据。

HTTP 2.0

改进之处(较于 1.1):

  • 头部压缩(HPACK 算法,客户端和服务器同时维护一张头信息表,每个字段都存入该表生成索引号,后续请求只发送索引号)
  • 二进制格式(头信息帧和数据帧)
  • 并发传输(Stream 传输,多个 Stream 复用一个 TCP 连接,针对不同的 HTTP 请求用独一无二的 Stream ID 区分,接收端根据 Stream ID 有序组装成 HTTP 消息)
  • 服务器推送

不足之处:

  • 队头阻塞问题依然存在

    HTTP/2 通过 Stream 的并发能力,解决了 HTTP/1 队头阻塞的问题,但只是解决了 HTTP 这一层面,而 TCP 这层人仍然存在问题。HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当「前 1 个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据,这就是 HTTP/2 队头阻塞问题。一旦发生了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来。

HTTP 3.0

改进之处(较于 2.0):

  • 使用 UDP 替代 TCP,解决队头阻塞问题
  • QUIC 协议保证更快的连接
  • 连接迁移,连接 ID 复用,避免切换问题时卡顿的问题。

QUIC 协议

  • 无队头阻塞。QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,某个流发生丢包了,只会影响该流,其他流不受影响。
  • 更快的连接建立。QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 里的 “记录”,再加上 QUIC 使用的是 TLS/1.3,因此仅需 1 个 RTT 就可以「同时」完成建立连接与密钥协商。
  • 连接迁移

    基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接。当移动设备的网络从 4G 切换到 WIFI 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立连接。而建立连接的过程包含 TCP 三次握手和 TLS 四次握手的时延,以及 TCP 慢启动的减速过程,给用户的感觉就是网络突然卡顿了一下,因此连接的迁移成本是很高的。而 QUIC 协议没有用四元组的方式来“绑定”连接,而是通过连接 ID 来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本,没有丝毫卡顿感,达到了连接迁移的功能。

TLS 握手各版本差异

参考:https://juejin.cn/post/6895624327896432654

首屏性能优化

首屏时间计算方式

// 方案一:
document.addEventListener('DOMContentLoaded', (event) => {
    console.log('first contentful painting');
});
// 方案二:
performance.getEntriesByName("first-contentful-paint")[0].startTime

// performance.getEntriesByName("first-contentful-paint")[0]
// 会返回一个 PerformancePaintTiming的实例,结构如下:
{
  name: "first-contentful-paint",
  entryType: "paint",
  startTime: 507.80000002123415,
  duratio
}
  1. 资源加载优化
    • 减少资源大小:代码压缩、Gzip、图片压缩、代码拆分
    • 减少 HTTP 请求数:Http 强缓存、Service Worker、本地存储、合并请求(nginx-http-concat、雪碧图)
    • 提高 HTTP 请求响应速度:CDN、http 弱缓存、DNS Prefetch、http2
    • 优化资源加载时机:按需加载、懒加载、预加载(preload)
    • 优化资源、内容加载方式:客户端 H5 考虑离线包、内容直出、SSR
  2. 页面渲染优化
    • 优化 html:js 外链放在底部、css 外链放在顶部、减少 DOM 数量
    • 优化 js、css:webworker、长任务分片执行、减少重排重绘、降低 css 选择器复杂性
    • 优化动画效果:使用 requestAnimationFrame、使用 transform 和 opacity 实现动画、使用 will-change 或者 translateZ 来提升某些元素到新的合成层。

页面渲染加载速度慢的原因

页面渲染的过程,导致加载速度慢的因素可能如下:

  • 网络延时问题
  • 资源文件体积是否过大
  • 资源是否重复发送请求去加载了
  • 加载脚本的时候,渲染内容堵塞了

正向代理(Forward Proxy)和 反向代理(Reverse Proxy)

正向代理是位于客户端和目标服务器之间的代理服务器。 当客户端发起请求时,请求先发送给正向代理服务器,然后由代理服务器代表客户端向目标服务器发送请求。 客户端不直接与目标服务器通信,而是通过正向代理服务器进行通信,目标服务器不知道请求的真实来源。 正向代理常用于绕过网络限制、访问被封锁的内容、提供匿名性等。例如,在某些国家或组织中,正向代理被用于访问被屏蔽的网站。

反向代理是位于目标服务器和客户端之间的代理服务器。 当客户端发起请求时,请求先发送到反向代理服务器,然后由代理服务器转发请求给后端的目标服务器。 目标服务器不直接与客户端通信,而是通过反向代理服务器进行通信,客户端不知道请求的真实目标。 反向代理常用于负载均衡、缓存、安全性等方面。例如,用于将请求分发到多台服务器,隐藏后端服务器的真实 IP 地址,提供缓存以加快响应速度等。

总结: 正向代理是客户端代理,代理客户端的请求;而反向代理是服务端代理,代理服务端的响应。正向代理隐藏客户端身份,用于访问被封锁的内容,而反向代理隐藏服务端身份,提供负载均衡和安全性等功能。

TCP 粘包问题

TCP 粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾。

TCP 粘包问题原因:

TCP 是一个面向「流」的协议,所谓流就是没有界限的一长串二进制数据。TCP 作为传输层协议并不了解上层业务数据的具体含义,它会根据 TCP 缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被 TCP 拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就会出现粘包拆包的问题。

UDP 有消息保护边界,不会发生粘包拆包问题

例如,TCP 缓冲区是 1024 个字节大小,如果应用一次请求发送的数据量比较小,没达到缓冲区大小,TCP 则会将多个请求合并为同一个请求进行发送,站在业务上来看这就是「粘包」;

如果应用一次请求发送的数据量比较大,超过了缓冲区大小,TCP 就会将其拆分为多次发送,这就是「拆包」,也就是将一个大的包拆分为多个小包进行发送。

TCP 粘包问题的解决:

  • 发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
  • 发送端将每个数据包封装为固定长度(不够的可以通过补其他字符填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
  • 可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。

Etag 生成规则

ETag(实体标签)是 HTTP 协议中用于标识特定版本资源的机制,由服务器生成并返回给客户端,帮助缓存和验证资源的状态。ETags 可以是强 ETags 或弱 ETags,两者在生成和使用上有一些不同。

1. 强 ETag

强 ETag 是精确表示资源状态的标识符,通常用于要求严格匹配的场景。

生成方式可以是基于资源内容的哈希值(如 MD5 或 SHA-1),文件的最后修改时间戳,或其他唯一标识符。服务器会在响应头中返回该 ETag,格式如:ETag: “abcd1234”。

2. 弱 ETag

弱 ETag 用于表示资源的语义上相同但可能有微小变化的版本。前缀为 W/,例如:ETag: W/”abcd1234”。

生成方式可能依赖于资源的某些属性,如内容的摘要加上 W/前缀,或基于文件的版本号等。弱 ETag 允许服务器在资源内容发生非本质变化(如格式化变动)时,仍可视作同一版本。

匹配严格性:

  • 强 ETag 要求字节级的完全匹配,因此任何改变都会导致 ETag 的变化。
  • 弱 ETag 允许一些程度的差异,适用于对内容微小变化不敏感的场合。

用途不同:

  • 强 ETag 适合于缓存严格控制和精确匹配的场合,如静态文件的缓存管理。
  • 弱 ETag 适合于动态内容或允许轻微变动的资源,减少不必要的重新下载。

不同服务器对 ETag 的生成规则可能不同,需要具体问题具体分析。

  1. nginx 中 etag 由响应头的 Last-Modified 与 Content-Length 表示为十六进制组合而成。
  2. Apache 的 ETag 默认是基于三个文件属性生成的:inode(文件节点)、mtime(最后修改时间)和 size(文件大小)。