sailei1 / blog

1 stars 0 forks source link

http2.0 #90

Closed sailei1 closed 4 years ago

sailei1 commented 4 years ago

由于HTTPS已经在安全方面做的非常好了,所以HTTP/2的唯一目标就是改进性能 头部压缩 由于报文Header一般会携带“User Agent”“Cookie”“Accept”“Server”等许多固定的头字段,多达 几百字节甚至上千字节,但Body却经常只有几十字节(比如GET请求、204/301/304响应),成了不折不扣 的“大头儿子”。更要命的是,成千上万的请求响应报文里有很多字段值都是重复的,非常浪费,“⻓尾效 应”导致大量带宽消耗在了这些冗余度极高的数据上。 所以,HTTP/2把“头部压缩”作为性能改进的一个重点,优化的方式你也肯定能想到,还是“压缩”。 不过HTTP/2并没有使用传统的压缩算法,而是开发了专⻔的“HPACK”算法,在客戶端和服务器两端建 立“字典”,用索引号表示重复的字符串,还釆用哈夫曼编码来压缩整数和字符串,可以达到50%~90%的 高压缩率。

二进制格式 HTTP/2 不再使用肉眼可⻅的ASCII码,而是向下层的TCP/IP协议“靠拢”,全面采用二进制格式。 它把TCP协议的部分特性挪到了应用层,把原来的“Header+Body”的消息“打散”为数个小片的二进 制“帧”(Frame),用“HEADERS”帧存放头数据、“DATA”帧存放实体数据。

虚拟流 HTTP/2为此定义了一个“流”(Stream)的概念,它是二进制帧的双向传输序列,同一个消息往返的帧会 分配一个唯一的流ID。你可以想象把它成是一个虚拟的“数据流”,在里面流动的是一串有先后顺序的数据帧,这些数据帧按照次序组装起来就是HTTP/1里的请求报文和响应报文。 因为“流”是虚拟的,实际上并不存在,所以HTTP/2就可以在一个TCP连接上用“流”同时发送多个“碎片化”的消息,这就是常说的“多路复用”( Multiplexing)多个往返通信都复用一个连接来处理。

强化安全 HTTP/2也增强了安全性,要求至少是TLS1.2,而且禁用了很多不安全的密码套件。

连接前言

由于HTTP/2“事实上”是基于TLS,所以在正式收发数据之前,会有TCP握手和TLS握手 TLS握手成功之后,客戶端必须要发送一个“连接前言”(connection preface),用来确认建立HTTP/2连接。这个“连接前言”是标准的HTTP/1请求报文,使用纯文本的ASCII码格式,请求方法是特别注册的一个关键 字“PRI”,全文只有24个字节

二进制帧 image

流与多路复用

在HTTP/2连接上,虽然帧是乱序收发的,但只要它们都拥有相同的流ID,就都属于一个流,而且在这个流里帧不是无序的,而是有着严格的先后顺序。 请求复用。拆分传输,一个请求头被阻塞,不会影响其他请求

HTTP/2的流有哪些特点呢

  1. 流是可并发的,一个HTTP/2连接上可以同时发出多个流传输数据,也就是并发多请求,实现“多路复用”;
  2. 客戶端和服务器都可以创建流,双方互不干扰;
  3. 流是双向的,一个流里面客戶端和服务器都可以发送或接收数据帧,也就是一个“请求-应答”来回;
  4. 流之间没有固定关系,彼此独立,但流内部的帧是有严格顺序的;
  5. 流可以设置优先级,让服务器优先处理,比如先传HTML/CSS,后传图片,优化用戶体验;
  6. 流ID不能重用,只能顺序递增,客戶端发起的ID是奇数,服务器端发起的ID是偶数;
  7. 在流上发送“RST_STREAM”帧可以随时终止流,取消接收或发送;
  8. 第0号流比较特殊,不能关闭,也不能发送数据帧,只能发送控制帧,用于流量控制。

HTTP/2在一个连接上使用多个流收发数据,那么它本身默认就会是⻓连接,所以永远不需 要“Connection”头字段(keepalive或close)。 下载大文件的时候想取消接收,在HTTP/1里只能断开TCP连接重新“三次握手”,成本很高,而在HTTP/2里就可以简单地发送一个“RST_STREAM”中断流,而⻓连接会继续保持。

因为客戶端和服务器两端都可以创建流,而流ID有奇数偶数和上限的区分,所以大多数的流ID都会 是奇数,而且客戶端在一个连接里最多只能发出2^30,也就是10亿个请求。 ID用完了该怎么办呢?这个时候可以再发一个控制帧“GOAWAY”,真正关闭TCP连接

http/2 做出了明确的规定,要求所有的头字段必须小写,大写会认为格式错误 在rst_steam 合goaway 帧里可以携带32位的错误代码,表示终止流的原因,它是真正的“错误” 但与状态码的含义不同

HTTP/2的优点 1 .完全保持了与HTTP/1的兼容

  1. 一个域名(或者IP)只用一个TCP连接,所有的数据都在这一个连接上传输,这样不仅节约了客戶端、服务器和网络的资源,还可以把带宽跑 满,让TCP使用充分。 缺点
  2. HTTP/2在TCP级别还是存在“队头阻塞”的问题。所以,如果网络连接质量差,发生丢包,那么TCP会等待重传,传输速度就会降低。
    1. 在移动网络中发生IP地址切换的时候,下层的TCP必须重新建连,要再次“握手”,经历“慢启 动”,而且之前连接里积累的HPACK字典也都消失了,必须重头开始计算,导致带宽浪费和时延。 HTTP/2对一个域名只开一个连接,所以一旦这个连接出问题,那么整个网站的体验也就变差 了。
sailei1 commented 4 years ago

HTTP/2虽然使用“帧”“流”“多路复用”,没有了“队头阻塞”,但这些手段都是在应用层 里,而在下层,也就是TCP协议里,还是会发生“队头阻塞”。

在HTTP/2把多个“请求-响应”分解成流,交给TCP后,TCP会再拆 成更小的包依次发送(其实在TCP里应该叫segment,也就是“段”)。 在网络良好的情况下,包可以很快送达目的地。但如果网络质量比较差,像手机上网的时候,就有可能会丢包。而TCP为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,其他的 包即使已经收到了,也只能放在缓冲区里,上层的应用拿不出来,只能“干着急”。 QUIC协议 QUIC就选定了UDP,在它之上把TCP的那一套连接管理、拥塞窗口、流量控制等“搬”了过 来,“去其糟粕,取其精华”,打造出了一个全新的可靠传输协议,可以认为是“新时代的TCP”

QUIC的特点 QUIC基于UDP,而UDP是“无连接”的,根本就不需要“握手”和“挥手”,所以天生就要比TCP快。

就像TCP在IP的基础上实现了可靠传输一样,QUIC也基于UDP实现了可靠传输,保证数据一定能够抵达目的地。它还引入了类似HTTP/2的“流”和“多路复用”,单个“流”是有序的,可能会因为丢包而阻塞, 但其他“流”不会受到影响。

为了防止网络上的中间设备(Middle Box)识别协议的细节,QUIC全面采用加密通信,可以很好地抵御窜 改和“协议僵化”(ossification)

内部细节 QUIC的基本数据传输单位是包(packet)和帧(frame),一个包由多个帧组成,包面向的是“连接”, 帧面向的是“流”。

QUIC使用不透明的“连接ID”来标记通信的两个端点,客戶端和服务器可以自行选择一组ID来标记自己, 这样就解除了TCP里连接对“IP地址+端口”(即常说的四元组)的强绑定,支持“连接迁 移”(Connection Migration)。 image 比如你下班回家,手机会自动由4G切换到WiFi。这时IP地址会发生变化,TCP就必须重新建立连接。而 QUIC连接里的两端连接ID不会变,所以连接在“逻辑上”没有中断,它就可以在新的IP地址上继续使用之前的连接,消除重连的成本,实现连接的无缝迁移。 QUIC的帧里有多种类型,PING、ACK等帧用于管理连接,而STREAM帧专⻔用来实现流。

QUIC里的流与HTTP/2的流非常相似,也是帧的序列,你可以对比着来理解。但HTTP/2里的流都是双向的,而QUIC则分为双向流和单向流。 image

QUIC帧普遍采用变⻓编码,最少只要1个字节,最多有8个字节。流ID的最大可用位数是62,数量上比 HTTP/2的2^31大大增加。 流ID还保留了最低两位用作标志,第1位标记流的发起者,0表示客戶端,1表示服务器;第2位标记流的方 向,0表示双向流,1表示单向流。 所以QUIC流ID的奇偶性质和HTTP/2刚好相反,客戶端的ID是偶数,从0开始计数。

http3.0 HTTP/3里仍然使用流来发送“请求-响应”,但它自身不需要像HTTP/2那样再去定义流,而是直接使用 QUIC的流,相当于做了一个“概念映射”。

HTTP/3里的“双向流”可以完全对应到HTTP/2的流,而“单向流”在HTTP/3里用来实现控制和推送,近 似地对应HTTP/2的0号流。 由于流管理被“下放”到了QUIC,所以HTTP/3里帧的结构也变简单了。 帧头只有两个字段:类型和⻓度,而且同样都采用变⻓编码,最小只需要两个字节。 image

HTTP/3里的帧仍然分成数据帧和控制帧两类,HEADERS帧和DATA帧传输数据,但其他一些帧因为在下层 的QUIC里有了替代,所以在HTTP/3里就都消失了,比如RST_STREAM、WINDOW_UPDATE、PING等。 头部压缩算法在HTTP/3里升级成了“QPACK”,使用方式上也做了改变。虽然也分成静态表和动态表,但 在流上发送HEADERS帧时不能更新字段,只能引用,索引表的更新需要在专⻔的单向流上发送指令来管 理,解决了HPACK的“队头阻塞”问题。 另外,QPACK的字典也做了优化,静态表由之前的61个增加到了98个,而且序号从0开始,也就是 说“:authority”的编号是0。

HTTP/3服务发现

HTTP/3没有指定默认的端口号,也就是说不一定非要在UDP的80或 者443上提供HTTP/3服务。

这就要用到HTTP/2里的“扩展帧”了。浏览器需要先用HTTP/2协议连接服务器,然后服务器可以在启动 HTTP/2连接后发送一个“Alt-Svc”帧,包含一个“h3=host:port”的字符串,告诉浏览器在另一个端点上 提供等价的HTTP/3服务。 浏览器收到“Alt-Svc”帧,会使用QUIC异步连接指定的端口,如果连接成功,就会断开HTTP/2连接,改用 新的HTTP/3收发数据。