051.HTTP/1.1发展至今遇到的问题
现代的网络世界随着贷款的增加,延迟并没有显著下降,
并发连接有限(比如Chrome一个客户端针对一个域名最多只能同时支持6个HTTP连接)
统一连接同时只能完成一个HTTP事务(即必须等一个请求/响应完了之后才能继续处理下一个请求/响应)
长肥网络 带宽很大,但是延迟也很高
052.HTTP/1.1为了解决性能问题做过的努力
Spriting 合并多张小图为一张大图供浏览器JS拼凑使用
Inlining内联,将图片嵌入到CSS或者HTML文件中,减少网络请求次数
Concatenation 拼接,将多个体积较小的JS使用webpak等工具打包成1个JS文件,减少网络请求次数
Sharding分片,将同一个页面的资源分散到不同的页面,提升连接上限
053.HTTP2协议的特点
HTTP2协议是基于谷歌的SPDY协议的,它拥有以下特点
。在应用层上修改,基于并充分挖掘TCP的协议性能
。客户端向Server发送Request,服务器发送响应Response这种基本模型不变
。老的schema不变,还是http://和https://
。使用HTTP/1.1的客户端和服务器可以无缝的通过代理方式转移到HTTP/2上
。不识别HTTP/2的代理服务器可以将请求降级为HTTP/1.1
。传输的数据量大幅减少,以二进制的方式传输数据,标头压缩
。多路复用,消息优先级
。服务器端并发消息推送
054.HTTP/2是不是必须基于TLS/SSL协议?
。IETF标准不要求必须基于TLS、SSL协议
。浏览器要求必须基于TLS/SSL协议
。在TLS层ALPN(Application Layer Protocal Negotiation)扩展做协商,只认识HTTP/1.1的代理服务器不会干扰HTTP/2
。schema还是http:// 和https://,默认基于80和443端口
。h2 基于TLS协议运行的HTTP/2被称为h2
。h2c 直接才TCP协议之上运行的HTTP/2称为h2c
从HTTP/1.1升级到h2其实是基于ALPN这个扩展来实现的,
而从HTTP/1.1升级到h2c,其实是和HTTP/1.1升级到WebSocket协议是一样的,步骤如下
1.客户端传一个头部
Connection: Upgrade,HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: 438997893ab379
2.客户端回一个101响应码
HTTP/1.1 101 SwitchingProtocals
Connection: Upgrade
Upgrade: h2c
3.客户端再发送一个Magic帧,Magic帧的固定格式 PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
055.TLS通讯过程
1.客户端发起一个Client Hello
2.服务器端回复一个Server Hello,并携带上自己支持的加密算法
3.服务器端发送一个证书,并发送一个自己已经选择的加密算法
4.客户端收到证书后,验证证书
5.服务器端再发送一个Server Hello 这时连接已经建立了
6.客户端和服务器端都基于加密算法经过非对称加密,都得到一个加密秘钥
7.接下来所有的数据传输都是基于这个加密秘钥的
056.h2的会话建立过程
1.客户端发送Client Hello 在请求的ALPN扩展中列出客户端支持的所有应用层协议(是一个列表)
2.服务器端收到Client Hello 后也在响应的ALPN扩展中将自己选择的协议告知客户端
3.客户端再发送一个由Magic帧和Settings真共同组成的报文请求,Settings真必须存在,哪怕没有携带任何数据
这时会话就建立了
057.消息、流、帧之间的关系
1.一个HTTP请求或响应就称为一个消息,在HTTP/1.1中一条消息是不能再拆分的,消息是数据传输的最小单位
2.连接Connection,即一个客户端和一个服务器只能建立一个TCP连接,一个连接上包含一个或多个流,流与流之间是并发进行的
3.数据流Stream,一个双向的数据流,包含一条或者多条消息
4.数据帧Fream,在HTTP/1.1中没有帧的概念,但是在HTTP2中一个消息可以被拆分成一个或者多个帧(HEADERS帧对应HTTP/1.1的头部 DATAS帧对应的是HTTP/1.1中的包体,帧与消息本质上没有什么关系,他们之间建立的是应用程层上的业务逻辑关系)
5.传输时无序,接收时组装:在同一个stream中,帧必须是按顺序传输的,但是在不同的stream中,frame可以是无序的
例如传输时应该是这样的:stream1(headers)-stream3(headers)-stream3(data)-stream1(data)
接收端根据streamId来组装数据
058.StreamID
使用控制帧SETTINGS_MAX_CONCURRENT_STREAMS来控制并发的stream数
客户端建立的stream流的streamid必须是基数
服务器端建立的stream流的streamid必须是偶数
新建立的流的ID必须大于曾经建立过的状态为opened或者reserved的流ID
在新建立的流上发送帧时,意味着更小的ID为idle状态的流设置为closed状态
StreamID不能复用,长连接耗尽ID创建新连接,比如当StreamID到达2的31次方时,就必须关闭当前的长连接,重新开启一个长连接
StreamId为0时,只能用于传输控制帧
059.帧的属性
0至2的14次方 - 1 所有实现必须可以支持16KB以下的帧
2的14资方至2的24次方-1 传递16KB到16MB时,必须接收端首先公布自己可以处理此大小,通过SETTINGS_MAX_FRAME_SIZE帧(Identifier)告知
帧类型
-DATA 0x0 传递HTTP包体
-HEADERS 0x1 传递HTTP头部
-PRIORITY 0x2 指定Stream流的优先级
-RST-STREAM 0x3 终止Stream流
-SETTINGS 0x4 修改连接或者Stream的配置
-PUSH-PROMISE 0x5 服务器端推送请求时描述请求的帧,只能由服务器发送
-PING 0x6 心跳检测,间距RTT往返时间的功能
-GOAWAY 0x7 优雅的终止连接或者通知错误
-WINDOW_UPDATE 0x8 实现流量控制
-CONTINUATION 0x9 传递较大HTTP头部时的持续帧
设置帧 不是协商,而是通知,协商是有交互的,而通知是客户端或者浏览器直接告诉对方应该如何做 格式是: dientifier: value
设置帧的类型
SETTINGS_HEADER_TABLE_SIZE 0x1 通知对端索引表的最大的尺寸(单位字节,初始值4096字节)
SETTINGS_ENABLE_PUSH 0x2 为0时可以禁用服务器推送功能,1时启用服务器推送功能
SETTINGS_MAX_CONCURRENT_STREAMS 0x3 告诉接受端允许的最大并发数量(最大Stream数)
SETTINGS_INITAL_WINDOW_SIZE 0x4 声明发送端的窗口大小,用于Stream级别流控(2的16次方-1字节)
SETTINGS_MAX_FRAME_SIZE 0x5 设置 帧的最大值,初始值2^14字节
SETTINGS_MAX_HEADER_LIST_SIZE 0x6 通知对端头部索引表的最大尺寸,单位字节,基于未压缩前的头部
060.HPACK
HPACK是用于压缩HTTP头部的压缩算法,它主要是有两个索引表:静态映射表和动态映射表
静态映射表就是固定的写死的例如1 动态映射表就是基于前面的请求,往HPACK映射里动态添加映射
Huffman树
HTTP头部的传输有三种方式
-key和value都是使用HPACK算法进行压缩
-key进行HPACK压缩,value不使用HPACK压缩
-key和value都不使用HPACK压缩
HEADER帧中使用相应的标志位来标识是否使用HPACK算法进行压缩
如何控制头部是否进入动态映射表
-进入动态表,提供给后续传输使用
-不进入动态表
-不进入动态表,并约定该头部永远都不仅如此动态表
HEADER帧中使用相应的标志位来标识该HEADER是否进入动态映射表
060.服务器端推送消息
服务器端的推送,必须基于一个请求,即服务器不能直接向客户端发起消息推送,而是先接收到一个客户端的请求
例如服务器端请求一个HTML页面,这个HTML页面又依赖了一个CSS文件,那么基于请求的服务器推送是这样实现的
客户端首先向服务器发送一个获取HTML页面的请求,
服务器端在收到这个请求后,新起一个Stream流,开始发送应答帧,其中包含一个PUSH_PROMISE帧,这个帧告诉客户端接下来我要向你推送一个消息,这个帧里包含真正推送消息的那个流的StreamID
服务发出这个响应后,新起一个Stream流,用于传送真正的推送帧(CSS文件)
061.Stream的状态变迁
一条TCP连接上,可以并发存在多个处于OPEN状态的Stream
客户端或者服务器端都可以创建新的Stream
客户端或者服务器端都可以首先关闭Stream
同一条Stream内的Frame是有序的
从StreamID的值可以轻易分辨PUSH消息
HTTP/2协议中消息的格式
一条HTTP Message由1个HEADER(可能含有0个或多个持续帧构成)个0个或多个DATA帧构成
HEADER消息同时包含HTTP/1.1的start line和headers部分
取消HTTP/1.1中的不定长包体Chunk包体
stream流的状态如下,
A1.首先一个stream被创建之后处于idle状态
A2.处于idle状态的stream在收到一个header帧之后,处于open状态
A3.处于open状态的流如果收到一个RST_STRAM帧后就变成close状态
B1.处于OPEN状态的Stream流如果接收到了一个带有END_STREAM标志位的帧,则处于half-closed(remote)状态,处于该状态的Stream不会再接受除了RST_STREAM帧以外的其他任何帧了
B2.处于half-closed(remote)状态的Stream流在发送了一个含有END_STREAM标志位的数据帧或者发送一个RST_STREAM帧或者接收到一个RST_STREAM帧之后都会变成closed状态
C1.处于OPEN状态的Stream流如果发出一个带有END_STREAM标志位的帧,则处于half-closed(local)状态,处于该状态的Stream不会再发送除了RST_STREAM帧以外的其他任何帧了
C2.处于half-closed(local)状态的Stream流在接收到一个含有END_STREAM标志位的帧或者RST_STREAM帧 或者发送了一个RST_STREAM帧之后处于closed状态
062.RST_STREAM帧和其常见的错误码
RST_STREAM用于立刻终止未完成的流
RST_STREAM帧的常见错误码
-NO_ERROR (0x0) 没有错误,GOAWAY帧优雅的关闭连接时可以使用此错误码
-PROTOCAL_ERROR (0x1) 检测到不识别的协议字段,比如两边的版本不匹配时
-INTERNAL_ERRPR (0x2) 内部错误,就是这个错误无法归类的时候
-FLOW_CONTROL_ERROR (0x3) 检测到对方没有遵守流控策略
-SETTINGS-TIMEOUT (0x4) 某些设置帧发出后需要接收端应答,在期待时间内没有得到应答则由此错误码表示
-STREAM_CLOSED (0x5) 当Stream已经处于半关闭状态时不再接受Fream帧时又收到了对端的数据帧时,返回给对端的响应码
-FREAM_SIZE_ERROR (0x6) 收到的帧的大小不合法
-REFUSED_STREAM (0x7) 拒绝先前的Stream流的执行
-CANCEL (0x8) 表示Stream不再存在
-COMPESSION_ERROR (0x9) 对HPACK压缩算法执行失败
-CONNECT_ERROR (0xa) 连接失败
-ENHPACK_YOUR_CALM (0xb) 检测打对端的行为可能导致负载的持续增加,提醒对方“冷静”一点
-INADEQUATE_SECURITY (0xc) 安全等级不够
-HTTP_1_1_REQUIRED (0xd) 对端只能接受HTTP/1.1的请求
063.数据流优先级 Priority设置帧
一个依赖的流ID (Stream Dependency),一个权重,依赖别的stream的stream必须要等待它依赖的stream传输完成了才能开始传送数据
例如在网站的首页HTML(假设这个HTML包含一个CSS文件一个JS文件和张图片),它的StreamID是1 权重是255,那么后续的CSS 的 Stream Dependency 就是1 权重可能是219 而后面的JS Stream Dependency 就是1 权重可能是200
而最后的图片等资源 Stream Dependency 就是1 权重可能是149等等
那么这CSS文件JS文件和图片文件的优先级分别是多少呢: 255/(255+200+149) 200/(255+200+149) 149/(255+200+149),在有流控的情况下,优先级越大的越先传输,所谓优先传输就是优先去阅读内存中对应的buffer然后将数据发送
标志位:如果B和C都依赖A 这时新来了一个stream流D里面包含一个Priority帧,而且这个帧依赖流A,这个帧的标准位是0,那么BCD是同级的
如果D的Priority帧的标志位是1(独占),那么则是B和C依赖D,D依赖A 这样子
064.HTTP/2流量控制
在HTTP/1.1中是由TCP层进行流量控制的
而TCP的流量控制包含两个概念,一个是滑动窗口,一个是拥塞控制
滑动窗口就是一个TCP连接,它一次性可以接受的数据报文的大小
拥塞控制是指,在同一台服务器上建立了许多许多的TCP连接,当连接数过多时就可能会导致连接之间互相影响,从而导致数据传输错误,这时候就会使用到TCP的拥塞控制来避免这种风险
它的原理是:初始化一个最大的TCP连接数,等到服务器运行过程中,逐渐的增加这个最大连接数,直到趋于稳定时确定这个最大连接数,例如服务刚开始时设置的最大连接数可能只有100,在服务运行期间TCP进行监控
发现10个最大连接数没问题,就试图增加这个最大连接数比如20,然后继续监控,一直到达一个最大值
HTTP/2中是在应用层进行的流量控制
原因1 是多个Stream争夺TCP的流控制,互相干扰可能会造成Stream的阻塞,
原因2 是代理服务器内存有限,上下游网速不一致,通过流量来管理内存
-HTTP/2中的流控既可以针对单个Stream,也可以针对整个TCP连接,HTTP/2的流量控制有两个方向,一个是控制一个连接或者一个Stream上传输的数据量大小,还有一个是控制一个TCP连接上并发的最大Stream数
控制一个连接或者一个Stream上传输的数据量大小:
-客户端与服务器都具备流量控制
-单向流控制:发送和接受端独立设定流量大小
-以信用为基础:接收端设定上限,发送端必须按照接收端设定的流量上限来发送数据报文
-流量控制窗口:的初始值时65535字节
-只有Data帧服从流量控制
-流量控制不能被禁用
WINDOW_UPDATE 控制帧 用于告知发送端,接收端能够接受多大的数据量
.窗口范围(即最多能传输多少字节的数据):1 到 2^31 - 1 0是错误的,接收端应返回PROTOCAL_ERROR
.当StreamID为0时表示这个流控作用于整个TCP连接,否则只针对于当前流
.流控只针对一个TCP连接的两端
代理服务器不透传WINDOW_UPDATE设置帧
接收端缩小流控窗口最终会传递到源发送端
.发送端在接收到WINDOW_UPDATE帧后是怎么执行的呢,假设收到的WINDOW_UPDATE帧的窗口范围是200字节,并且分成10个data帧来发送,那么假设先发送了2个data帧消耗掉了40字节,那么后面8个data真最多是能发送160字节的数据(并不一定是每一个data帧传输20字节)
控制一个TCP连接上并发的最大Stream数:
在StreamID为0的SETTINGS_MAX_CONNECURRENT_STREAMS设置帧来控制一个TCP连接上最大的并发Stream数
该设置帧仅限制状态为open或者half-closed状态的流
超出限制后的错误码: PROTOCAL_ERROR REFUSED_STREAM
HTTP2协议
最新推荐文章于 2024-08-22 20:49:20 发布