暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

拥抱 HTTP/2(3更)

红牛编程 2021-08-21
738
更新内容:服务器推送、帧格式

HTTP/2

Homepage: https://http2.github.io/

官方文档:

  • RFC7540,HTTP2

  • RFC7541,HPACK -  HTTP/2 的头压缩

服务器推送,server push

故名思意,服务器主动将资源发送到浏览器端,称为推送!

我们以 nginx 的实现为例,来了解一下服务器推送!

http2_push 推送指令

这个推送不是任意资源都推送,必须是被依赖的资源才可以推送,也需满足同源策略。例如:

1<!-- index.html -->
2<head>
3  <link rel="stylesheet" href="dist/css/adminlte.min.css">
4</head>
5<body>
6    <script src="dist/js/adminlte.js"></script>
7</body>

此时 nginx 配置,指令 http2_push
用于设置推送资源:

1location / {
2        root   /usr/share/nginx/html;
3        index  index.html index.htm;
4        http2_push /dist/css/adminlte.min.css;
5        http2_push /dist/js/adminlte.js;
6    }

http2_push 需要使用带有绝对目录的相对地址。说白了,就是以 / 开头,表示从 root 目录开始。

当浏览器针对该服务器请求 Index.html 资源时,就会将 adminlte.min.css
adminlte.js
推送到浏览器端,如图:

这个流程的思路如图所示:

自动推送 http2_push_preload

http2_push
指令推送时,需要配置大量的静态资源依赖关系。这个操作在很多情况下会不方便,或者受到限制。缘于此,Nginx 也支持基于响应头中的 Link preload 字段完成自动推送。响应头格式为:

1Link: </css/styles.css>; rel=preload; as=style
2Link: </css/styles.css>; rel=preload; as=style, </js/scripts.js>; rel=preload; as=script
3


自动推送使用配置 http2_push_preload on|off
:

1location / {
2    root   /usr/share/nginx/html;
3    index  index.html index.htm;
4    proxy_pass http://upstream;
5    http2_push_preload on;
6}

nginx 服务器虽开启了自动推送,但 nginx 不能确定哪些资源要自动推送。这个需要后端程序来完成,nginx 通过 upstream
指令与后端程序交互,例如 Go, PHP 等:

PHP:

1header("Link: </dist/css/adminlte.min.css>; rel=preload; as=style");
2header("Link: </dist/js/adminlte.js>; rel=preload; as=script");

Go

1mux := http.NewServeMux()
2mux.HandleFunc("/"func(w http.ResponseWriter, req *http.Request) {
3        w.Header().Add("Link""</dist/css/adminlte.min.css>; rel=preload; as=style");
4        w.Header().Add("Link""</dist/js/adminlte.js>; rel=preload; as=script");
5    })

如果服务器或者浏览器不支持 HTTP/2,那么浏览器就会按照 preload 来处理这个头信息,预加载指定的资源文件。

选择性推送

服务器推送一个小 bug 是,服务器不确定浏览器是否真正需要该资源。例如,浏览器已经缓存了 /style.css
,那么就不需要推该资源了。一个示例,在 cookie 中有 session=1
时,不推送,没有该字段时推送,示例来自 https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/:

 1server {
2    http2_push_preload on;
3
4    location = /demo.html {
5        add_header Set-Cookie "session=1";
6        add_header Link $resources;
7    }
8}
9
10map $http_cookie $resources {
11    "~*session=1" "";
12    default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, </image2.jpg>; as=image; rel=preload";
13}

该逻辑通常在后端应用处理。

数据帧,Frame

HTTP/2 数据传输最小的单位是帧,Frame,所有的数据包括,head、body 都会打包到 Frame 发送。

Frame 是在数据传输流中的每个数据切片,为了标识 Frame 属于哪个流 Stream,Frame 结构会有  Stream ID 字段标识所属的数据流。

基于传输内容不同,Frame 分为很多类型,例如 Header、Data、PRIORITY 等。

帧格式,format

帧由 Header 和 Payload 组成,Header 长度固定,9 Bytes,72 bits。

格式如下:

 1 +-----------------------------------------------+
2 |                 Length (24)                   |
3 +---------------+---------------+---------------+
4 |   Type (8)    |   Flags (8)   |
5 +-+-------------+---------------+-------------------------------+
6 |
R|                 Stream Identifier (31)                      |
7 +=+=============================================================+
8 |                   Frame Payload (0...)                      ...
9 +---------------------------------------------------------------+
10


字段说明如下:

  • Length,荷载数据长度,24 位无符号整型可表示最大数 16,384(2^14),意味着 PayLoad 数据最大长度为 16,384。

  • Type,帧类型

  • Flags,标志位

  • R,保留位

  • Stream Identifier,流 ID,标识 Frame 所属的流

  • Frame Payload,荷载数据

帧类型,type

Frame TypeCode
DATA0x0数据帧,请求或响应主体帧
HEADERS0x1头帧,请求或响应头
PRIORITY0x2优先级帧,发送方对流优先级权重的建议值
RST_STREAM0x3强制停止帧,关闭对应流,接收者不能够在此流上发送任何帧
SETTINGS0x4设置帧,接收者向发送者通告己方设定,服务器端在连接成功后必须第一个发送的帧
PUSH_PROMISE0x5推送准备帧,服务器端通知对端准备推送数据
PING0x6Ping 帧,测试响应时间,心跳机制用于检测空闲连接是否有效
GOAWAY0x7优雅停止帧,通知对方关闭流,但需要完成未完成的任务
WINDOW_UPDATE0x8窗口升级帧,用于流量控制
CONTINUATION0x9延续帧,一个HEADERS/PUSH_PROMISE 帧后面会跟随零个或多个 CONTINUATION,只要上一个帧没有设置END_HEADERS 标志位

更详细的信息请参考:https://datatracker.ietf.org/doc/html/rfc7540#section-6

列举几个类型的帧载荷结构

DATA 类型的 PayLoad:

1+---------------+
2|Pad Length? (8)|
3+---------------+-----------------------------------------------+
4|                            Data (*)                         ...
5+---------------------------------------------------------------+
6|                           Padding (*)                       ...
7+---------------------------------------------------------------+

字段说明:

  • Pad Length,填充长度

  • Data,应用数据

  • Padding,填充

HEADER 类型的 PayLoad:

 1+---------------+
2|Pad Length? (8)|
3+-+-------------+-----------------------------------------------+
4|E|                 Stream Dependency? (31)                     |
5+-+-------------+-----------------------------------------------+
6|  Weight? (8)  |
7+-+-------------+-----------------------------------------------+
8|                   Header Block Fragment (*)                 ...
9+---------------------------------------------------------------+
10|                           Padding (*)                       ...
11+---------------------------------------------------------------+

字段说明:

  • Pad Length,填充长度

  • E,是否依赖专用位

  • Stream Dependency,流依赖

  • Weight,优先级权重

  • Header Block Fragment,头块分段

  • Padding,填充

SETTING 类型的 PayLoad:

1+-------------------------------+
2|       Identifier (16)         |
3+-------------------------------+-------------------------------+
4|                        Value (32)                             |
5+---------------------------------------------------------------+

字段说明:

  • Identifier,配置项标识

  • Value,配置项值

标志位,flag

下表展示了 Frame 类型和标志可能的组合关系,引用自 https://metacpan.org/release/CRUX/Protocol-HTTP2-0.14/view/lib/Protocol/HTTP2/Frame.pm:

 1Table represent possible combination of frame types and flags.
2Last column -- Stream ID of frame types (x -- sid >= 10 -- sid = 0)
3
4
5                    +-END_STREAM 0x1
6                    |   +-ACK 0x1
7                    |
   |   +-END_HEADERS 0x4
8                    |
   |   |   +-PADDED 0x8
9                    |   |   |   |   +-PRIORITY 0x20
10                    |   |   |   |   |        +-stream id (value)
11                    |
   |   |   |   |        |
12|
 frame type\flag | V | V | V | V | V |   |  V  |
13| --------------- |:-:|:-:|:-:|:-:|:-:| - |:---:|
14| DATA            | x |   |   | x |   |   |  x  |
15|
 HEADERS         | x |   | x | x | x |   |  x  |
16| PRIORITY        |   |   |   |   |   |   |  x  |
17|
 RST_STREAM      |   |   |   |   |   |   |  x  |
18| SETTINGS        |   | x |   |   |   |   |  0  |
19|
 PUSH_PROMISE    |   |   | x | x |   |   |  x  |
20| PING            |   | x |   |   |   |   |  0  |
21|
 GOAWAY          |   |   |   |   |   |   |  0  |
22| WINDOW_UPDATE   |   |   |   |   |   |   | 0/x |
23|
 CONTINUATION    |   |   | x | x |   |   |  x  |

几个标志位含义为:

  • END_STREAM 0x1,结束流

  • ACK 0x1,确认

  • END_HEADERS 0x4,结束头

  • PADDED 0x8,填充

  • PRIORITY 0x20,优先


文章转载自红牛编程,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论