http详解

http 1.0

  • 简单、灵活、易扩展、跨平台
    1. 基本格式:header+body
    2. header、status 都可以自定义
    3. https = http + tls/ssl
  • 无状态: 服务器不会记录请求的状态
  • 默认关闭 keepalive,每次请求都建立一次tcp连接,服务器处理后立即断开tcp连接

http1.0有队头阻塞, 即没收到第一个请求对应的响应 就不能发送第二个请求

http 1.1

  • 默认keepalive|长连接
  • 流水线模式基于长连接,但是默认关闭

流水线模式解决了请求阻塞,不用等待前一个请求收到响应就可以发送下一个请求
但是对于响应队头阻塞没有解决。例如,服务器依次收到A和B,但是先完成了B却需要等待A完成先发送A。

keepalive的缺点

keepalive会保持tcp连接一段时间,如果服务器要响应大量不同用户一次性的请求,会造成服务器同时保活多个tcp连接

http 2.0

  1. 二进制格式: 把传输消息分成更小的帧,并使用二进制格式编码。头部和数据体都是二进制格式
  2. 并发传输:引入Stream概念,每个请求分配到唯一的StreamID,多个Stream复用一条Tcp连接。每个Stream的帧之间是有序的,不同stream的帧是可以乱序的。
  3. 首部压缩:减小首部大小。先给对方一份 首部各字段和对应值 的编码表,之后首部只需要填入对应编码就可以。
  4. 服务端推送: 收到用户的请求后,预测用户可能会请求其他资源,主动将这些资源发送给客户端。

解决了响应队头阻塞
但是没有解决tcp层面的队头阻塞

tcp的队头阻塞



多个Stream共用一个Tcp连接,即共用一个滑动窗口。那么当某个前面的stream的某个帧丢失,tcp协议规定必须按顺序读取,所以此时后面的stream全部被阻塞。

http 3.0

回顾http1.1 未解决响应队头阻塞
回顾http2.0 未解决tcp层次的队头阻塞。
所以http3.0采用 UDP + QUIC

  • UDP:
    • 无序
    • 不可靠
  • QUIC:
    • 无队头阻塞
    • 更快建立连接
    • 连接迁移
  1. 无队头阻塞?
    也有类似Stream与多路复用的概念,可以在同一条连接上并发传输多个Stream。当某个Stream丢包只阻塞这个Stream不影响其他Stream
  2. 更快的建立连接
    QUIC握手整合了TLS握手
  3. 连接迁移
    http2.0采用TCP四元组来标识一个连接,当用户切换网络就要重新建立连接。
    http3.0采用连接ID来标识,只要还有TLS密钥就可以无缝连接

    TCP四元组:(源IP+源端口+目的IP+目的端口)

轮询

先思考一个场景,服务器需要主动给用户发送请求,而http貌似只能由用户发起请求,服务器在响应该请求

要解决这个痛点,有两种办法

  1. 定时轮询: 前端定时发送请求给服务器
    1. 例如:扫码登录
  2. 长轮询: 设定一个时间比较长的超时时间,服务器收到请求后,先不响应,而是等待用户完成扫码操作,再响应。

这种简单的请求可以用如上方法,但是如果是游戏场景呢?

websocket解决了,全双工。

http1.1 半双工

http中谁主动断开tcp连接?

两方都可以,但是客户端主动比较好。因为主动断开方最后需要等待2msl时间,会占用资源

如果响应头包含 content-length,则客户端知道什么时候接收完毕,客户端主动断开。
否则则服务端断开。

缓存

流程:

  1. 第一次请求后缓存response的资源和header,并在下一次请求同一资源时
    1. 根据expires和cache-control判断是否命中强缓存,如果命中则不再发起真实请求,但是状态码返回200.
    2. 未命中强缓存,则发起请求 .
      1. 服务器根据请求头的IF-Modified-Since 或 IF-None-Match , 他们的值分别表示 Last-Modified 缓存文件最晚一次修改时期,Etag 资源唯一标识(一般是hash) , 来判断缓存是否命。如果命中,服务器响应状态码为304。
      2. 未命中:浏览器发送资源以及新的header信息。

强制缓存

  • 第一次响应后,响应头的 ExpiresCache-Control 被保存起来
    • Expires: 资源过期日期。
    • Cache-Control:
      • max-age: 资源有效时长, 结合响应头的 Date 算出资源过期时间。
      • no-cache:使用协商缓存策略。
      • 优先级高于Expires
  • 如果命中,则使用本地资源。
  • 如果未命中,则走协商缓存策略。

协商缓存

img

  • 协商缓存由服务器决定资源是否可用
  • 主要涉及到两对属性, 第一次响应头里的 last-ModifiedEtag 以及 后续请求头里的 If-Modified-SinceIf-None-Match ,它们的值对应相等。
  • 第二次请求时,请求头带上 If-Modified-SinceIf-None-Match ,服务器通过这两个属性判断资源是否变化,从而判断资源是否可用。
    • 如果资源不可用(更新了),则返回200,和最新资源以及新的 last-ModifiedEtag
    • 如果资源可用(不变),则返回304,表示直接从缓存里拿资源

tcp粘包

首先tcp是面向字节流的,在发送数据时需要把数据分成一个个报文

报文有两种情况:

  • 大于等于mss(最小报文长度)—— 直接发送
  • 小于mss —— 等待以下一个报文一起发送

可能出现如下情况,就是粘包

1
2
3
4
5
mss1.1 ===> 发送

mss1.2 + mss2.1 ===> 发送

mss2.2 + mss3.1 ===> 发送