极客时间《透视HTTP协议》总结笔记

999 阅读34分钟

破冰篇

01 | 时势与英雄:HTTP的前世今生

  • HTTP1.0
    • 确立了大部分现在使用的技术
  1. 增加了 HEAD、POST 等新方法;
  2. 增加了响应状态码,标记可能的错误原因;
  3. 引入了协议版本号概念;
  4. 引入了 HTTP Header(头部)的概念,让 HTTP 处理请求和响应更加灵活;
  5. 传输的数据不再仅限于文本。
  • HTTP1.1
    • 互联网上使用最广泛的协议
  1. 增加了 PUT、DELETE 等新的方法;
  2. 增加了缓存管理和控制;
  3. 明确了连接管理,允许持久连接;
  4. 允许响应数据分块(chunked),利于传输大文件;
  5. 强制要求 Host 头,让互联网主机托管成为可能。
  • HTTP2.0
    • 基于 Google 的 SPDY 协议
  1. 二进制协议,不再是纯文本;
  2. 可发起多个请求,废弃了 1.1 里的管道;
  3. 使用专用算法压缩头部,减少数据传输量;
  4. 允许服务器主动向客户端推送数据;
  5. 增强了安全性,“事实上”要求加密通信。
  • HTTP3.0
    • 基于 Google 的 QUIC 协议
  1. QUIC协议

02 | HTTP是什么?HTTP又不是什么?

  1. HTTP 专门用来在两点之间传输数据,不能用于广播、寻址或路由。
  2. HTTP 传输的是文字、图片、音频、视频等超文本数据。

03 | HTTP世界全览(上):与HTTP相关的各种概念

download.png

  • CDN
  1. 全称是“Content Delivery Network”,翻译过来就是“内容分发网络”。它应用了 HTTP 协议里的缓存和代理技术,代替源站响应客户端的请求。
  2. 提供负载均衡、安全防护、边缘计算、跨运营商网络等功能
  3. 实际上就是一种代理,它代替源站服务器响应客户端的请求,通常扮演着透明代理和反向代理的角色
  • 爬虫
  1. 可以自动访问 Web 资源的应用程序。
  2. 绝大多数是由各大搜索引擎“放”出来的,抓取网页存入数据库,再建立关键字索引,这样才能够在搜索引擎中搜索到互联网的页面。

04 | HTTP世界全览(下):与HTTP相关的各种协议

download.png

  • TCP/IP
  1. TCP/IP 是网络世界最常用的协议,HTTP 通常运行在 TCP/IP 提供的可靠传输基础上;
  2. TCP/IP 协议实际上是一系列网络通信协议的统称,其中最核心的两个协议是TCPIP,其他的还有 UDP、ICMP、ARP 等
  3. ip协议v4版,地址是四个用“.”分隔的数字,例如“192.168.0.1”
  4. ip协议v6版,使用 8 组“:”分隔的数字作为地址
  • DNS
  1. DNS 域名是 IP 地址的等价替代,需要用域名解析实现到 IP 地址的映射;
  2. 域名用“.”分隔成多个单词,级别从左到右逐级升高,最右边的被称为“顶级域名”
  • URI/URL
  1. URI 是用来标记互联网上资源的一个名字,由“协议名 + 主机名 + 路径”构成,俗称 URL;
  2. 协议名:即访问该资源应当使用的协议,在这里是“http”
  3. 主机名:即互联网上主机的标记,可以是域名或 IP 地址,在这里是“nginx.org”
  4. 路径:即资源在主机上的位置,使用“/”分隔多级目录,在这里是“/en/download.html”
  • HTTPS
  1. HTTPS 相当于“HTTP+SSL/TLS+TCP/IP”,为 HTTP 套了一个安全的外壳;
  2. SSL 使用了许多密码学最先进的研究成果,综合了对称加密、非对称加密、摘要算法、数字签名、数字证书等技术,能够在不安全的环境中为通信的双方创建出一个秘密的、安全的传输通道
  • 代理
  1. 代理是 HTTP 传输过程中的“中转站”,可以实现缓存加速、负载均衡等功能。
  2. 匿名代理:完全“隐匿”了被代理的机器,外界看到的只是代理服务器;
  3. 透明代理:顾名思义,它在传输过程中是“透明开放”的,外界既知道代理,也知道客户端;
  4. 正向代理:靠近客户端,代表客户端向服务器发送请求;
  5. 反向代理:靠近服务器端,代表服务器响应客户端的请求;
    • 代理作用
    1. 负载均衡:把访问请求均匀分散到多台机器,实现访问集群化;
    2. 内容缓存:暂存上下行的数据,减轻后端的压力;
    3. 安全防护:隐匿 IP, 使用 WAF 等工具抵御网络攻击,保护被代理的机器;
    4. 数据处理:提供压缩、加密等额外的功能

05 | 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?

  • TCP的数据是连续的“字节流”,有先后顺序,而UDP则是分散的小数据包,是顺序发,乱序收。 download.png
  1. 第一层:物理层,TCP/IP 里无对应;
  2. 第二层:数据链路层,对应 TCP/IP 的链接层;
  3. 第三层:网络层,对应 TCP/IP 的网际层;
  4. 第四层:传输层,对应 TCP/IP 的传输层;
  5. 第五、六、七层:统一对应到 TCP/IP 的应用层。

06 | 域名里有哪些门道?

download.png

  1. 看一下极客时间的域名“time.geekbang.org”,这里的“org”就是顶级域名,“geekbang”是二级域名,“time”则是主机名。
  2. 根域名服务器(Root DNS Server):管理顶级域名服务器,返回“com”“net”“cn”等顶级域名服务器的 IP 地址;
  3. 顶级域名服务器(Top-level DNS Server):管理各自域名下的权威域名服务器,比如 com 顶级域名服务器可以返回 apple.com 域名服务器的 IP 地址;
  4. 权威域名服务器(Authoritative DNS Server):管理自己域名下主机的 IP 地址,比如 apple.com 权威域名服务器可以返回 www.apple.com 的 IP 地址。
  • 域名的新玩法
  1. 因为域名代替了 IP 地址,所以可以让对外服务的域名不变,而主机的 IP 地址任意变动。当主机有情况需要下线、迁移时,可以更改 DNS 记录,让域名指向其他的机器。
  2. 因为域名是一个名字空间,所以可以使用 bind9 等开源软件搭建一个在内部使用的 DNS,作为名字服务器。这样我们开发的各种内部服务就都用域名来标记,比如数据库服务都用域名“mysql.inner.app”,商品服务都用“goods.inner.app”,发起网络通信时也就不必再使用写死的 IP 地址了,可以直接用域名
  3. 域名解析可以返回多个 IP 地址,所以一个域名可以对应多台主机,客户端收到多个 IP 地址后,就可以自己使用轮询算法依次向服务器发起请求,实现负载均衡。
  4. 域名解析可以配置内部的策略,返回离客户端最近的主机,或者返回当前服务质量最好的主机,这样在 DNS 端把请求分发到不同的服务器,实现负载均衡。

07 | 自己动手,搭建HTTP实验环境

基础篇

08 | 键入网址再按下回车,后面究竟发生了什么?

download.png

  1. 使用“三次握手”建立与 Web 服务器的连接,经过 SYN、SYN/ACK、ACK 的三个包之后,浏览器与服务器的 TCP 连接就建立起来了。
  2. 浏览器按照 HTTP 协议规定的格式,通过 TCP 发送了一个“GET / HTTP/1.1”请求报文,也就是 Wireshark 里的第四个包。
  3. Web服务器回复了第五个包,在 TCP 协议层面确认:“刚才的报文我已经收到了”,不过这个 TCP 包 HTTP 协议是看不见的。
  4. 从磁盘上把文件读出来,拼成符合 HTTP 格式的报文。这就是第六个包“HTTP/1.1 200 OK”,底层走的还是 TCP 协议。
  5. 浏览器也要给服务器回复一个 TCP 的 ACK 确认,“你的响应报文收到了,多谢。”,即第七个包。
  6. 之后还有两个来回,共四个包,浏览器自动请求了作为网站图标的“favicon.ico”文件,服务器在硬盘上找不到,返回了一个“404 Not Found”。 download.png
  7. 浏览器从地址栏的输入中获得服务器的 IP 地址和端口号;
  8. 浏览器用 TCP 的三次握手与服务器建立连接;
  9. 浏览器向服务器发送拼好的报文;
  10. 服务器收到报文后处理请求,同样拼好报文再发给浏览器;
  11. 浏览器解析报文,渲染输出页面。
  • dns缓存
  1. 在域名解析的过程中会有多级的缓存,浏览器首先看一下自己的缓存里有没有,如果没有就向操作系统的缓存要,还没有就检查本机域名解析文件 hosts,也就是上一讲中我们修改的“C:\WINDOWS\system32\drivers\etc\hosts”。
  • 真实的网络世界
  1. 用 DNS 协议开始从操作系统、本地 DNS、根 DNS、顶级 DNS、权威 DNS 的层层解析,当然这中间有缓存,可能不会费太多时间就能拿到结果。
  2. DNS 解析可能会给出 CDN 服务器的 IP 地址,这样你拿到的就会是 CDN 服务器而不是目标网站的实际地址。
  3. CDN 会缓存网站的大部分资源,比如图片、CSS 样式表,所以有的 HTTP 请求就不需要再发到 Apple,CDN 就可以直接响应你的请求,把数据发给你。
  4. 动态资源”,CDN 无法缓存,只能从目标网站获取,服务器对外表现的是一个 IP 地址,但为了能够扛住高并发,在内部也是一套复杂的架构。通常在入口是负载均衡设备
  5. 负载均衡设备会先访问系统里的缓存服务器,如果缓存服务器里也没有,那么负载均衡设备就要把请求转发给应用服务器了。
  6. 它们又会再访问后面的 MySQL、PostgreSQL、MongoDB 等数据库服务,然后把执行的结果返回给负载均衡设备,同时也可能给缓存服务器里也放一份。
  7. 应用服务器的输出到了负载均衡设备这里,请求的处理就算是完成了,就要按照原路再走回去,经过 CDN 的时候它也会做缓存,这样下次同样的请求就不会到达源站了。

09 | HTTP报文是什么样子的?

download.png

  • HTTP 协议的请求报文和响应报文的结构
  1. 起始行(start line):描述请求或响应的基本信息;
  2. 头部字段集合(header):使用 key-value 形式更详细地说明报文;
  3. 消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。
  4. 前两部分起始行和头部字段经常又合称为“请求头”或“响应头”,消息正文又称为“body”。 download.png
  • 请求行
  1. 请求方法:是一个动词,如 GET/POST,表示对资源的操作;
  2. 请求目标:通常是一个 URI,标记了请求方法要操作的资源;
  3. 版本号:表示报文使用的 HTTP 协议版本。
  • GET / HTTP/1.1 download.png
  • 状态行
  1. 版本号:表示报文使用的 HTTP 协议版本;
  2. 状态码:一个三位数,用代码的形式表示处理的结果,比如 200 是成功,500 是服务器错误;
  3. 原因:作为数字状态码补充,是更详细的解释文字,帮助人理解原因。
  • HTTP/1.1 200 OK download.png
  • 头部字段
  1. 头部字段是 key-value 的形式,key 和 value 之间用“:”分隔,最后用 CRLF 换行表示字段结束。 download.png
  • 常用头字段
  1. Host字段要求必须出现

10 | 应该如何理解请求方法?

  1. GET:获取资源,可以理解为读取或者下载数据;
  2. HEAD:获取资源的元信息;
  3. POST:向资源提交数据,相当于写入或上传数据,更像新增;
  4. PUT:类似 POST,更像更新。
  5. DELETE:删除资源;
  6. CONNECT:建立特殊的连接隧道;
  7. OPTIONS:列出可对资源实行的方法;
  8. TRACE:追踪请求 - 响应的传输路径。
  • 扩展方法
  1. 我们可以任意添加请求动作,只要请求方和响应方都能理解就行。

11 | 你能写出正确的网址吗?

  • URI 的格式 download.png
  1. scheme 叫“方案名”或者“协议名”,表示资源应该使用哪种协议来访问;
  2. “host:port”表示资源所在的主机名和端口号;
  3. path 标记资源所在的位置;
  4. query 表示对资源附加的额外要求;
  • URI 的编码
  1. URI 转义的规则有点“简单粗暴”,直接把非 ASCII 码或特殊字符转换成十六进制字节值,然后前面再加上一个“%”

12 | 响应状态码该怎么用?

  1. 1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
  2. 2××:成功,报文已经收到并被正确处理;
  3. 3××:重定向,资源位置发生变动,需要客户端重新发送请求;
  4. 4××:客户端错误,请求报文有误,服务器无法处理;
  5. 5××:服务器错误,服务器在处理请求时内部发生了错误。
  • 2xx
  1. 200 OK”是最常见的成功状态码,表示一切正常,服务器如客户端所期望的那样返回了处理结果,如果是非 HEAD 请求,通常在响应头后都会有 body 数据。
  2. 204 No Content”是另一个很常见的成功状态码,它的含义与“200 OK”基本相同,但响应头后没有 body 数据。所以对于 Web 服务器来说,正确地区分 200 和 204 是很必要的。
  3. 206 Partial Content”是 HTTP 分块下载或断点续传的基础,在客户端发送“范围请求”、要求获取资源的部分数据时出现,它与 200 一样,也是服务器成功处理了请求,但 body 里的数据不是资源的全部,而是其中的一部分。
    • 状态码 206 通常还会伴随着头字段“Content-Range”,表示响应报文里 body 数据的具体范围,供客户端确认。
  • 3××
  1. 301 Moved Permanently”俗称“永久重定向”,含义是此次请求的资源已经不存在了,需要改用改用新的 URI 再次访问。
  2. 302 Found”,曾经的描述短语是“Moved Temporarily”,俗称“临时重定向”,意思是请求的资源还在,但需要暂时用另一个 URI 来访问。
    • 301 和 302 都会在响应头里使用字段Location指明后续要跳转的 URI,最终的效果很相似,浏览器都会重定向到新的 URI。
  3. 304 Not Modified” 是一个比较有意思的状态码,它用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。
  • 4××
  1. 400 Bad Request”是一个通用的错误码,表示请求报文有错误
  2. 403 Forbidden”实际上不是客户端的请求出错,而是表示服务器禁止访问资源。
    • 如果服务器友好一点,可以在 body 里详细说明拒绝请求的原因
  3. 404 Not Found”可能是我们最常看见也是最不愿意看到的一个状态码,它的原意是资源在本服务器上未找到。
  4. 405 Method Not Allowed:不允许使用某些方法操作资源,例如不允许 POST 只能 GET;
  5. 406 Not Acceptable:资源无法满足客户端请求的条件,例如请求中文但只有英文;
  6. 408 Request Timeout:请求超时,服务器等待了过长的时间;
  7. 409 Conflict:多个请求发生了冲突,可以理解为多线程并发时的竞态;
  8. 413 Request Entity Too Large:请求报文里的 body 太大;
  9. 414 Request-URI Too Long:请求行里的 URI 太大;
  10. 429 Too Many Requests:客户端发送了太多的请求,通常是由于服务器的限连策略;
  11. 431 Request Header Fields Too Large:请求头某个字段或总体太大;
  • 5××
  1. 500 Internal Server Error”与 400 类似,也是一个通用的错误码,服务器究竟发生了什么错误我们是不知道的
  2. 501 Not Implemented”表示客户端请求的功能还不支持
  3. 502 Bad Gateway”通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误
  4. 503 Service Unavailable”表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503。

13 | HTTP有哪些特点?

  1. HTTP 是灵活可扩展的,可以任意添加头字段实现任意功能;
  2. HTTP 是可靠传输协议,基于 TCP/IP 协议“尽量”保证数据的送达;
  3. HTTP 是应用层协议,比 FTP、SSH 等更通用功能更多,能够传输任意数据;
  4. HTTP 使用了请求 - 应答模式,客户端主动发起请求,服务器被动回复请求;
  5. HTTP 本质上是无状态的,每个请求都是互相独立、毫无关联的,协议不要求客户端或服务器记录请求相关的信息。

14 | HTTP有哪些优点?又有哪些缺点?

  1. HTTP 最大的优点是简单、灵活和易于扩展;
  2. HTTP 拥有成熟的软硬件环境,应用的非常广泛,是互联网的基础设施;
  3. HTTP 是无状态的,可以轻松实现集群化,扩展性能,但有时也需要用 Cookie 技术来实现“有状态”;
    • Cookie 技术
  4. HTTP 是明文传输,数据完全肉眼可见,能够方便地研究分析,但也容易被窃听;
  5. HTTP 是不安全的,无法验证通信双方的身份,也不能判断报文是否被窜改;
    • 为了解决 HTTP 不安全的缺点,所以就出现了 HTTPS
  6. HTTP 的性能不算差,但不完全适应现在的互联网,还有很大的提升空间。
    • “队头阻塞”(Head-of-line blocking),当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据。

进阶篇

15 | 海纳百川:HTTP的实体数据

  • 支持的格式
  1. 客户端:Accept: text/html,application/xml,image/webp,image/png
  2. 服务端:Content-Type: text/html
  • 压缩格式
  1. Accept-Encoding: gzip, deflate, br
  2. Content-Encoding: gzip
  • 语言
  1. Accept-Language: zh-CN, zh, en
  2. Content-Language: zh-CN
  • 字符集
  1. Accept-Charset: gbk, utf-8
  2. Content-Type: text/html; charset=utf-8
  • 权重
  1. Accept: text/html,application/xml;q=0.9,/;q=0.8
    • 浏览器最希望使用的是 HTML 文件,权重是 1,其次是 XML 文件,权重是 0.9,最后是任意数据类型,权重是 0.8。

image.png

  1. 数据类型表示实体数据的内容是什么,使用的是 MIME type,相关的头字段是 Accept 和 Content-Type;
  2. 数据编码表示实体数据的压缩方式,相关的头字段是 Accept-Encoding 和 Content-Encoding;
  3. 语言类型表示实体数据的自然语言,相关的头字段是 Accept-Language 和 Content-Language;
  4. 字符集表示实体数据的编码方式,相关的头字段是 Accept-Charset 和 Content-Type;
  5. 客户端需要在请求头里使用 Accept 等头字段与服务器进行“内容协商”,要求服务器返回最合适的数据;
  6. Accept 等头字段可以用“,”顺序列出多个可能的选项,还可以用“;q=”参数来精确指定权重。

16 | 把大象装进冰箱:HTTP传输大文件的方法

  1. 压缩 HTML 等文本文件是传输大文件最基本的方法;
  2. 分块传输可以流式收发数据,节约内存和带宽,使用响应头字段“Transfer-Encoding: chunked”来表示,分块的格式是 16 进制长度头 + 数据块;
  3. 范围请求可以只获取部分数据,即“分块请求”,实现视频拖拽或者断点续传,使用请求头字段“Range”和响应头字段“Content-Range”,响应状态码必须是 206;
  4. 也可以一次请求多个范围,这时候响应报文的数据类型是“multipart/byteranges”,body 里的多个部分会用 boundary 字符串分隔

17 | 排队也要讲效率:HTTP的连接管理

  • HTTP 0.9/1.0 短连接 image.png
  • HTTP 1.1 长连接 image.png
  • 同样发送三次请求,因为只在第一次时建立连接,在最后一次时关闭连接
  • 如果服务器支持长连接,它总会在响应报文里放一个“Connection: keep-alive”字段
  • 关闭策略
    1. 在请求头里加上“Connection: close”字段,服务器看到这个字段,就知道客户端要主动关闭连接,于是在响应报文里也加上这个字段,发送之后就调用 Socket API 关闭 TCP 连接。
    2. 服务端设置长连接的超时时间,如果在一段时间内连接上没有任何数据收发就主动断开连接,避免空闲连接占用系统资源。
    3. 服务端设置长连接上可发送的最大请求次数。比如设置成 1000,那么当 Nginx 在这个连接上处理了 1000 个请求后,也会主动断开连接。
  • “队头阻塞”问题会导致性能下降,可以用“并发连接”和“域名分片”技术缓解。

18 | 四通八达:HTTP的重定向和跳转

  • 浏览器收到 301/302 报文,会检查响应头里有没有“Location”。如果有,就从字段值里提取出 URI,发出新的 HTTP 请求,相当于自动替我们点击了这个链接。
  • 301俗称“永久重定向”(Moved Permanently),意思是原 URI 已经“永久”性地不存在了,今后的所有请求都必须改用新的 URI。
  • 302俗称“临时重定向”(“Moved Temporarily”),意思是原 URI 处于“临时维护”状态,新的 URI 是起“顶包”作用的“临时工”。
    • 例如域名变更、服务器变更、网站改版、系统维护
  • 有的网站都会申请多个名称类似的域名,然后把它们再重定向到主站上。

19 | 让我知道你是谁:HTTP的Cookie机制

image.png

  • Cookie 的有效期可以使用 Expires 和 Max-Age 两个属性来设置。
  1. Expires”俗称“过期时间”,用的是绝对时间点,可以理解为“截止日期”(deadline)。
  2. Max-Age”用的是相对时间,单位是秒,浏览器用收到报文的时间点再加上 Max-Age,就可以得到失效的绝对时间。
  • Cookie 的作用域
  1. Domain”和“Path”指定了 Cookie 所属的域名和路径,浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。
  • Cookie 的安全性
  1. 属性“HttpOnly”会告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问,浏览器的 JS 引擎就会禁用 document.cookie 等一切相关的 API,脚本攻击也就无从谈起了。
  2. 另一个属性“SameSite”可以防范“跨站请求伪造”(XSRF)攻击,设置成“SameSite=Strict”可以严格限定 Cookie 不能随着跳转链接跨站发送,而“SameSite=Lax”则略宽松一点,允许 GET/HEAD 等安全方法,但禁止 POST 跨站发送。
  3. 还有一个属性叫“Secure”,表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。 image.png

20 | 生鲜速递:HTTP的缓存控制

  • 服务器标记资源有效期使用的头字段是“Cache-Control”,里面的值“max-age=30”就是资源的有效时间。
    • max-age 是“生存时间”(又叫“新鲜度”“缓存寿命”,类似 TTL,Time-To-Live),时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻),而不是客户端收到报文的时刻,也就是说包含了在链路传输过程中所有节点所停留的时间。
  • no_store:不允许缓存,用于某些变化非常频繁的数据,例如秒杀页面;
  • no_cache:它的字面含义容易与 no_store 搞混,实际的意思并不是不允许缓存,而是可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本;
  • must-revalidate:又是一个和 no_cache 相似的词,它的意思是如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。 image.png
  • 条件请求
  1. 条件请求一共有 5 个头字段,我们最常用的是“if-Modified-Since”和“If-None-Match”这两个。需要第一次的响应报文预先提供“Last-modified”和“ETag”,然后第二次请求时就可以带上缓存里的原值,验证资源是否是最新的。 image.png
  • 浏览器也可以发送“Cache-Control”字段,使用“max-age=0”或“no_cache”刷新数据
  • 验证资源是否失效需要使用“条件请求”,常用的是“if-Modified-Since”和“If-None-Match”,收到 304 就可以复用缓存里的资源

21 | 良心中间商:HTTP的代理服务

  • 所谓的“代理服务”就是指服务本身不生产内容,而是处于中间位置转发上下游的请求和响应,具有双重身份。
  • 代理最基本的一个功能是负载均衡。因为在面向客户端时屏蔽了源服务器,客户端看到的只是代理服务器,源服务器究竟有多少台、是哪些 IP 地址都不知道。 image.png
  • 负载均衡算法,轮询、一致性哈希等等,这些算法的目标都是尽量把外部的流量合理地分散到多台源服务器,提高系统的整体资源利用率和性能。
  • 在负载均衡的同时,代理服务还可以执行更多的功能
    • 健康检查:使用“心跳”等机制监控后端服务器,发现有故障就及时“踢出”集群,保证服务高可用;
    • 安全防护:保护被代理的后端服务器,限制 IP 地址或流量,抵御网络攻击和过载;
    • 加密卸载:对外网使用 SSL/TLS 加密通信认证,而在安全的内网不加密,消除加解密成本;
    • 数据过滤:拦截上下行的数据,任意指定策略修改请求或者响应;
    • 内容缓存:暂存、复用服务器响应
  • 代理相关头字段
    • Via 是一个通用字段,请求头或响应头里都可以出现。每当报文经过一个代理节点,代理服务器就会把自身的信息追加到字段的末尾 image.png
  • X-Forwarded-For
    • 每经过一个代理节点就会在字段里追加一个信息。但“Via”追加的是代理主机名(或者域名),而“X-Forwarded-For”追加的是请求方的 IP 地址。
  • X-Real-IP
    • 记录客户端 IP 地址,没有中间的代理信息
  • 整体流程 image.png
  1. 客户端 55061 先用三次握手连接到代理的 80 端口,然后发送 GET 请求;
  2. 代理不直接生产内容,所以就代表客户端,用 55063 端口连接到源服务器,也是三次握手;
  3. 代理成功连接源服务器后,发出了一个 HTTP/1.0 的 GET 请求;
  4. 因为 HTTP/1.0 默认是短连接,所以源服务器发送响应报文后立即用四次挥手关闭连接;
  5. 代理拿到响应报文后再发回给客户端,完成了一次代理服务。

22 | 冷链周转:HTTP的缓存代理

image.png

  • privatepublic
    • “private”表示缓存只能在客户端保存,是用户“私有”的,不能放在代理上与别人共享。而“public”的意思就是缓存完全开放,谁都可以存,谁都可以用。
  • s-maxage
    • 限定在代理上能够存多久
  • no-transform
    • 代理有时候会对缓存下来的数据做一些优化,比如把图片生成 png、webp 等几种格式,方便今后的请求处理,而“no-transform”就会禁止这样做,不许“偷偷摸摸搞小动作”
  • 以下是服务器端缓存控制策略,可以同时控制客户端和代理。 image.png
  • 以下是客户端的缓存控制 image.png
  • max-stalemin-fresh
    • “max-stale”的意思是如果代理上的缓存过期了也可以接受,但不能过期太多,超过 x 秒也会不要。“min-fresh”的意思是缓存必须有效,而且必须在 x 秒后依然有效。

安全篇

23 | TLS又是什么?

  • 如果通信过程具备了四个特性,就可以认为是“安全”的,这四个特性是:机密性、完整性,身份认证和不可否认。

    • 机密性(Secrecy/Confidentiality)是指对数据的“保密”,只能由可信的人访问,对其他人是不可见的“秘密”
    • 完整性(Integrity,也叫一致性)是指数据在传输过程中没有被窜改,不多也不少,“完完整整”地保持着原状
    • 身份认证(Authentication)是指确认对方的真实身份,也就是“证明你真的是你”,保证消息只能发送给可信的人
    • 不可否认(Non-repudiation/Undeniable),也叫不可抵赖,意思是不能否认已经发生过的行为,不能“说话不算数”“耍赖皮”
  • 浏览器和服务器在使用 TLS 建立连接时需要选择一组恰当的加密算法来实现安全通信,这些算法的组合被称为“密码套件”

    • ECDHE-RSA-AES256-GCM-SHA384
    • TLS 的密码套件命名非常规范,格式很固定。基本的形式是“密钥交换算法(机密性) + 签名算法(身份认证) + 对称加密算法(机密性) + 摘要算法(不可否认、完整性)”
    • 握手时使用 ECDHE 算法进行密钥交换,用 RSA 签名和身份认证,握手后的通信使用 AES 对称算法,密钥长度 256 位,分组模式是 GCM,摘要算法 SHA384 用于消息认证和产生随机数。

24 | 固若金汤的根本(上):对称加密与非对称加密

image.png

  1. 对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换,常用的有 AES 和 ChaCha20;
  2. 非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢,常用的有 RSA 和 ECC;
  3. 把对称加密和非对称加密结合起来就得到了“又好又快”的混合加密,也就是 TLS 里使用的加密方式。

25 | 固若金汤的根本(下):数字签名与证书

  • 数字签名
    • 同时实现“身份认证”和“不可否认” image.png
  • 数字证书和 CA image.png
  • 有了这个证书体系,操作系统和浏览器都内置了各大 CA 的根证书,上网的时候只要服务器发过来它的证书,就可以验证证书里的签名,顺着证书链(Certificate Chain)一层层地验证,直到找到根证书,就能够确定证书是可信的,从而里面的公钥也是可信的。
  • CA证书验证过程?

26 | 信任始于握手:TLS1.2连接过程解析

  • TLS 协议的组成
    • 记录协议(Record Protocol)规定了 TLS 收发数据的基本单位:记录(record)。它有点像是 TCP 里的 segment,所有的其他子协议都需要通过记录协议发出。但多个记录数据可以在一个 TCP 包里一次性发出,也并不需要像 TCP 那样返回 ACK。
    • 警报协议(Alert Protocol)的职责是向对方发出警报信息,有点像是 HTTP 协议里的状态码。比如,protocol_version 就是不支持旧版本,bad_certificate 就是证书有问题,收到警报后另一方可以选择继续,也可以立即终止连接。
    • 握手协议(Handshake Protocol)是 TLS 里最复杂的子协议,要比 TCP 的 SYN/ACK 复杂的多,浏览器和服务器会在握手过程中协商 TLS 版本号、随机数、密码套件等信息,然后交换证书和密钥参数,最终双方协商得到会话密钥,用于后续的混合加密系统。
    • 变更密码规范协议(Change Cipher Spec Protocol),它非常简单,就是一个“通知”,告诉对方,后续的数据都将使用加密保护。那么反过来,在它之前,数据都是明文的。 image.png
  • 仔细剖析 TLS 的握手过程 image.png
  • 在 TCP 建立连接之后,浏览器会首先发一个“Client Hello”消息,也就是跟服务器“打招呼”。里面有客户端的版本号、支持的密码套件,还有一个随机数(Client Random) ,用于后续生成会话密钥。
    • 这个的意思就是:“我这边有这些这些信息,你看看哪些是能用的,关键的随机数可得留着。”
  • 服务器收到“Client Hello”后,会返回一个“Server Hello”消息。把版本号对一下,也给出一个随机数(Server Random) ,然后从客户端的列表里选一个作为本次通信使用的密码套件,在这里它选择了“TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”
    • 这个的意思就是:“版本号对上了,可以加密,你的密码套件挺多,我选一个最合适的吧,用椭圆曲线加 RSA、AES、SHA384。我也给你一个随机数,你也得留着。”
  • 服务器为了证明自己的身份,就把证书也发给了客户端(Server Certificate)。
  • 因为服务器选择了 ECDHE 算法,所以它会在证书后发送“Server Key Exchange”消息,里面是椭圆曲线的公钥(Server Params) ,用来实现密钥交换算法,再加上自己的私钥签名认证。
    • 这相当于说:“刚才我选的密码套件有点复杂,所以再给你个算法的参数,和刚才的随机数一样有用,别丢了。为了防止别人冒充,我又盖了个章。”
  • 之后是“Server Hello Done”消息,服务器说:“我的信息就是这些,打招呼完毕。”
  • 这样第一个消息往返就结束了(两个 TCP 包),结果是客户端和服务器通过明文共享了三个信息:Client Random、Server Random 和 Server Params
  • 客户端开始走证书链逐级验证,确认证书的真实性,再用证书公钥验证签名,就确认了服务器的身份:“刚才跟我打招呼的不是骗子,可以接着往下走。”
  • 客户端按照密码套件的要求,也生成一个椭圆曲线的公钥(Client Params) ,用“Client Key Exchange”消息发给服务器。
  • 现在客户端和服务器手里都拿到了密钥交换算法的两个参数(Client Params、Server Params),就用 ECDHE 算法一阵算,算出了一个新的东西,叫“Pre-Master”,其实也是一个随机数。
  • 现在客户端和服务器手里有了三个随机数:Client Random、Server Random 和 Pre-Master。用这三个作为原始材料,就可以生成用于加密会 话的主密钥,叫“Master Secret”。
  • 有了主密钥和派生的会话密钥,握手就快结束了。客户端发一个“Change Cipher Spec”,然后再发一个“Finished”消息,把之前所有发送的数据做个摘要,再加密一下,让服务器做个验证。
    • 意思就是告诉服务器:“后面都改用对称算法加密通信了啊,用的就是打招呼时说的 AES,加密对不对还得你测一下。”
  • 服务器也是同样的操作,发“Change Cipher Spec”和“Finished”消息,双方都验证加密解密 OK,握手正式结束,后面就收发被加密的 HTTP 请求和响应了。

  • 传统的握手
    • 第一个,使用 ECDHE 实现密钥交换,而不是 RSA,所以会在服务器端发出“Server Key Exchange”消息。
    • 第二个,因为使用了 ECDHE,客户端可以不用等到服务器发回“Finished”确认握手完毕,立即就发出 HTTP 报文,省去了一个消息往返的时间浪费。这个叫“TLS False Start”,意思就是“抢跑”,和“TCP Fast Open”有点像,都是不等连接完全建立就提前发应用数据,提高传输的效率。 image.png
    • 大体的流程没有变,只是“Pre-Master”不再需要用算法生成,而是客户端直接生成随机数,然后用服务器的公钥加密,通过“Client Key Exchange”消息发给服务器。服务器再用私钥解密,这样双方也实现了共享三个随机数,就可以生成主密钥。

27 | 更好更快的握手:TLS1.3特性解析

  • 最大化兼容性
    • 在记录头的 Version 字段被兼容性“固定”的情况下,只要是 TLS1.3 协议,握手的“Hello”消息后面就必须有“supported_versions”扩展,它标记了 TLS 的版本号,使用它就能区分新旧协议。
Handshake Protocol: Client Hello
    Version: TLS 1.2 (0x0303)
    Extension: supported_versions (len=11)
        Supported Version: TLS 1.3 (0x0304)
        Supported Version: TLS 1.2 (0x0303)
  • 强化安全
    • 算法精简后带来了一个意料之中的好处:原来众多的算法、参数组合导致密码套件非常复杂,难以选择,而现在的 TLS1.3 里只有 5 个套件,无论是客户端还是服务器都不会再犯“选择困难症”了。 image.png
  • 提升性能
    • HTTPS 建立连接时除了要做 TCP 握手,还要做 TLS 握手,在 1.2 中会多花两个消息往返(2-RTT),可能导致几十毫秒甚至上百毫秒的延迟,在移动网络中延迟还会更严重。
    • TLS1.3 压缩了以前的“Hello”协商过程,删除了“Key Exchange”消息,把握手时间减少到了“1-RTT”,效率提高了一倍。
    • 其实具体的做法还是利用了扩展。客户端在“Client Hello”消息里直接用“supported_groups”带上支持的曲线,比如 P-256、x25519,用“key_share”带上曲线对应的客户端公钥参数,用“signature_algorithms”带上签名算法。
    • 服务器收到后在这些扩展里选定一个曲线和参数,再用“key_share”扩展返回服务器这边的公钥参数,就实现了双方的密钥交换,后面的流程就和 1.2 基本一样了。 image.png image.png
Handshake Protocol: Client Hello Version: TLS 1.2 (0x0303)                             |
|                                  | Random: cebeb6c05403654d66c2329                      |
|                                  | Cipher Suites (18 suites)                             |
|                                  | Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)       |
|                                  | Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303) |
|                                  | Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)       |
|                                  | Extension: supported_versions (len=9)                 |
|                                  | Supported Version: TLS 1.3 (0x0304)                   |
|                                  | Supported Version: TLS 1.2 (0x0303)                   |
|                                  | Extension: supported_groups (len=14)                  |
|                                  | Supported Groups (6 groups)                           |
|                                  | Supported Group: x25519 (0x001d)                      |
|                                  | Supported Group: secp256r1 (0x0017)                   |
|                                  | Extension: key_share (len=107)                        |
|                                  | Key Share extension                                   |
|                                  | Client Key Share Length: 105                          |
|                                  | Key Share Entry: Group: x25519                        |
|                                  | Key Share Entry: Group: secp256r1
  1. 为了兼容 1.1、1.2 等“老”协议,TLS1.3 会“伪装”成 TLS1.2,新特性在“扩展”里实现;
  2. 1.1、1.2 在实践中发现了很多安全隐患,所以 TLS1.3 大幅度删减了加密算法,只保留了 ECDHE、AES、ChaCha20、SHA-2 等极少数算法,强化了安全;
  3. TLS1.3 也简化了握手过程,完全握手只需要一个消息往返,提升了性能。

28 | 连接太慢该怎么办:HTTPS的优化