java http2_探索HTTP/2: HTTP 2协议简述(原)

探索HTTP/2: HTTP/2协议简述

HTTP/2的协议包含着两个RFC:Hypertext Transfer Protocol Version 2 (RFC7540),即HTTP/2;HPACK: Header Compression for HTTP/2 (RFC7541),即HPACK。RFC7540描述了HTTP/2的语义,RFC7541则描述了用于HTTP/2的头部压缩的格式。本文只涉及HTTP/2协议,本系列的后续文章将会涉及HPACK协议。(2016.10.13最后更新)

1. HTTP/2要解决的问题

HTTP/1.0只允许在一个TCP连接中出现一个请求。后来的HTTP/1.1虽然引入了请求流水线,以允许在一个连接中发送多个请求,但这只是部分地解决了请求并发的问题。服务器端在返回响应时,还是必须要按照它接收到的请求的顺序进行返回。如果排在前面的响应要消耗较长的时间,那依然会对后面的响应的造成阻塞,亦即线头阻塞(Head-of-line blocking)。所以,客户端必须要使用多条连接去发起多个的请求以实现并发,并进而减小延迟。更大的并发会增大服务器的负载,也会占用更大的网络带宽。另外,头部通常会包含有大量的信息,如cookie,而这也会增加网络传输的开销。

HTTP/2允许在同一个TCP连接中交错地出现多个请求与响应,亦即多工(Multiplex)。同时,它使用了一个高效的编码方法对头部进行压缩。HTTP/2还允许对请求进行优先级排序,以便让更为重要的请求得以更快的完成,这会进一步提高性能。HTTP/2还改变了服务器端只能被动地向客户端返回响应的定式,允许服务器端主动地向客户端推送数据,这就可以减少客户端发起请求的数量。

总之,HTTP/2主要是解决性能问题。

2. 发起HTTP/2

HTTP/2会使用与HTTP/1相同的URI scheme,即http和https。而且实现HTTP/2的服务器端也不会使用不同的端口去分别支持HTTP/1和HTTP/2。这样有利于平滑地从HTTP/1升级到HTTP/2。毕竟目前已部署的绝大部分网络服务都只支持HTTP/1,当未来它们升级到HTTP/2时,如果换用了不同URI scheme或端口,那么肯定会对客户端产生极大的影响。但是HTTP/2协议为运行在http和https上的HTTP/2分别定义了两个不同的标识符:h2c和h2。h2c中的"c"指的是cleartext,即明文。本文后面会使用h2c指代运行在http2上(直接使用TCP)的HTTP/2,而用h2指代运行在https上(使用TLS)的HTTP/2。

那么,支持HTTP/2的客户端如何知道它所连接的服务器端是否也支持HTTP/2呢?

对于h2c,支持HTTP/2的客户端可以在发起的请求中使用HTTP/1.1的Upgrade头部去尝试要求服务器升级到HTTP/2。该请求的格式如下:

GET / HTTP/1.1

Host: server.example.com

Connection: Upgrade, HTTP2-Settings

Upgrade: h2c

HTTP2-Settings:

HTTP2-Settings是一个经由BASE64编码过的字符串,其原始内容是客户端将要发送的SETTINGS帧的载荷,即一些配置参数。

如果服务器端支持HTTP/2,它就响应"101 Switching Protocols",表示可以进行升级。该响应的格式如下:

HTTP/1.1 101 Switching Protocols

Connection: Upgrade

Upgrade: h2c

如果服务器端不支持HTTP/2,则会忽略Upgrade请求头部,后续依然使用HTTP/1.1。

对于h2,会使用到协议Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension (RFC7301),即TLS-ALPN。该协议允许客户端和服务器端就使用何种版本的HTTP进行协商。如果TLS-ALPN在现实中运行良好的话,也许某天还会使用该方法去协商使用别的协议。

当客户端与服务器端都同意使用HTTP/2时,双方都需要各自发出一个连接序言(Connection Preface)以进行最后的确认。

客户端在接收到服务器端的"101 Switching Protocols"响应(针对h2c)或TLS连接的第一个应用数据字节(针对h2)之后会立即发出连接序言。该序言的开头是"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"(其十六进制形式为"0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a")(1),后面必须再跟一个SETTINGS帧,哪怕这个帧是空的。

服务器端的连接序言则由一个SETTINGS帧构成,该帧必须是服务器端在HTTP/2连接中发送的第一个帧。这个SETTINGS帧可以为空,也可以包含一些希望客户端如何与自己进行通信的必要配置信息。

3. 帧(Frame)

HTTP/2消息使用二进制格式(实际编码时使用十六进制书写),相比于文本格式,这样可以提高消息处理的效率。HTTP/2消息的最小单元为帧,它由头部与载荷(Payload)组成。每个帧的长度必须是一个或多个8比特位字节(octet,下文将其简写为"字节")。

帧头部依次包含有如下的5个字段:

长度(Length):该字段占用24个比特位,代表帧载荷的长度。该长度是一个24位的无符号整数。

类型(Type):该字段占用8个比特位,代表帧的类型。

标志(Flags):该字段占用8个比特位,代表帧所定义的一个或多个标志。并不是所有的帧都定义了标志。

保留位(R):该字段占用1个比特位,其语义尚未被定义。在读取帧时,该位需要被忽略;但在发送帧时,该位需要保持为0(0x0)。

流标识符(Stream Identifier):该字段占用31个比特位,代表该帧所在流的标识符。

在头部之后,紧接着的就是载荷。载荷的结构与内容完全由帧的类型决定,它的长度也是不定的。     HTTP/2定义了如下10种不同类型的帧。

DATA:用于携带一组长度不定的字节。一个或多个DATA可作为请求或响应的载荷。

HEADERS:用于开启一个流,并可携带一个头部块片断。头部块指由一个HEADERS/PUSH_PROMISE帧和紧随它的零到多个CONTINUATION帧组成的集合,因为只有它们才可能携带头部信息。这个集合可被分割成一个或一组字节,这样的字节被称为头部块片断。头部块中各个特定类型的帧必须紧紧相邻,不能出现其它类型的帧。

PRIORITY:用于指定发送端建议的流优先级。

RST_STREAM:用于立即终止流。当希望取消一个流或发生错误时,就可发送RST_STREAM帧。

SETTINGS:用于携带可以影响两端之间通信方式的配置参数。SETTINGS帧定义了一个ACK标志,用于指示该帧所设置的参数是否已被接收端获知。当收到一个SETTINGS且其中的ACK标志为0时,接收端必须尽可能快的应用其中已被更新的参数。

PUSH_PROMISE:用于向接收端通知发送端将要创建的流。当接收端接收到该帧时,新的流尚未被发送端创建,但发送端承诺会创建该流。该帧用于实现HTTP/2的重要特性"服务器端推送(Server Push)"。

PING:用于测量发送端与接收端之间的最小往返时间。这与使用众所周知的ping命令的目的相似,是为了测试某个空闲的连接是否还可用。

GOAWAY:用于发起对连接的关闭,或触发严重的错误条件。该帧允许一端,在完成对之前已创建的流的处理的同时,优雅地停止接收新的流。一端在创建新的流,另一端在发送GOAWAY,这两者之间天然存在着竞争关系。为了就对这种情况,发送端在发送GOAWAY时会让它携带上(该发送端所知晓的)接收端最后创建的流的标识符,当该GOAWAY被发送之后,发送端将会忽视由接收端创建的任何一个标识符比该标识符大的流。

WINDOW_UPDATE:用于流量控制。该帧的载荷由一个单比特保留位和一个31比特位的无符号整数组成。该整数向该帧的接收端指示了其向当前流量控制窗口所能增加传输量的值。

CONTINUATION:用于继续发送头部块片断。只要同一个流中前面的帧是HEADERS,PUSH_PROMISE或CONTINUATION,并且该帧没有设置END_HEADERS标志,那么可无限量地发送CONTINUATION帧。

部分帧,DATA,HEADERS和PUSH_PROMISE,的载荷中可能包含填白(Padding)。填白在业务上没有实际的用处,它的出现是基于安全目的。比如,可以用它来扰乱实际数据的长度,以减轻特定的HTTP攻击。发送端发送的帧的最大长度要尊重接收端设定的SETTINGS_MAX_FRAME_SIZE的值。但该值的范围要介于2^14至2^24-1个字节之间。

4. 流(Stream)

流是用于在客户端与服务器端之间进行帧传送的通道,同一个TCP连接中可以同时有多个流,如下图所示,

┌────────┐          Connection           ┌────────┐

│        │ ============================= │        │

│        │    ---------------------

│        │    ┌─────┐┌─────────┐┌─┐      │        │

│        │    └─────┘└─────────┘└─┘

│        │    ---------------------      │        │

│ Client │                               │ Server │

│        │    ----------                 │        │

│        │    ┌──┐┌────┐                 │        │

│        │    └──┘└────┘                 │        │

│        │    ----------                 │        │

│        │ ============================= │        │

└────────┘                               └────────┘

服务器端和客户端可以交错地向同一个连接中的不同流中传送帧。可以把一个流看作HTTP/1中的一个连接。客户端与服务器端在同一个流中的交互依然遵循发送请求-等待响应模式。两端都可以创建新的流,共享对方创建的流,也可以关闭对方创建的流。帧在流中的顺序是有意义的,接收端会以接收到的顺序去处理帧。

每个流都有一个标识符,是一个31比特位的无符合整数。在同一个连接中,流标识符是唯一的。由客户端创建的流的标识符为奇数,由服务器创建的流的标识符为偶数。但标识符为0的流可看作连接,用于连接控制信息,创建新的流时不可使用该标识符。同一个连接中的任何一个流的标识符都不可重用,即便这个流已被关闭了。对于长时间没有中断的连接,可能会出现标识符不够用的情况,那时就必须强制创建一个新的连接。     HTTP/2协议为流的生命周期定义了7种状态(2):idle,reserved(local),reserved(remote),open,half closed(local),half closed(remote)和closed。当一端接收或发送头部块或(帧DATA和HEADERS的)标志RST_STREAM后可使流的状态发生转变。

使用流来实现多工就会引起对TCP连接使用的竞争,这会造成流的阻塞。基于帧WINDOW_UPDATE的流量控制方案可以确保相同连接中的流相互之间不会产生破坏性干扰。流量控制可以作用于两个层面,即单个流或整个连接。只有帧DATA需要遵守流量控制,所有其它的帧所有消耗的空间均不会占用流量控制窗口。HTTP/2协议只是定义了WINDOW_UPDATE帧的结构和语义,协议的实现可以选择任何适用自己的流量控制算法。

流可以有优先级。客户端在创建一个新的流时,可在HEADERS中指定优先级权重。在后续任何时间,通过PRIORITY可以改变流的优先级权重。在并发能力有限的情况下,高权重流的帧会被优先传送。权重的值必须介于1至256之间,默认权重为16。流与流之间还可以有依赖关系,这种关系会组成一棵依赖关系树。一个流能够指定自己成为另一个流的子流。这一过程,可以是非排他的,也可以是排他的。非排他性依赖,是指一个流在将自己变成另一个流的子流的过程中,允许另一个流还有别的子流,即允许有自己的兄弟流存在。排他性依赖,指在前述过程中,不允许另一个流还有别的子流。如果另一个流已经有子流了,那么该流会把所有潜在的兄弟流先变成自己的子流,然后再使自己成为另一个流的唯一子流。其实,排他性依赖的作用就是为了能够打破已有的关系树,在既成的父子节点中插入新的节点。否则,只能为已有节点添加子节点,那么关系树将不可能进行重构。所有的流在被创建时,默认成为标识符为0x0的流的子流。在"服务器端推送"中生成的"推送"流将自动地成为生成该推送流的流的子流,其默认权重也为16。

5. 消息交换

5.1 请求/响应交换

HTTP/2沿袭了HTTP/1的语义,即所有的请求与响应语义均得到了保留,尽管传递这些语义的语法已经改变了。

一个HTTP/2消息由如下几个部分组成:

[1]仅对于响应消息,可以包含一个携带有1xx响应头部的头部块。该头部块由一个HEADERS帧和紧随它的零到多个CONTINUATION帧组成。

[2]一个头部块。该头部块由一个HEADERS帧和紧随它的零到多个CONTINUATION帧组成。

[3]零到多个携带有体部(Body)消息的DATA帧。HTTP/1中使用的"分块(chunked)"体部将不适用于HTTP/2。因为一个体部可由多个DATA帧组成,所以HTTP/2的体部天然就是可分块的。

[4]一个可能存在的包含着尾部消息的头部块。该头部块由一个HEADERS帧和紧随它的零到多个CONTINUATION帧组成。

HTTP/2仍然沿用HTTP/1中的头部字段,但字段名称中的字母必须全部为小写。另外,还将HTTP/1消息开始行(请求中的请求行与响应中的状态行)中的消息,分解成了若干伪头部字段,此类字段均以冒号(:)开头。

HTTP/1请求行格式为"method request-target HTTP-version",对应的HTTP/2伪头部字段有:method=method和:path=request-target,但HTTP-version无对应字段,默认为HTTP/2。

HTTP/1状态行格式为"HTTP-version status-code reason-phrase",对应的HTTP/2伪头部字段有:status=status-code。但HTTP-version无对应字段,默认为HTTP/2;reason-phrase也无对应字段,因为可以通过状态代码查找到其对应的reason-phrase。HTTP/2协议是在尽量减少冗余消息。

HTTP/2协议还为请求头部定义了另外两个伪字段:

:scheme:URI中的scheme部分。它可以不仅仅是http或https,因为有时候可能会与非HTTP服务进行交互。

:authority:URI中的授权部分。即,scheme://user:password@host:port/path?query#fragment中的"user:password@host:port"。

HTTP/2协议8.1.3节中给出一些简单示例,展示了如何将HTTP/1消息对应到HTTP/2消息。

5.2 服务器端推送

HTTP/2的服务器端推送是传统的请求/响应模式的一种特殊形式。服务器端在收到客户端的请求(主请求)之后,为了主动向客户端推送更多的内容,会自动地生成若干新的请求(推送请求)。服务器向客户端发送的响应中,不仅包含对主请求的响应(主响应),还包含对推送请求的响应(推送响应)。

客户端可以通过发送包含有SETTINGS_MAX_CONCURRENT_STREAMS参数的SETTINGS帧去禁用服务器端推送,也可以通过发送RST_STREAM帧去取消已经发起的服务器端推送,但不能发送包含有END_STREAM标志的帧。

(1)"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"中的"PRI"与"SM"合起来就是"RRISM(棱镜)"。呵呵,HTTPbis工作组这是想表达什么意思呢 ;-)

(2)本系列的后续文章解读了流的状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/388812.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

错误处理

错误处理: 许多系统调用和函数在失败后,会在失败时设置外部变量errno的值来指明失败原因。许多不同的函数库都把这个变量作为报告错误的标准方法。程序必须在函数报告出错后立刻检查errno变量,因为它可能被下一个函数调用所覆盖&#xff…

Android类库介绍

Android类库介绍 GPhone开发包Android SDK含了很多丰富的类库: android.util 涉及系统底层的辅助类库 android.os 提供了系统服务、消息传输、IPC管道 android.graphics GPhone图形库,包含了文本显示、输入输出、文字样式 android.database 包含底层的AP…

递归函数基例和链条_链条和叉子

递归函数基例和链条因果推论 (Causal Inference) This is the fifth post on the series we work our way through “Causal Inference In Statistics” a nice Primer co-authored by Judea Pearl himself.这是本系列的第五篇文章,我们通过“因果统计推断”一书进行…

前端技能拾遗

本文主要是对自己前端知识遗漏点的总结和归纳,希望对大家有用,会持续更新的~ 解释语言和编译型语言 解释型语言与编译型语言的区别翻译时间的不同。 编译型语言在程序执行之前,有一个单独的编译过程,将程序翻译成机器语言&#xf…

java lock 信号_java各种锁(ReentrantLock,Semaphore,CountDownLatch)的实现原理

先放结论:主要是实现AbstractQueuedSynchronizer中进入和退出函数,控制不同的进入和退出条件,实现适用于各种场景下的锁。JAVA中对于线程的同步提供了多种锁机制,比较著名的有可重入锁ReentrantLock,信号量机制Semapho…

Intent.ACTION_MAIN

1 Intent.ACTION_MAIN String: android.intent.action.MAIN 标识Activity为一个程序的开始。比较常用。 Input:nothing Output:nothing 例如&#xff1a; 1 <activity android:name".Main"android:label"string/app_name">2 <intent-filter…

足球预测_预测足球热

足球预测By Aditya Pethe通过阿蒂亚皮特(Aditya Pethe) From September to January every year, football takes over America. Games dominate TV Sunday and Monday nights, and my brother tears his hair out each week over his consistently underperforming fantasy te…

C#的特性Attribute

一、什么是特性 特性是用于在运行时传递程序中各种元素&#xff08;比如类、方法、结构、枚举、组件等&#xff09;的行为信息的声明性标签&#xff0c;这个标签可以有多个。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号&am…

java 技能鉴定_JAVA试题-技能鉴定

一、单选题1.以下创建了几个对象( B)String A,B,CA"a";B"b":AAB;StringBuffer Dnew StringBuffer("abc");DD.append("567");A.6B.4C.3D.52.关于以下程序段&#xff0c;正确的说法是( C )1&#xff0e;String s1“a”“b”;2&#xff0…

ADD_SHORTCUT_ACTION

String ADD_SHORTCUT_ACTION 动作&#xff1a;在系统中添加一个快捷方式。. “android.intent.action.ADD_SHORTCUT”   String ALL_APPS_ACTION 动作&#xff1a;列举所有可用的应用。   输入&#xff1a;无。 “android.intent.action.ALL_APPS”   String ALTERNATIVE…

python3中朴素贝叶斯_贝叶斯统计:Python中从零开始的都会都市

python3中朴素贝叶斯你在这里 (You are here) If you’re reading this, odds are: (1) you’re interested in bayesian statistics but (2) you have no idea how Markov Chain Monte Carlo (MCMC) sampling methods work, and (3) you realize that all but the simplest, t…

java映射的概念_Java 反射 概念理解

文章来源:http://hollischuang.gitee.io/tobetopjavaer/#/basics/java-basic/reflection反射反射机制指的是程序在运行时能够获取自身的信息。在java中&#xff0c;只要给定类的名字&#xff0c;那么就可以通过反射机制来获得类的所有属性和方法。反射有什么作用在运行时判断任…

【转载】移动端布局概念总结

布局准备工作及布局思想及概念: 一个显示器&#xff08;pc端显示器 及 手机屏显示器&#xff09;&#xff0c;既有物理像素&#xff0c;又有独立像素&#xff08;独立像素也叫作css像素&#xff0c;用于前端人员使用&#xff09;&#xff1b; -->重要 首先确定设计稿的尺寸…

深入浅出:HTTP/2

上篇文章深入浅出&#xff1a;5G和HTTP里给自己挖了一根深坑&#xff0c;说是要写一篇关于HTTP/2的文章&#xff0c;今天来还账了。 本文分为以下几个部分&#xff1a; HTTP/2的背景HTTP/2的特点HTTP/2的协议分析HTTP/2的支持 HTTP/2简介 HTTP/2主要是为了解决现HTTP 1.1性能不…

画了个Android

画了个Android 今晚瞎折腾&#xff0c;闲着没事画了个机器人——android&#xff0c;浪费了一个晚上的时间。画这丫还真不容易&#xff0c;为那些坐标&#xff0c;差点砸了键盘&#xff0c;好在最后画出个有模有样的&#xff0c;心稍安。 下面来看看画这么个机器人需要些什么东…

数据治理 主数据 元数据_我们对数据治理的误解

数据治理 主数据 元数据Data governance is top of mind for many of my customers, particularly in light of GDPR, CCPA, COVID-19, and any number of other acronyms that speak to the increasing importance of data management when it comes to protecting user data.…

mysql 选择前4个_mysql从4个表中选择

不要认为GROUP BY是必需的 . 虽然如果一个孩子有2个父记录&#xff0c;你可能想用它来将2个父母分组到一行 - 但不确定这是否是你的要求 . 因为如果一个孩子有2个父母&#xff0c;那么将为该孩子返回的父母是未定义的 .假设所有孩子都有父母&#xff0c;所有父母都会有姓&#…

提高机器学习质量的想法_如何提高机器学习的数据质量?

提高机器学习质量的想法The ultimate goal of every data scientist or Machine Learning evangelist is to create a better model with higher predictive accuracy. However, in the pursuit of fine-tuning hyperparameters or improving modeling algorithms, data might …

mysql 集群实践_MySQL Cluster集群探索与实践

MySQL集群是一种在无共享架构(SNA&#xff0c;Share Nothing Architecture)系统里应用内存数据库集群的技术。这种无共享的架构可以使得系统使用低廉的硬件获取高的可扩展性。MySQL集群是一种分布式设计&#xff0c;目标是要达到没有任何单点故障点。因此&#xff0c;任何组成部…

Python基础:搭建开发环境(1)

1.Python语言简介 2.Python环境 Python环境产品存在多个。 2.1 CPython CPython是Python官方提供的。一般情况下提到的Python就是指CPython&#xff0c;CPython是基于C语言编写的。 CPython实现的解释器将源代码编译为字节码&#xff08;ByteCode&#xff09;&#xff0c;再由虚…