HTTP协议探究(六):H2帧详解和HTTP优化
一 复习与目标
1 复习
HTTP1.1存在的问题
HTTP2.0要兼容HTTP1.1
HTTP2.0的重要概念
- 分帧层
- 二进制:流 消息 帧
- 流的状态、优先级和并发
- 流量控制
- 服务器推送
- 首部压缩
HTTP2.0的流的建立(HEADERS或PUSH_PROMISE)和数据发送(DATA)
2 目标
- 帧定义
- HTTP2.0流量分析
- Chrome插件:HTTP/2 and SPDY
- WireShark
- 对某些帧进行分析
- HTTP优化
二 帧定义
1 HEADERS
(1)定义
- 长度:16位,代表帧净荷最大可达65535字节(64KB),只有当Padded为1时才有效。
- 类型:0x01
- 标志:
- End Stream:位1标识最后报头区块;一个HEADER帧太长时会分帧传输,后续跟着CONTINUATION 帧,如果CONTINUATION帧的END_STREAM标志为1,代表流传输结束。但是CONTINUATION帧并不能用于关闭流。
- End Segment:位2标识当前端的最后一帧。
- End Header:位3标识帧包含了整个的报头块且后面没有延续帧。
- Padded:位4标识Pad Length字段会呈现。
- Priority:位6标识专用标记、流依赖及权重字段
- R:保留字段,1位
- 流标志符:31位,唯一标识 HTTP 2.0 的流,流标识符为奇数(不包含1),1为升级协议使用。
- 首部块:HTTP首部
(2)wireshark抓包
Stream: HEADERS, Stream ID: 25, Length 51, POST /fd/ls/lsp.aspx
Length: 51
Type: HEADERS (1)
Flags: 0x24
.... ...0 = End Stream: False
.... .1.. = End Headers: True
.... 0... = Padded: False
..1. .... = Priority: True
00.0 ..0. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0001 1001 = Stream Identifier: 25
1... .... .... .... .... .... .... .... = Exclusive: True
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Dependency: 0
Weight: 219
Header Block Fragment: 83dd870084b958d33f8b625918a10c508ad71a2bf35c830b...
Header: :method: POST
Header: :authority: cn.bing.com
Header: :scheme: https
#......
2 DATA
(1)定义
- 类型为0x0
注:基本上与HEADERS帧差不多,不过多介绍了。
(2)wireshark抓包
Stream: DATA, Stream ID: 25, Length 1316
Length: 1316
Type: DATA (0)
Flags: 0x01
.... ...1 = End Stream: True
.... 0... = Padded: False
0000 .00. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0001 1001 = Stream Identifier: 25
Data: ......
3 SETTINGS帧
(1)定义
类型为0x4
ACK标志:位1表示设置帧被接收端接受并应用。当ACK为1时,帧净荷必须为空.
帧净荷为帧参数
- SETTINGS_HEADER_TABLE_SIZE(1) :允许发送端通知远端终端解码报头区块的报头压缩表的最大承载量。初始值为4096字节
- SETTINGS_ENABLE_PUSH(2) :服务器推送许可标志,默认开启。
- SETTINGS_MAX_CONCURRENT_STREAMS(3) :最大流并发数,0代表不允许新建流,默认没有限制。
- SETTINGS_INITIAL_WINDOW_SIZE(4):初始窗口大小,默认65535,不超过2 ^ 31-1。
(2)wireshark抓包
# 发送设置帧
202.89.233.101 192.168.1.46 HTTP2 123 SETTINGS[0], WINDOW_UPDATE[0]
Stream: SETTINGS, Stream ID: 0, Length 18
Length: 18
Type: SETTINGS (4)
Flags: 0x00
.... ...0 = ACK: False
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
Settings - Header table size : 65536
Settings - Max concurrent streams : 1000
Settings - Initial Windows size : 6291456
# 发送设置帧确认
192.168.1.46 202.89.233.101 HTTP2 92 SETTINGS[0]
Stream: SETTINGS, Stream ID: 0, Length 0
Length: 0
Type: SETTINGS (4)
Flags: 0x01
.... ...1 = ACK: True
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
4 WINDOW_UPDATE帧
(1)定义
- 类型:0x8
- 标志:无
- 帧净荷:
- Reserved:1位保留位
- Window Size Increment:最大值位2^31-1
(2)wireshark抓包
Stream: WINDOW_UPDATE, Stream ID: 0, Length 4
Length: 4
Type: WINDOW_UPDATE (8)
Flags: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 1111 0000 0000 0000 0001 = Window Size Increment: 983041
(3)补充
- 流量控制是能作用某个流或整个连接(流标志符号为0,即根流可以限制依赖流)。
- WINDOW_UPDATE可由一个已经发送带有END_STREAM标记的帧的对等端来发送。这意味着接收端可以在“半封闭(远程)”或者“关闭”的流上接收WINDOW_UPDATE帧。
- 流量控制窗口用于指示发送端被允许传输的字节数(请回想起TCP的阻塞窗口和接受窗口)。
- 流量控制计算不包含帧报头。
- 流量窗口初始值为65535
- 流量窗口为负值代表禁止发送
- SETTING帧无法修改链接状态的流量控制窗口(即只能设置初始值,无法通过SETTING帧进行修改)
例如,如果客户端在建立的连接上立即发送60KB的数据,而终端将初始的窗口大小设置成16KB,客户端将重新计算流量控制窗口的可用空间为-44KB。终端将保持一个负数的流量控制窗口直到窗口更新帧恢复窗口到正数,这个时候客户端才能恢复数据发送。
5 PING帧
(1)定义
- 长度:固定为0x8
- 类型:0x6
- ACK标志:位1用于标识0为请求,1为响应
- 流标志符:固定为0x0
- 帧净荷:ping的唯一标识符
(2)wireshark抓包
# 成对出现
192.168.1.46 202.89.233.100 HTTP2 100 PING[0]
Stream: PING, Stream ID: 0, Length 8
Length: 8
Type: PING (6)
Flags: 0x00
.... ...0 = ACK: False
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
Ping: 0000000000000001
202.89.233.100 192.168.1.46 HTTP2 100 PING[0]
Stream: PING, Stream ID: 0, Length 8
Length: 8
Type: PING (6)
Flags: 0x00
.... ...1 = ACK: True
0000 000. = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
Ping: 0000000000000001
6 RST_STREAM帧
(1)定义
- 长度:固定为0x4
- 类型:0x3
- ACK标志:无
- 流标志符:标识哪个流非正常终结
- 帧净荷:错误码
- NO_ERROR(0):没有错误,用于给GOWAY帧平滑关闭
- PROTOCOL_ERROR(1):协议错误
- INTERNAL_ERROR (2) : 终端遇到意外的内部错误。
- FLOW_CONTROL_ERROR (3) : 终端检测到对等端违反了流量控制
- SETTINGS_TIMEOUT (4) : 终端发送了设置帧,但没有及时收到响应。
- STREAM_CLOSED (5) : 终端在流半封闭的时候收到帧。
- FRAME_SIZE_ERROR (6) : 终端收到大小超过最大尺寸的帧(2^14 -1)。
- REFUSED_STREAM (7) : 终端拒绝流在它执行任何应用处理之前.
- CANCEL (8) : 终端使用这个标示某个流不再需要。
- COMPRESSION_ERROR (9) : 终端无法维持报头压缩上下文的连接
- CONNECT_ERROR (10) : 响应某个连接请求建立的连接被服为异常关闭。
- ENHANCE_YOUR_CALM (11) : 终端检测出对等端在表现出可能会产生过大负荷的行为。
- INADEQUATE_SECURITY (12) : 基础传输包含属性不满足文档或者终端申明的最小要求。
(2)wireshark抓包
Stream: RST_STREAM, Stream ID: 5, Length 4
Length: 4
Type: RST_STREAM (3)
Flags: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0101 = Stream Identifier: 5
Error: CANCEL (8)
7 PUSH_PROMISE
(1)定义
- 长度:非固定
- 类型:0x04
- 标志:
- END_HEADERS:位3标识帧包含了整个的报头块且后面没有延续帧。
- Padded:位4标识Pad Length字段会呈现。
- 流标志符(Stream Identifier)
- 准备推送的流标志符(Promised-Stream-ID)
- 首部块:HTTP首部
(2)wireshark抓包
# 由服务器建立流并发送HTTP头部
Stream: PUSH_PROMISE, Stream ID: 1, Length 166, GET /styles.780f923f35dd43d00653.css
Length: 166
Type: PUSH_PROMISE (5)
Flags: 0x04
.... .1.. = End Headers: True
.... 0... = Padded: False
0000 ..00 = Unused: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Identifier: 1
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0010 = Promised-Stream-ID: 2
Header Block Fragment: 208244976109f54150bbaf0257c4ccacb7248d332000e36c...
Header: :method: GET
# ......
(3)补充
- PUSH_PROMISE为服务器建立流,HEADERS为客户端建立流。
- 服务器建立的流标识符为偶数(不包含0),0为根流标识符。
- 客户端 -----HEADERS帧(含priority)-----> 服务器
- 服务器 -----PUSH_PROMISE帧(不含priority)-----> 客户端,所以一般后续帧为 客户端 -----PRIORITY帧----> 服务器。
8 PRIORITY帧
(1)定义
- 长度:非固定
- 类型:0x02
- 标志:无
- 保留位和流标志符(Stream Identifier)
- 独占位(Exclusive):1位,指示流的依赖是专有的
- 依赖流标识符(Stream Dependency)
- 优先级权重:1-256的权重值
(2)wireshark抓包
Stream: PRIORITY, Stream ID: 2, Length 5
Length: 5
Type: PRIORITY (2)
Flags: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0010 = Stream Identifier: 2
1... .... .... .... .... .... .... .... = Exclusive: True
.000 0000 0000 0000 0000 0000 0000 0001 = Stream Dependency: 1
Weight: 109
9 GOWAY帧
(1)定义
- 长度:固定
- 类型:0x07
- 标志:无
- 保留位和流标志符(Stream Identifier)
- 保留位与最后一个流标识符
- 错误码
(2)wireshark抓包
Stream: GOAWAY, Stream ID: 0, Length 8
Length: 8
Type: GOAWAY (7)
Flags: 0x00
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0000 = Stream Identifier: 0
0... .... .... .... .... .... .... .... = Reserved: 0x0
.000 0000 0000 0000 0000 0000 0000 0011 = Promised-Stream-ID: 3
Error: NO_ERROR (0)
(3)补充
- 通知远端对等端不要在这个连接上建立新流,即提示抛弃该连接,重新建立一条TCP连接。
- 允许终端优雅的停止接收新的流,但仍可以继续完成之前已经建立的流的处理。
注:CONTINUTION帧省略,CONTINUTION帧比较简单。
三 Chrome插件解析HTTP2流量
88: HTTP2_SESSION
Start Time: 2018-12-03 14:36:45.975
# HEADERS帧
t= 10464 [st= 0] HTTP2_SESSION_SEND_HEADERS
--> exclusive = true
--> fin = false
--> has_priority = true
--> :method: POST
:authority: cn.bing.com
:scheme: https
:path: /fd/ls/lsp.aspx
content-length: 1316
origin: https://cn.bing.com
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36
content-type: text/xml
accept: */*
referer: https://cn.bing.com/
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7
cookie: _EDGE_V=1; MUID=1EF97277BD3961513FB57ECDBC17605A; MUIDB=1EF97277BD3961513FB57ECDBC17605A; SRCHD=AF=BEHPTB; SRCHUID=V=2&GUID=BD0C76F5F80546D8877C9F6688DDF1AA&dmnchg=1; ENSEARCH=BENVER=1; SRCHUSR=DOB=20181203&T=1543818612000; _EDGE_CD=u=zh-cn; _UR=MC=1; SRCHHPGUSR=CW=1536&CH=711&DPR=1.25&UTC=480&WTS=63679415390&NEWWND=1&NRSLT=-1&SRCHLANG=&AS=1&NNT=1&HAP=0; _EDGE_S=SID=04B092AF518367C83A3F9E1550AD6673; _SS=SID=04B092AF518367C83A3F9E1550AD6673&bIm=978208&HV=1543819002; ULC=P=1D5FE|9:1&H=1D5FE|5:1&T=1D5FE|6:1:8
--> parent_stream_id = 0
--> source_dependency = 259 (HTTP_STREAM_JOB)
--> stream_id = 25
--> weight = 220
# DATA帧
t= 10464 [st= 0] HTTP2_SESSION_SEND_DATA
--> fin = true
--> size = 1316
--> stream_id = 25
# WINDOW_UPDATE帧
t= 10464 [st= 0] HTTP2_SESSION_UPDATE_SEND_WINDOW
--> delta = -1316
--> window_size = 1047260
t= 10501 [st= 37] HTTP2_SESSION_RECV_WINDOW_UPDATE
--> delta = 1316
--> stream_id = 0
t= 10501 [st= 37] HTTP2_SESSION_UPDATE_SEND_WINDOW
--> delta = 1316
--> window_size = 1048576
t= 10541 [st= 77] HTTP2_SESSION_RECV_HEADERS
--> fin = false
--> :status: 204
x-msedge-ref: Ref A: D0007C217F0649D79ED88874FFB61759 Ref B: BJ1EDGE0217 Ref C: 2018-12-03T06:36:55Z
date: Mon, 03 Dec 2018 06:36:55 GMT
--> stream_id = 25
t= 10542 [st= 78] HTTP2_SESSION_RECV_DATA
--> fin = true
--> size = 0
--> stream_id = 25
# PING帧
t= 96465 [st= 86001] HTTP2_SESSION_PING
--> is_ack = false
--> type = "sent"
--> unique_id = 1
t= 96501 [st= 86037] HTTP2_SESSION_PING
--> is_ack = true
--> type = "received"
--> unique_id = 1
# RST_STREAM
t=25836233 [st=1902] HTTP2_SESSION_SEND_RST_STREAM
--> description = ""
--> error_code = "8 (CANCEL)"
--> stream_id = 5
t=222812 [st=212348] HTTP2_SESSION_CLOSE
--> description = "Error 101 reading from socket."
--> net_error = -101 (ERR_CONNECTION_RESET)
t=222813 [st=212349] HTTP2_SESSION_POOL_REMOVE_SESSION
t=222813 [st=212349] -HTTP2_SESSION
四 HTTP优化
(1)基于TCP和TLS优化后
(2)减少DNS查询
- 尽量不用多域名
- 证书链不要太长
(3)减少HTTP重定向
- HTTP 重定向极费时间,特别是不同域名之间的重定向,更加费时
(4)使用CDN(内容分发网络)
- 把数据放到离用户地理位置更近的地方,可以显著减少每次TCP 连接的网络延
迟,增大吞吐量。
(5)在客户端缓存资源
# nginx添加响应头
add_header Cache-Control private,max-age=86400,proxy-revalidate;
- 具体缓存策略参考:HTTP协议探究(二):代理、网关和隧道
(6)传输压缩过的内容
- 速度:brotli > gzip压缩 > 没压缩
# nginx使用ngx_brotli模块
# gzip压缩1.1,只有britli失效才有用
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
gzip_comp_level 6;
gzip_http_version 1.1;
# brotli压缩所有HTTP版本
brotli on;
brotli_comp_level 6;
brotli_buffers 16 8k;
brotli_min_length 20;
brotli_types *;
(7)消除不必要的请求开销
- 升级HTTP2.0即可
- HTTP2.0能够压缩头部,减少重复头部传输
# nginx配置
listen 443 ssl http2 fastopen=3 reuseport default_server;
(8)利用服务器推送
# nginx配置
location / {
root /home/nginx;
index index.html;
http2_push /styles.389ab69b43b87e6d33f0.css;
# ...
}
参考:
- http2详解
- HTTP/2 服务器推送(Server Push)教程
- 《Web性能权威指南》
- RFC 7540
HTTP协议探究(六):H2帧详解和HTTP优化的更多相关文章
- TCP/IP协议学习(六) 链路层详解
学习知识很简单,但坚持不懈却又是如此的困难,即使一直对自己说"努力,不能停下"的我也慢慢懈怠了... 闲话不多说,本篇将讲述TCP/IP协议栈的链路层.在本系列第一篇我讲到,TCP ...
- HTTP协议 (六) 状态码详解
HTTP协议 (六) 状态码详解 HTTP状态码,我都是现查现用. 我以前记得几个常用的状态码,比如200,302,304,404, 503. 一般来说我也只需要了解这些常用的状态码就可以了. 如果 ...
- H264编码原理以及I帧、B和P帧详解, H264码流结构分析
H264码流结构分析 http://blog.csdn.net/chenchong_219/article/details/37990541 1.码流总体结构: h264的功能分为两层,视频编码层(V ...
- C++11 并发指南六(atomic 类型详解四 C 风格原子操作介绍)
前面三篇文章<C++11 并发指南六(atomic 类型详解一 atomic_flag 介绍)>.<C++11 并发指南六( <atomic> 类型详解二 std::at ...
- C++11 并发指南六(atomic 类型详解三 std::atomic (续))
C++11 并发指南六( <atomic> 类型详解二 std::atomic ) 介绍了基本的原子类型 std::atomic 的用法,本节我会给大家介绍C++11 标准库中的 std: ...
- C++11 并发指南六( <atomic> 类型详解二 std::atomic )
C++11 并发指南六(atomic 类型详解一 atomic_flag 介绍) 一文介绍了 C++11 中最简单的原子类型 std::atomic_flag,但是 std::atomic_flag ...
- HTTP协议头部与Keep-Alive模式详解
HTTP协议头部与Keep-Alive模式详解 .什么是Keep-Alive模式? 我们知道HTTP协议采用“请求-应答”模式,当使用普通模式,即非KeepAlive模式时,每个请求/应答客户和服务器 ...
- 搞懂分布式技术4:ZAB协议概述与选主流程详解
搞懂分布式技术4:ZAB协议概述与选主流程详解 ZAB协议 ZAB(Zookeeper Atomic Broadcast)协议是专门为zookeeper实现分布式协调功能而设计.zookeeper主要 ...
- 小白进阶之Scrapy第六篇Scrapy-Redis详解(转)
Scrapy-Redis 详解 通常我们在一个站站点进行采集的时候,如果是小站的话 我们使用scrapy本身就可以满足. 但是如果在面对一些比较大型的站点的时候,单个scrapy就显得力不从心了. 要 ...
随机推荐
- python 设计模式之解释器(Interpreter)模式
#写在前面 关于解释器模式,我在网上转了两三圈,心中有了那么一点概念 ,也不知道自己理解的是对还是错. 其实关于每一种设计模式,我总想找出一个答案,那就是为什么要用这种设计模式, 如果不用会怎么样,会 ...
- 如何查看SWT源代码和帮助文档
如何查看SWT源代码https://blog.csdn.net/wzq__janeGreen_/article/details/80068998
- ElementUI】日期选择器时间选择范围限制,只能选今天之前的时间,或者是只能选今天之后的时间。今天是否可以选。限制结束日期不能大于开始日期
<el-date-picker v-model="value1" type="date" placeholder="选择日期" :pi ...
- [插件式开发][C#]
Demo 下载 参考文章:https://www.cnblogs.com/hippieZhou/p/9398354.html 技术方面要使用到 依赖注入,可以参考此示例逐步学习:https://git ...
- PHP使用MongoDB存储经纬度,查询距离
https://blog.csdn.net/qq_40012295/article/details/84861466 https://docs.mongodb.com/manual/reference ...
- python 适合的才是最好的
群里老有人问最新的破解码最新的包,最新的就是最好的吗? 今天说一下这些新手的坑: numpy 最好使用的版本是1.13.3 而非新的1.17.0 pandas最好使用的版本是0.18.0 而非新的0 ...
- 阶段5 3.微服务项目【学成在线】_day09 课程预览 Eureka Feign_12-课程预览功能开发-需求分析
5 课程预览功能开发 5.1 需求分析 课程预览功能将使用cms系统提供的页面预览功能,业务流程如下: 1.用户进入课程管理页面,点击课程预览,请求到课程管理服务 2.课程管理服务远程调用cms添加页 ...
- (六)Centos之目录作用介绍
我们先切换到系统根目录 / 看看根目录下有哪些目录 这里首先看下 根目录/ 下的 bin 和 sbin: 在user下也有bin和sbin 根目录下的bin和sbin,usr目录下的bin和sbin, ...
- 基于OpenAM系列的SSO----基础
基于OpenAM系列的SSO----基础 OpenAM简介:OpenAM是一个开源的访问管理.授权服务平台.由ForegeRock公司发起.OpenAM前身为OpenSSO,由SUN公司创建,现在 ...
- 动态PHP电商网站伪静态的 Nginx反向代理Cache缓存终极设置
转自: http://www.ttlsa.com/nginx/dynamic-php-nginx-cache/