三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

【协议】【http2】

【协议】【http2】

http1 做了哪些优化

  • 存在什么问题:http1 没有持久的tcp 连接,访问一个网页(html jpg css js 等资源)需要建立多个tcp 连接 访问资源,每次请求都会导致两次往返延迟(tcp握手和挥手)
    如何解决的

  • 优化1:keep-alive:长连接,一个tcp 连接可以复用,消除另一次tcp 慢启动的往返

  • 优化2 http1.1 pipling: 管道话的方式,由客户端浏览器决定,优先访问一些资源,管道化,c端可以同时发送多个请求到s,s 同时处理两个请求的资源,但是s按照请求先来后到把资源返回c,如果第一个请求资源阻塞住了,后面所有的请求都被阻塞。

  • 管道化夭折的原因:只是解决了,请求发送的并行,没有解决响应的并行化。

    • 1 只能严格按照请求顺序串行返回响应,不允许响应交错到达
    • 2 如果第一个请求阻塞了,并行发送的后边的所有请求都会阻塞。
    • 3 服务端pipeline 需要缓存资源,服务端并行处理请求资源,但是串行返回资源,前边的资源没返回给c的时候,剩下的资源需要s 自己缓存,服务端资源占用太多。
    • 4 并行的几个请求中如果前边的 请求断连了,那么即使s 处理了剩余的并行请求的资源,c也hui重新发送所有的http 请求,造成s重复处理。
    • 5 中间如果存在代理,若代理不支持管道,代理会拒绝这个http 请求或者,导致http 请求的串行化。
  • 疑问: tcp 慢启动需要再了解,tcp 连接建立的时候 tcp 窗口是一点一点的变化,最终到一个合适的窗口大小,tcp的启动整个过程非常慢

  • 优化3 大多数浏览器 为每个域名打开6-8个连接,为了突破,6个连接的限制,会切分子域名提供静态资源。资源消耗比较多(客户端需要和每个域名建立tcp 请求)

  • 多域名的缺点
    针对那么多tcp 连接,c 和s 都是有额外开销
    每个tcp 连接都要经过建立tcp 连接,慢启动阶段
    页面加载完之后,tcp 连接大多数都用不到了,(74%的连接仅仅处理一次请求),资源消耗。整体的效率低

  • 优化4 资源组合,比如把多个 js css 合并成一个文件,达到建立更少的连接的目的 ;拼接:多张图片合为一张更大的图片(sprite图)

  • 优化5 嵌入资源:为了减少下载次数,直接嵌入到网页里边。维护成本还是有点高

现在很多网站都已经启动了http2 了,如何禁掉,命令行启动浏览器,加参数 --disable-http2

http2

http1升级2环境搭建

linux环境,工具:
nghttp2 官网:https://www.nghttp2.org/

http2二进制分帧

就是把原来http1中的请求和响应按照帧格式标准化成格式一样的小包裹。这些小包裹带着帧类型和流ID,可以乱序发送和接受,不再像http1的pipeline 一样第一个请求没有返回,第二个请求的响应就会一直被阻塞,真正实现了多路复用。同一个请求响应是一条流,其帧的流id是一样的。
帧类型 header帧和 data帧。

HPACK头部压缩

http中请求头很多的header字段和其值是固定的,每次发请求,都要重复发一次这些header数据,占用的带宽较大。HPACK通过对header头部压缩达到减少header带宽的效果。
HPACK压缩使用两张表,一张静态表 一张动态表,两张表用于存储header的字段和对应的值。每次请求值需要使用表中对应的索引即可。

静态表

是一张预定义的表格,硬编码在http2协议中的,共1-61个编号。把"GET"、“Host”、"Content-Type"这些最常用的头部字段,提前编好1-61的固定编号,传输时直接发编号代替整串文字。
其中部分编号对应的键值对是完整的,比如编号2对应:method: GET、编号16对应accept-encoding: gzip, deflate,key和value都已预定义完成。其余大部分编号的value位置为空,比如编号1对应:authority、编号32对应cookie,这类编号可以直接引用预定义的key,再搭配自定义的value使用,依然是完整的键值对条目。

具体来说,当编码器遇到这类头部时,它会采用‌“带索引的字面量表示法”‌(Indexed Name, Literal Value)。以下是其存储和传输的具体步骤:

  1. 传输过程:Key 用编号,Value 用明文或哈夫曼编码
    在二进制帧中,这个头部会被拆成两部分发送:

    ‌第一部分(引用 Key)‌:发送静态表中该 Key 对应的‌索引编号‌。这告诉解码器:“我要用的字段名是静态表里第 N 号那个”。
    ‌第二部分(传输 Value)‌:紧接着发送具体的 ‌Value 字符串‌。
    这个 Value 可以选择‌直接明文传输‌。
    也可以选择使用 ‌Huffman 编码‌压缩后传输(通常为了节省空间,都会选 Huffman 编码)。

  2. 是否存入动态表?由编码器决定
    传输完这个 Key(索引) + Value(字面量) 的组合后,编码器会根据策略决定是否将其存入动态表,这对应了 HPACK 的三种指令:

指令类型行为描述典型场景
‌增量索引 (Incremental Indexing)‌将完整的 Key + Value 作为新条目‌存入动态表‌。下次再传相同的 Cookie 时,就可以直接用动态表的新索引,连 Value 都不用传了。大多数普通请求,希望后续复用。
‌不索引 (Without Indexing)‌‌不存入‌动态表,仅本次传输使用。Value 依然通过字面量发送。敏感数据(如某些 Token),防止侧信道攻击推断内容。
‌永不索引 (Never Index)‌‌永远不存入‌动态表,且标记该字段为敏感。极高安全要求的场景,代理服务器通常会强制使用此模式处理 Cookie。
  1. 举个直观的例子
    假设你要发送一个自定义 Cookie:cookie: session_id=abc123

    (1). 查找静态表‌:发现 cookie 在静态表中排第 ‌32‌ 号,但静态表里没有预设具体的 value。
    ‌ (2). 编码发送‌:
    先发送一个特殊的二进制前缀,表示“我要引用静态表的 Key,并附带一个新的 Value”。
    发送索引值 ‌32‌(代表 Key 是 cookie)。
    发送 Value session_id=abc123(通常会经过 Huffman 编码压缩)。
    (3). 解码端操作‌:
    收到索引 32,从静态表取出 Key cookie。
    收到后面的数据,解码出 Value session_id=abc123。
    组合得到完整头部:cookie: session_id=abc123。
    (4). 更新动态表(可选)‌:如果编码器选择了“增量索引”,客户端和服务端会将 cookie: session_id=abc123 这一整对存入各自的动态表。下次再发同样的 Cookie,就直接发动态表的新索引(比如 62),只需 1 个字节。

动态表

是分别存储在客户端和服务器端的表格,把这次请求里新出现的自定义头部,临时存到双方的共享字典里,下次再传直接用短编号引用,原本几十上百字节的重复头部,最后可能只用1-2个字节就能传完,头部压缩率最高能达到90%以上,弱网环境下的加载速度提升特别明显。

http2服务器推送

https://www.cnblogs.com/tinywan/p/8599858.html

服务器推送配置

server{#Ensure that HTTP/2is enabledforthe serverlisten443ssl http2;ssl_certificate ssl/certificate.pem;ssl_certificate_key ssl/key.pem;root/var/www/html;#whenevera client requests demo.html,also push#/style.css,/image1.jpg and/image2.jpg location=/demo.html{http2_push/style.css;http2_push/image1.jpg;http2_push/image2.jpg;}}
← 返回列表