HTTP 协议
HTTP 协议基础个人总结
# 什么是 HTTP 协议
HTTP 协议是由客户端到服务器请求和从服务端到客户端的响应进行约束和规范的协议。这是一个应用层的协议,依赖于 TCP 协议
注意
HTTPS 基于 HTTP,嵌入 TLS、SSL 做安全校验。但本质上是两个协议。
# 浏览器行为与 HTTP 协议
问:
当我们从浏览器地址栏中输入网址到页面可交互的过程中发生了什么?
这是一道典型的考基础也考知识面的问题。粗略地概括,有以下步骤:
- 用户输入网址并回车
- DNS 解析
- 浏览器发送 HTTP 请求
- 服务器处理请求
- 服务器返回 HTML 响应
- 浏览器处理 HTML
- 继续请求其他资源
# DNS 解析
域名是供人使用的,但计算机只识别 IP,当输入一个网址后浏览器首先需要检查本地的防火墙,看当前的网络是否是与互联网互通的,如果无障碍,继续通过域名查询 IP 地址,这一步称为 DNS 解析,下面会详细阐述。客户端能获取到目标 IP 后,尝试与服务端建立 TCP 链接,后续的 HTTP 请求即可发送至正确的服务器。
# 查找浏览器缓存
浏览器首先检查本地缓存中是否有该域名与对应 IP 的解析记录,如果检查到,则解析过程结束,向目标 IP 请求资源。
缓存时间各个浏览器可能都不同,以 Chrome 为例,缓存时间大约为 1 分钟,这个时间内访问相同的域名不会重新请求 DNS。
# 查找系统缓存
如果上一步浏览器查找缓存失败,则继续向操作系统的缓存中查找。可以通过配置 host 的方式来让浏览器首先使用你配置的映射关系作为系统的 DNS 的本地缓存,平时开发时将测试服务器的 IP 映射到一个常用的 dev 域名是较为常用的做法。
# 查找路由缓存
如果系统缓存中仍查找不到记录,查询请求会发向路由器。
# 查找 ISP 缓存
如果连路由器都没有对应的记录,那只能向 ISP(Internet Service Provider,互联网服务提供商,比如一级运营商电信联动这些)来索取 IP。操作系统会向系统指定的 DNS IP 地址发送查询指令,这个专门供查询用的域名解析服务器(术语为 Recursive Server)性能一般都会很好。有数据统计,80% 以上的 DNS 解析在这一步甚至之前都已完成。
# 递归搜索
假设上面的查询均不能获取到结果,ISP 的服务器会递归地向其授权服务器(Authoritative Server)发送请求。以 unbrella.cisco.com
为例(下图来自于 unbrella.cisco.com):
- Recursive Server 找到授权服务器的根(Root)服务器
- 让根服务器查询被查询域名的顶级域名(这个例子中顶级域名为 com),将顶级域名的 IP 返回至 Recursive Server
- Recursive Server 向顶级域名 com 查询二级域名 cisco.com,返回
- 向二级域名查询 umbrella.cisco.com
- ... // TODO: 如果还有更多级的话
Recursive Server 不断递归直至查询到最终的结果,然后返回到主机。
# 服务器处理请求
一个 IP 地址实际上可能对应多个服务器,通过反向代理(如 Nginx)使多个服务器协同处理同一个 IP 下的请求。 服务器接受到请求内容后作相应的响应并返回,可能是 HTML / JS / CSS 等静态资源,也可能是一个客户端发起的 AJAX 请求的响应,但第一次一般都是 HTML。
# HTTP 1.1 vs HTTP 1.0
HTTP 1.1 相较于 HTTP 1.0 主要做了以下方面的改进:
长连接。HTTP 1.1 增加了长连接(Persistent Connection)及请求流水线(Pipelining)的支持,在一个 TCP 连接中可以传送多个 HTTP 请求和响应,而非 HTTP 1.0 响应完毕后直接关闭,减少了 TCP 连接建立和关闭的多次握手带来的延迟和资源开销。后续版本中默认开启长连接,即
Connection: keep-alive
。缓存处理。在 HTTP 1.0 中主要使用 header 里的
If-Modified-Since
、Expires
来做为缓存判断的标准,HTTP 1.1 则引入了更多的缓存控制策略例如ETag
、If-Unmodified-Since
、If-Match
、If-None-Match
等更多可供选择的缓存头来控制缓存策略。带宽优化及网络连接的使用。HTTP 1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP 1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
错误通知的管理。在 HTTP 1.1 中新增了 24 个错误状态响应码,如:409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
Host 头处理。在 HTTP 1.0 中认为每台服务器都绑定一个唯一的 IP 地址,因此,请求消息中的 URL 并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个 IP 地址。HTTP 1.1 的请求消息和响应消息都应支持 Host 头域,且请求消息中如果没有Host 头域会报告一个错误(400 Bad Request)。
# HTTP 1.x 的队首阻塞问题
对于同一个 TCP 连接,HTTP 1.1 允许一次发送多个请求,也就是说,不必等前一个响应收到,就可以发送下一个请求,这样就解决了 HTTP 1.0 的客户端的队首阻塞。但是,HTTP 1.1 规定,服务器端的响应的发送要根据请求被接收的顺序排队,也就是说,先接收到的请求的响应也要先发送。这样造成的问题是,如果最先收到的请求的处理时间长的话,响应生成也慢,就会阻塞已经生成了的响应的发送,也会造成队首阻塞,这是服务端的队首阻塞。
无论是客户端还是服务端的队首阻塞,都导致带宽无法被充分利用,客户端迟迟收不到响应。
# HTTP 缓存
# Cookies 与 Session
Cookies 是保存在客户端的小段文本,在服务端返回的响应头中若携带 set-cookie,后续的每一次请求浏览器都会自动携带所有 Cookies 到服务端。 Session 由服务端维护,通过唯一的 SessionID 来唯一标识用户。Session 可能维护在内存中,也可能持久化于数据库中。 HTTP 是一种无状态协议,Cookies 与 Session 都是为了让服务端区分请求是否来自同一浏览器(用户)。