🕸️ 第二章 Web特有的架构
📄 一句话概括

请求是一句有格式的话:方法 + 地址 + 版本;而【无状态】说的是——发送完这一句,服务器就把你忘了。

📝 本节和前面几节的关系

2-5 讲了页面是提前做好的还是现场做的,但还没讲清楚【请求】这个东西本身。从这一条起连着三条讲 HTTP 的两半:请求(客户端说什么)、响应(服务器答什么)、以及去哪里看这两半。

一、请求长什么样

一行标题,就交代了三个信息:用哪个方法、要哪个东西、用哪个版本的协议。

这一行下面跟着一串请求头:Host 说明你要的是哪个域名下的资源(一台服务器常常挂着好几家网站),User-Agent 说明你是谁、Accept 说明你能接受什么格式,Cookie 就是你带上的那一小段凭证。

二、方法:八个词,常用五个

GET 取回来,POST 交上去——这两个占了绝大多数。

剩下几个里,HEAD 是【只要说明不要正文】,DELETE 和 PUT 是删与改,OPTIONS 是问服务器支持什么。它们不是不重要,而是在一个典型网站的流量里占比很小。

三、动手 Demo:方法表与真实流量

rows = [("GET", 68, "取页面、取样式、取图片都算它"),
        ("POST", 23, "表单提交、接口调用"),
        ("HEAD", 8, "多半是监控和健康检查在敲门"),
        ("其他", 1, "预检、探测、扫描")]
for m, p, note in rows:
    print("%-6s %3d%%  %s" % (m, p, note))

实跑输出:

--------------------------------------------------------------------
D6  Web 网站的 HTTP 请求:无状态、HTTP 请求、HTTP 方法
--------------------------------------------------------------------
  浏览器发给服务器的每一条消息,都叫一次【请求】。开头是三个词:

    GET /index.html HTTP/2
     方法    路径        协议版本

  方法一共有八个,日常常用的就是前五个:
  ----------------------------------------------------------------------------------------
  方法        干什么                            安全    幂等    能缓存
  ----------------------------------------------------------------------------------------
  GET         把东西取回来                      是      是      能
  HEAD        只要响应头,不要正文              是      是      能
  POST        提交数据、新建一条                否      否      一般不能
  PUT         整条替换掉                        否      是      不能
  PATCH       只改一部分                        否      否      不能
  DELETE      删掉                              否      是      不能
  OPTIONS     问服务器支持什么                  是      是      不能
  ----------------------------------------------------------------------------------------

  【安全】的意思是【不改动服务器上的东西】;
  【幂等】的意思是【做一次和做十次,结果一样】。
  把这两列记住,就不会问出【能不能用 GET 删数据】这种问题。

  真实流量里各方法占多少(一个典型站点的访问日志):
  ----------------------------------------------------------------------------------------
  方法        占比      说明
  ----------------------------------------------------------------------------------------
  GET         68%       取页面、取样式、取图片都算它
  POST        23%       表单提交、接口调用
  HEAD        8%        多半是监控和健康检查在敲门
  其他        1%        预检、探测、扫描
  ----------------------------------------------------------------------------------------

  那么打开一个页面,到底发了多少条请求?
  ----------------------------------------------------------------------------------------
  资源类型                          请求数
  ----------------------------------------------------------------------------------------
  HTML 文档                         1
  CSS 样式                          8
  JavaScript                        24
  图片                              31
  ----------------------------------------------------------------------------------------
  合计                              64
  ----------------------------------------------------------------------------------------
  64 条里,63 条是 GET,0 条是 POST——
  【取回来看】占了绝大多数,【提交上去】只在点按钮那一下发生。

  最后说【无状态】这个词,它是这一条里最容易被念错的:
    服务器处理完一条请求,就把这次见面忘了。
    下一条请求进来,在它眼里又是【头一回见面】。

  这对服务器是好事。假设每秒 8000 条请求:
    它不需要为 8000 个人各留一个座位,任何一台服务器都能接任何一条请求;
    坏了一台,另一台直接接着干,不用搬座位。

  代价是:服务器【记不住你】。想让它记住,就得让浏览器每次自己带上凭证——
  那就是 2-13 的 Cookie 和 2-14 的会话要做的事。

读这个输出:GET 一个人占了近七成。这不奇怪——打开一个页面要取 HTML、取样式、取脚本、取一堆图片,全是 GET;而 POST 只在【点按钮】那一下发生。记住这个比例,就能明白为什么 GET 会被特别强调【安全】和【可缓存】。

四、无状态:不是缺陷,是设计

【无状态】这个词说的是:服务器处理完一条请求,就把这次见面忘了。下一条请求进来,在它眼里又是头一回见面。

这对服务器是好事。假设每秒 8000 条请求——它不需要为 8000 个人各留一个座位;任何一台服务器都能接任何一条请求,坏了一台,另一台直接接着干。

代价是服务器记不住你。要它记住,就得让浏览器每次自己带上凭证——这就是 2-13 的 Cookie 和 2-14 的会话要解决的事。

所以这条线是连着的:HTTP 故意不记人 → 需要 Cookie 来补 → Cookie 会过期 → 需要会话来串起一连串操作。教材把这一条放在 Cookie 前面七节,不是顺序凑巧。

30 秒实验

  1. 按 F12 打开开发者工具,切到 Network 面板,然后刷新页面。
  2. 随便点一条请求,看它的 Method 那一列是什么——猜猜一共有多少条是 GET。
  3. 再找一个你填过表、点过提交的页面重复这个过程,找出那个 POST,看看它和 GET 有什么不一样。

常见误区

  1. 错误:GET 和 POST 的区别就是【一个在地址栏、一个在请求体】。——纠正:它们表达的是两种意图:GET 是【取回来看】,POST 是【提交上去改点什么】。地址栏和请求体只是实现方式。意图不同,带来的是能不能缓存、能不能重复执行、能不能被预取这一整套差别。
  2. 错误:无状态是个缺陷,早晚要改掉。——纠正:它是故意的,而且是 Web 能扩展到今天这个规模的关键之一。没有状态,任何一台服务器都能接任何一条请求;有状态就得让服务器互相通报,成本高得多。
  3. 错误:既然是无状态,服务器就是完全不认人的。——纠正:协议本身不认人,但应用层可以加上记性。Cookie 与会话就是加在外面的那层记性。分清【协议不带状态】和【服务器不知道你是谁】这两件事,是理解后面几节的关键。
  4. 错误:请求头都是可有可无的装饰。——纠正:它们决定了这次请求怎么看、给不给、怎么给。Host 决定去哪个网站(一台服务器可以挂很多站)、Accept-Encoding 决定要不要压缩、Cookie 决定认不认得你。少一个,结果可能就不一样。
自测三问(点开对答案) 1. 为什么 GET 在真实流量里占近七成? —— 因为**打开一个页面就要取很多东西**:HTML、样式、脚本、图片,每一样都是一次 GET(见 1-6 的渲染路径)。而 POST 只在用户真正提交的时候发生。**所以【流量结构】其实是【页面结构】的投影。** 2. 无状态给服务器带来的最大好处是什么? —— **任何一台服务器都能接任何一条请求**。不需要给每个访问者留位置,也就不需要让服务器之间互相通报——**这才撑得住横向扩展**:不够就加机器,加上去就能干活。 3. 为什么教材要把无状态和 Cookie 分成两节讲、还隔了七节? —— 因为**它们是两个层面的东西**:无状态是协议的设计,Cookie 是在协议外面加上去的一层记性。**先讲清楚协议为什么不记人,才能讲清楚那层记性是必要的、以及它是怎么补上去的。**

相关笔记