HTTP2总结及简单实践总结

简介:
本文讲的是 HTTP2总结及简单实践总结, 在总结http2之前先来回顾下http的发展历史。以下三张图片来自 Jerry Qu

HTTP/0.9 (1991)

HTTP2总结及简单实践

HTTP/1.0 (1996)

HTTP2总结及简单实践

HTTP/1.1 (1999)

HTTP2总结及简单实践

HTTP通信过程

众所周知,http是基于tcp之上的应用层协议,即在tcp连接建立之后,在tcp的链路上传送数据。

HTTP2总结及简单实践

  1. 首先进行TCP连接,三次握手, C --(SYN{k})--> S , S --(ACK{k+1}&SYN{j})--> C , C --ACK{j+1}--> S
  2. 客户端发送ACK后,就会发送一个HTTP请求
  3. 服务端接受到ACK,确认TCP连接建立,再接着收到HTTP请求,进行解析并将结果返回客户端。
  4. 客户端收到HTTP请求结果。

在 HTTP/0.9 和 HTTP/1.0 中,第3步之后,服务端就会关闭连接,也就是TCP的四次挥手,但是在 HTTP/1.1 后,客户端在发送HTTP请求时头部可以带上 Connection:Keep-Alive ,就是告诉服务器保持连接,不要关闭TCP。当 Connection:Close 时,服务器会关闭连接。

HTTP2 的通信过程无外乎这是这个流程,但是通过TCP传输的数据会有不同,客户端和服务器的行为也有了新的规则。引入了Connection、Stream、Message、Frame这四个概念,从下图大概可以看出他们之间的关系。

HTTP2总结及简单实践

  • Connection: 其实就是一个TCP连接
  • Stream:已建立的连接上的双向字节流
  • Message:请求或者响应,由一个或多个帧组合而成
  • Frame: Message中的二进制帧,HTTP/2通信的最小单位,后面会详细解释

HTTP/2 新特性

  • 二进制分帧(Binary framing layer)
  • 多路复用 (Multiplexing)
  • 单一连接(One connection per origin)
  • 数据流优先级(Stream prioritization)
  • 首部压缩(Header Compression)
  • 流控 (Flow control)
  • 服务端推送(Server Push)

这些新特性的产生,主要是为了解决之前的问题,我们来对比下之前的 HTTP/1.1 ,看看解决了哪些问题

二进制分帧(Binary framing layer)

二进制分帧就是把http的数据按照规定的格式进行封装,类似IP和TCP的数据包, 简单画了个承载HTTP2数据的以太帧结构,方便理解。

HTTP2总结及简单实践

通过wireshark抓包可以看到http2的结构

HTTP2总结及简单实践

  • Length: 无符号的自然数,24个比特表示,仅表示帧负载所占用字节数,不包括帧头所占用的9个字节。默认大小区间为为0~16,384(2^14),一旦超过默认最大值2^14(16384),发送方将不再允许发送,除非接收到接收方定义的SETTINGS_MAX_FRAME_SIZE(一般此值区间为2^14 ~ 2^24)值的通知。
  • Type: 8个比特表示,定义了帧负载的具体格式和帧的语义,HTTP/2规范定义了10个帧类型,这里不包括实验类型帧和扩展类型帧
  • Flags: 8个比特表示,服务于具体帧类型,默认值为0x0。有一个小技巧需要注意,一般来讲,8个比特可以容纳8个不同的标志,比如,PADDED值为0x8,二进制表示为00001000;END_HEADERS值为0x4,二进制表示为00000100;END_STREAM值为0X1,二进制为00000001。可以同时在一个字节中传达三种标志位,二进制表示为00001101,即0x13。因此,后面的帧结构中,标志位一般会使用8个比特表示,若某位不确定,使用问号?替代,表示此处可能会被设置标志位
  • R: 在HTTP/2语境下为保留的比特位,固定值为0X0
  • Stream Identifier: 无符号的31比特表示无符号自然数。0x0值表示为帧仅作用于连接,不隶属于单独的流。

HTTP2帧中的类型如下:

HTTP2总结及简单实践

想了解每一个类型的详细数据结构可以参考我的另一篇文章http2帧类型详解

通过Google Developers中的一个图,我们可以更好的理解,HTTP2的分帧在网络数据中所处的位置,以及和HTTP/1.1的不同之处。

HTTP2总结及简单实践

HTTP/1.1中的头部变成HEADERS类型的帧,请求体/回应体变成DATA类型的帧,通过二进制分帧,将传输的数据使用二进制方式,对比文本方式减少数据量;通过不同类型的帧实现流控、服务器推送等功能。

多路复用 (Multiplexing) & 单一连接(One connection per origin)

我们知道在HTTP2之前,我们如果想加快网页资源的加载速度,会采用同时建立多条连接的方式,但是这样每次建立TCP连接效率比较低,并且浏览器往往会限制最大连接数(例如chrome的最大连接数为6)。另外在HTTP/1.1中引入了Pipeline,可以在一个TCP连接中连续发送多个请求,不用关心前面的响应是否到达,但是服务器必须要按照收到请求的顺序来进行响应,这样一旦前面的请求阻塞,后来的请求也将不能及时回应。

HTTP2中,因为新的二进制帧的使用,使得可以轻松复用单个TCP连接。客户端和服务器可以将 HTTP 消息分解为互不依赖的帧,然后交错发送,最后再在另一端把它们重新组装起来。

还是 Google Developers的图:

HTTP2总结及简单实践

可以看到我们可以并行交错的发送多个响应和请求,并且使用同一个TCP连接,没有先后顺序,每个帧中携带有如何组装的信息,客户端会等某项工作所需要的所有的资源都就绪之后再执行。

数据流优先级(Stream prioritization)

由于可以进行单连接复用,服务器和客户端的帧都是交错发送,对于发送给服务器的帧,为了解决哪些该先处理,哪些该后处理,因此引入了数据流的优先级,服务器根据优先级来分配资源。例如优先级高的获得更多的CPU和带宽资源。那么优先级是如何标示的呢?还记得前面的帧类型中有一个Type为PRIORITY,这种类型的帧就是为了告诉服务器这个stream的优先级,此外HEADERS帧中也包含了优先级信息。

HTTP/2通过父依赖和权重来标示优先级,每一个stream会标示一个父stream id,没有标示的默认为虚拟的root stream,这样按照这种依赖关系构建一个依赖树,树上层的stream权重较高,同一层的stream会有一个weight来区分资源分配比。。

HTTP2总结及简单实践

上图是依赖树的一些示例,从左到右,共四棵树。

  • 第一个两个stream A 和 B,没有标明父stream,默认依赖虚拟的root节点,A、B处于同一层,优先级相同,根据权重分配资源,A分到 12/(12+4)=3/4 资源,B分到 1/4 资源。
  • 第二个D和C有层级结构,C的父级是D,那么服务器拿完整资源优先处理D,然后再处理C。
  • 第三个,服务器先处理D,再处理C,然后处理A和B,A分到 3/4 资源,B分到 1/4 资源。
  • 第四个,先处理D,再讲资源对半分处理E和C,之后再按照权重处理A和B

需要注意的一点是,流优先级并不是强制约束,当优先级高的流阻塞时,并不能不让服务器处理优先级低的流

首部压缩 (Header Compression)

由于当前网站内容越来越复杂,单个页面的请求数基本都是几十个甚至上百,每个请求都要带上客户端或者用户的标识,例如:UA,cookie等头部数据,请求数量多了以后,传输http头部消耗的流量也非常可观,并且头部数据中大部分都是相同的,这就是赤裸裸的浪费呀。于是产生了头部压缩技术来节省流量。

HTTP2总结及简单实践

  • 维护一份相同的静态字典(Static Table),包含常见的头部名称,以及特别常见的头部名称与值的组合
  • 维护一份相同的动态字典(Dynamic Table),可以动态地添加内容
  • 支持基于静态哈夫曼码表的哈夫曼编码(Huffman Coding)

静态字典

静态字典就是把常用的头部映射为字节较短的索引序号,如下图所示,截取了前面几个映射,全部定义可以看 Static Table Definition

HTTP2总结及简单实践

例如当头部有个字段是 :method: GET ,那么查表可知,可以用序号2标识,于是这个字段的数据就是 0000010 (2的二进制表示)

动态字典

静态字典能表示的头部数据毕竟有限,压缩率也不会高。但是对于一个站点来讲,和某个用户交互时会发生非常多的请求,但是每次请求头部差别不大,会有很多重复数据,因为用户和浏览器的标识是不变的。那么我们可以针对一次HTTP2的连接生成一个可添加映射的动态字典,这样再后面的连接中就可以使用动态字典中的序号。动态字典的生成过程其实就是通知对方添加映射,客户端可以通知服务端添加,反之亦可。

具体的通知方式就是按照协议规定的格式传输数据。

Huffman Coding

哈弗曼编码的特性是出现频率越高,编码长度越短。HTTP2协议中根据大量的请求头部数据样本生成了一种canonical Huffman code,具体在 Huffman Code 列出。

流控 (Flow control)

HTTP/2 流量控制的目标,在流量窗口初始值的约束下,给予接收端以全权,控制当下想要接受的流量大小。

算法:

  • 两端(收发)保有一个流量控制窗口(window)初始值。
  • 发送端每发送一个DATA帧,就把window递减,递减量为这个帧的大小,要是window小于帧大小,那么这个帧就必须被拆分。如果window等于0,就不能发送任何帧
  • 接收端可以发送 WINDOW_UPDATE帧给发送端,发送端以帧内指定的Window Size Increment作为增量,加到window上

服务端推送 (Server Push)

流程:

  • 客户端在交换 SETTINGS 帧时,设置字段 SETTINGS_ENABLE_PUSH(0x2) 为1显式允许服务器推送
  • 服务器在接受到请求时,分析出要推送的资源,先发个 PUSH_PROMISE 帧给浏览器
  • 然后再发送各个response header和response body
  • 浏览器收到 PUSH_PROMISE 帧时,根据header block fragment字段里的url,可以知道当前有没有缓存,从而判断是否要接收。如果不要,浏览器就要发送个 RST_STREAM 来终止服务器推送

问题:

  • 流量浪费。若浏览器有缓存,不要这个推送,就会出现浪费流量的现象,因为整个过程都是异步的,在服务器接收到RST_STREAM时,响应很有可能部份发出或者全部发出了。

HTTP/2简单实践

Okhttp是一个java生态中有名的的http client,由于其简单易用,性能较好,支持http2。下面用这个工具来实践下,因为本人博客已经在nginx上配置了http2,就拿本博客来实验下。


 
 
  1. public class Http2Example { 
  2.     final static OkHttpClient client = new OkHttpClient.Builder().build(); 
  3.     public static void main(String[] args) { 
  4.         Request request = new Request.Builder() 
  5.                 .url("https://blog.fliaping.com"
  6.                 .build(); 
  7.         try { 
  8.             Response response = client.newCall(request).execute(); 
  9.             System.out.println(JSON.toJSONString(response.protocol())); 
  10.             System.out.println(response.headers().toString()); 
  11.             System.out.println(response.body().string()); 
  12.         } catch (IOException e) { 
  13.             e.printStackTrace(); 
  14.         } 
  15.     } 

用过Okhttp的同学就会发现,这跟平时用的方法一样啊,没有任何区别,是的没错,就是没有任何区别。别的不多说,执行下看看,不幸的是你会发现protocol还是http1.1,并不是h2,这是怎么回事?这是因为HTTP2新加入了ALPN(Application Layer Protocol Negotiation),从字面意思理解就是应用层协议协商,即双方商量下用哪个协议。不幸的是jdk8是在2014年发布的,当时HTTP2协议还没出生,幸运的是通过第三方jar包就可以支持ALPN。另外jdk9已经支持了HTTP2,虽然还没正式发布,但是我们可以试用下JDK 9 Early-Access Builds。

jdk7和jdk8通过添加jvm参数加入第三方alpn支持包,注意版本不能搞错,jdk7使用 alpn-boot-7.*.jar ,jdk8使用 alpn-boot-8.*.jar ,这里有版本对应关系 alpn-versions


 
 
  1. # jdk8 
  2. -Xbootclasspath/p:/home/payne/Downloads/alpn-boot-8.1.11.v20170118.jar 
  3. # jdk7 
  4. -Xbootclasspath/p:/home/payne/Downloads/alpn-boot-7.1.3.v20150130.jar 
  5. # jdk9 
  6. # 使用jdk9平台时,注意okhttp版本大于3.3.0  
  7. # https://mvnrepository.com/artifact/org.mortbay.jetty.alpn/alpn-boot 

本文作者:佚名

来源:51CTO

原文标题:HTTP2总结及简单实践总结
相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
6月前
|
算法 API UED
基于Gin框架的HTTP接口限速实践
基于Gin框架的HTTP接口限速实践
158 0
|
1月前
|
Rust 前端开发 API
Tauri 开发实践 — Tauri HTTP 请求开发
本文介绍了如何在 Tauri 中发起 HTTP 请求。首先通过安装 Tauri 生态中的工具包并配置 `tauri.conf.json` 文件来允许特定域名的 HTTP 通信。接着封装了一个简单的 HTTP 客户端类,并在页面中使用该客户端实现 GET 和 POST 请求。最后提供了完整的源码地址以供参考。此功能使得桌面应用能够与远程服务器进行交互,增强了应用的实用性。
91 1
Tauri 开发实践 — Tauri HTTP 请求开发
|
1月前
|
存储 缓存 NoSQL
保持HTTP会话状态:缓存策略与实践
保持HTTP会话状态:缓存策略与实践
|
1月前
|
存储 JSON API
HTTP 请求与响应处理:C#中的实践
【10月更文挑战第4天】在现代Web开发中,HTTP协议至关重要,无论构建Web应用还是API开发,都需要熟练掌握HTTP请求与响应处理。本文从C#角度出发,介绍HTTP基础知识,包括请求与响应结构,并通过`HttpClient`库演示如何发送GET请求及处理响应,同时分析常见错误并提供解决方案,助你更高效地完成HTTP相关任务。
86 2
|
1月前
|
存储 缓存 监控
HTTP:强缓存优化实践
HTTP强缓存是提升网站性能的关键技术之一。通过精心设计缓存策略,不仅可以显著减少网络延迟,还能降低服务器负载,提升用户体验。实施上述最佳实践,结合持续的监控与调整,能够确保缓存机制高效且稳定地服务于网站性能优化目标。
49 3
|
5月前
|
程序员 API 开发者
Socket与HTTP协议的实践
【6月更文挑战第4天】本文介绍了Python中的网络编程,包括Socket编程和基于HTTP协议的实践。Socket编程是网络通信的基础,Python的`socket`模块简化了其使用。文中展示了服务器和客户端的简单示例,以及如何通过多线程处理多个客户端连接。另外,文章讨论了HTTP协议,推荐了`requests`库,并给出了发送GET和POST请求的例子。最后,总结了Socket编程和HTTP协议在网络编程中的应用及其在Web开发和API交互中的重要性。
67 5
|
6月前
|
前端开发 API UED
AngularJS的$http服务:深入解析与进行HTTP请求的技术实践
【4月更文挑战第28天】AngularJS的$http服务是核心组件,用于发起HTTP请求与服务器通信。$http服务简化了通信过程,通过深入理解和实践,能构建高效、可靠的前端应用。
|
6月前
|
网络协议 Java API
深度剖析:Java网络编程中的TCP/IP与HTTP协议实践
【4月更文挑战第17天】Java网络编程重在TCP/IP和HTTP协议的应用。TCP提供可靠数据传输,通过Socket和ServerSocket实现;HTTP用于Web服务,常借助HttpURLConnection或Apache HttpClient。两者结合,构成网络服务基础。Java有多种高级API和框架(如Netty、Spring Boot)简化开发,助力高效、高并发的网络通信。
141 0
|
6月前
|
数据采集 缓存 监控
HTTP与URL基础解析及简单示例实践
HTTP与URL基础解析及简单示例实践
|
6月前
|
存储 缓存 网络协议
淘宝HTTP3/QUIC技术演进与实践
淘宝HTTP3/QUIC技术演进与实践
327 0
淘宝HTTP3/QUIC技术演进与实践

热门文章

最新文章

下一篇
无影云桌面