前言

无论是HTTP/1.*还是HTTP/2,HTTP的基本语义是不变的,比如方法语义(GET/PUST/PUT/DELETE),状态码(200/404/500等),Range Request,Cacheing,Authentication、URL路径。以前纯文本形式作为传输的载体,HTTP/2带来了与之不同的二进制传输语法语义。

下面为HTTP/2消息交换方便笔记。

请求/响应流程

一个典型的HTTP消息包含请求/响应,组成如下:

  • 以响应为例,零或多个HEADERS帧(每一个HEADERS帧可能跟着>=0个CONTINUATION帧,以补充单个HEADERS容量不够的情况)包含状态码为1xx的报文头响应
  • 或者一个HEADERS帧包含了完整的报文头(一般情况下)
  • 零个或多个DATA数据帧包含了具体的消息负载内容
  • 一个HEADERS帧,后面跟随零个或多个包含有报尾(trailer-part)的CONTINUATION帧,作为可选项

注意事项:

  • 一个HEADERS帧携带有END_STREAM标志,后面可跟随有CONTINUATION帧用以补充剩余的包头块
  • 来自于其它流的类型帧是不能够出现在HEADERS帧和CONTINUATION帧中间
  • DATA数据帧不支持分块传输编码(chunked transfer encoding)
  • 报尾字段(Trailing header field)当出现在报头块中时,可以终止当前流
  • HEADERS帧以及关联的CONTINUATION帧只能够出现在一个流的开始或结束时
  • HTTP请求-相应交换消耗一个流:请求准备HEADERS帧打开流,请求的帧包含有END_STREAM标志导致两端流处于半关闭状态,half closed(local/server);响应一个HEADERS帧,若某响应帧包含有END_STREAM标志,流将被关闭
  • 一个HTTP响应完成指的响应帧是包含有END_STREAM标志,在服务器发送并且客户端接收成功。若响应不依赖于客户端的请求,服务器端可以在先于客户端发送请求之前发送完成,之后服务器通过再次发送一个RST_STREAM流(错误代码为NO_ERROR)请求客户端放弃发送请求。这要求客户端在接收到RST_STREAM帧后必须不能够丢弃响应,无论是处于什么谨慎原因。

1. 不支持升级机

HTTP/2多路复用,以及自身也可以通过HTTP/1.1 101切换古来,因此不支持101切换协议(Switching Protocols)机制也是情理之中。

2. HTTP Header Fields

HTTP/2报头字段注意点:

  1. 和HTTP/1.x报头字段一样,都是ASCII字符表示
  2. 字段要求全部小写,"Accept" -> "accept"
  3. 若大写,会被作为不完整数据对待,有被丢弃的风险
  4. 新增伪报头字段,但不属于常规HTTP头部字段,不允许终端自己产生,只允许规范中所定义的5个
    • :method
    • :scheme
    • :authority
    • :path
    • :status
  5. 伪报头字段必须出现在常规HTTP报头字段之前
  6. 连接属性专用字段(Connection-Specific Header Fields)不再被使用(但Transfer-Encoding可以允许出现在请求头中),比如Keep-Alive, Proxy-Connection, Transfer-Encoding和Upgrade等

3. 简单示范

简单图片请求:

模拟一次提交,设置报头大于16KB(一般情况下,报头没有那么大,除非Cookie撑大):

以上示例,可以帮助理解HTTP/1.x和HTTP/2在HTTP语义表述上的不同。

4. 可靠性机制

HTTP/1.1,HTTP客户端无法重试非幂等请求,尤其在错误发生的时候,由于无法检测错误性质这会对重试带来不利的影响。

而HTTP/2在这方面有所增强,提供了两种方式可判断请求是否被完成:

  • GOAWAY帧会携带上流标识符的最大值,低于此值的请求已经被执行过,高于此值的请求帧,则可以再次放心重试
  • 包含有REFUSED_STREAM错误代码的RST_STREAM帧说明当前流早于任何处理发生之前就已经被关闭,因此发生在当前流上的请求可以安全重试。

另外PING帧有利于客户端检测当前连接是否可用,可以理解为心跳保活机制,因为一些网关、负载设备会关闭空闲状态下的连接以节省资源。

服务器推送机制

HTTP/2新增特性,服务器根据客户端一次请求内容主动推送与之相关的请求过去,避免客户端在解析出初次请求页面内容时,再逐一发送资源请求,节省网络资源利用效率。 一些注意事项:

  • 客户端可以通过设置SETTINGS_ENABLE_PUSH为0值通知服务器端禁用推送
  • 承诺请求应该是可缓存、安全,并且不能够携带请求的负载内容,这需要客户端做检测
  • 推送的响应若不可缓存,客户端不能作为HTTP cache存储,这对单独的非浏览器环境特别适合
  • 服务器必须包含一个:authority伪头部字段,标明自身被授权。客户端若检测不到需要作为PROTOCOL_ERROR类型流错误对待
  • 中介设备接收到服务器的推送后,可以决定是否要转发给客户端,中介可以单独选择推送内容发送给客户端。这是一个特别需要注意的点
  • 客户端必须拒绝来自服务器端的对SETTINGS_ENABLE_PUSH属性非0值的修改,也就是说服务器不能要求客户端打开PUSH开关,客户端一旦遇到需要响应PROTOCOL_ERROR类型连接错误
  • 客户端不能够发送推送,PUSH_PROMISE帧只能够来自于服务器端(作为推送请求者发送),否则将会作为PROTOCOL_ERROR类型的连接错误对待
  • PUSH_PROMISE需要包含伪头部:method,若客户端认为不安全,必须响应一个PROTOCOL_ERROR类型流错误
  • 服务器端应该尽可能早的发送PUSH_PROMISE帧,以避免与来自客户端对相同资源的请求两者产生冲突
  • 发送PUSH_PROMISE帧会创建一个新的流,然后处于两端的保留状态,reserved (local/remote)
  • 发送完PUSH_PROMISE帧,服务器需要马上发送具体DATA数据帧
  • 客户端接收完PUSH_PROMISE帧后,选择接收PUSH响应内容,这期间不能触发请求承诺的响应内容,直到承诺流关闭
  • 客户端不需要接收推送内容时,可以选择发送RST_STREAM帧,包含CANCEL/REFUSED_STREAM代码,以及PUSH流标识符发送给服务器端,重置推送流
  • 客户端可以通过设置SETTINGS_MAX_CONCURRENT_STREAMS限制响应数,值为0禁用。但不能阻止服务器发送PUSH_PROMISE帧

比如,服务器接收到来自客户端的请求某个HTML文档资源,该文档包含了若干图片连接,服务器应该优先发送图片数据到客户端,这需要优先发送推送承诺早于包含完整HTML文档内容的DATA帧,这样客户端优先接收到承诺资源,后面接收到DATA数据帧进行解析出图片连接的时候,就避免再次发送图片资源请求嘛。

CONNECT方法

在HTTP原始语义中是没有CONNECT方法的,这个伪方法(pseudo-method)在HTTP/1.x,HTTP代理用作转换HTTP连接通过隧洞方式到远程主机,HTTPS方式交互。 HTTP/2与之类似,伪方法CONNECT被HTTP代理用作在一个单独的HTTP/2流之上建立一个到远程主机的隧道,要求如下:

  • :method=CONNECT
  • ":scheme"和":path"被省略
  • ":authority"字段为代理要连接的远程主机和端口信息

一旦不满足要求,会被视为不完整的需求。

  • 连接成功建立,代理发送给客户端一个2xx的状态码
  • 代理两端在HEADERS帧都发送完毕后,后续的DATA帧开始发送
  • 代理转发客户端发送的DATA数据帧到远程服务器
  • 代理接收到服务器数据组装成DATA数据帧
  • 非DATA类型数据帧,包括流管理类型的RST_STREAM、WINDOW_UPDATE、PRIORITY帧都是不能够在已经连接的流上发送的,否则会被当做流错误对待
  • 客户端接收到包含有END_STREAM标志位的DATA帧时,尽量也要发送一个包含有END_STREAM标志位的DATA帧
  • DATA帧END_STREAM标志位被当做TCP FIN比特标志对待:
    • 代理接收到DATA帧带有END_STREAM标志位,在转发时会设置TCP FIN比特位
    • 代理接收到TCP段包含有FIN比特位设置时,会转发一个DATA帧并携带END_STREAM标志位
    • 最后的TCP段或DATA帧可以为空
  • TCP连接错误以RST_STREAM帧关联
  • 代理对待在TCP连接中出现的错误,包括接收到一个包含有RST比特位的TCP段,作为CONNECT_ERROR类型的流错误抛出
  • 一旦检测到流或HTTP/2连接的错误,代理必须发送一个TCP段并且其RST标志被设置
  • 代理不能仅仅依靠SETTINGS_MAX_CONCURRENT_STREAMS属性值进行限制资源消耗

持久连接和重用

HTTP/2消息交换通过持久连接、重用实现,目的尽可能做到资源利用率最大化。

  1. HTTP/2为持久性连接,基于性能原因,规范建议客户端不要关闭已有连接除非不再需要和服务器保持通信。服务器端要是主动关闭连接的话,在请求量大的情况下,会导致系统出现大量的TIME_WAIT状态TCP,每一个TIME_WAIT状态默认情况下至少持续60秒,特别占用系统资源。因此最佳实践是客户端主动关闭连接,避免Linux服务器端出现TIME_WAIT。
  2. 基于具体主机和端口,客户端应该只打开一个HTTP/2连接
  3. 客户端可以额外创建连接作为替代补充:替换已耗尽可用流标识符,或刷新TLS连接,或替换遇到错误的连接
  4. 当任一端想关闭连接的时候,都应该第一时间发送一个GOAWAY帧到对端,告知对方先前发送的帧已经被处理过,终止之后的一些剩余任务,终止可放心关闭
  5. 有一些情况服务器若不希望客户端重用连接,可返回421 (Misdirected Request) 状态码作为响应,默认可缓存(POST方法或cache-control可控制),但代理不能够为客户端请求生成421状态码。
  6. HTTP代理与每一个服务器之间可以尽可能保持一个持久的连接方便专递客户端的请求;客户端到代理之间可以所有请求共享、重用一个连接

小结

以上为HTTP/2消息交换机制的一些简单梳理,需要注意点:

  1. HTTP/2不允许使用连接特定头部字段
  2. 新增的5个头部
  3. 推送机制的一些特性需求
  4. RST_STREAM等帧标志位的使用

原文 http://www.blogjava.net/yongboy/archive/2015/03/23/423751.html

HTTP/2笔记之消息交换的更多相关文章

  1. WCF学习笔记之消息交换模式

    在WCF通信中,有三种消息交换模式,OneWay(单向模式), Request/Reponse(请求回复模式), Duplex(双工通信模式)这三种通信方式.下面对这三种消息交换模式进行讲解. 1. ...

  2. WCF初探-3:WCF消息交换模式之单向模式

    单向模式(One-Way Calls): 在这种交换模式中,存在着如下的特征: 只有客户端发起请求,服务端并不会对请求进行回复 不能包含ref或者out类型的参数 没有返回值,返回类型只能为void ...

  3. WCF初探-4:WCF消息交换模式之请求与答复模式

    请求与答复模式( Request/Reply) 这种交换模式是使用最多的一中,它有如下特征: 调用服务方法后需要等待服务的消息返回,即便该方法返回 void 类型 相比Duplex来讲,这种模式强调的 ...

  4. Linux进程间通信IPC学习笔记之消息队列(SVR4)

    Linux进程间通信IPC学习笔记之消息队列(SVR4)

  5. WCF消息交换模式之双工通讯(Duplex)

    WCF消息交换模式之双工通讯(Duplex) 双工通讯Duplex具有以下特点: 1它可以在处理完请求之后,通过请求客户端中的回调进行响应操作 2.消息交换过程中,服务端和客户端角色会发生调换 3.服 ...

  6. WCF消息交换模式之请求-响应模式

    WCF的消息交换模式(MEP)有三种:请求/响应.单向模式和双工模式.WCF的默认MEP是请求/响应模式. 请求/响应模式操作签名代码如下,无需指定模式,默认就是. [OperationContrac ...

  7. 简易远程消息交换协议SRMP

    一.SRMP目标定位 经过十多年实战经验积累以及多方共同讨论,新生命团队(https://github.com/newlifex)制订了一种简单而又具有较好扩展性的RPC(Remote Procedu ...

  8. WCF系列教程之消息交换模式之请求与答复模式(Request/Reply)

    1.使用WCF请求与答复模式须知 (1).客户端调用WCF服务端需要等待服务端的返回,即使返回类型是void (2).相比Duplex来讲,这种模式强调的是客户端的被动接受,也就是说客户端接受到响应后 ...

  9. WCF系列教程之WCF消息交换模式之单项模式

    1.使用WCF单项模式须知 (1).WCF服务端接受客户端的请求,但是不会对客户端进行回复 (2).使用单项模式的服务端接口,不能包含ref或者out类型的参数,至于为什么,请参考C# ref与out ...

随机推荐

  1. C语言 · 实现strcmp函数 · 字符串比较

    蓝桥杯练习场上碰到两个此类题了: 算法提高 11-1实现strcmp函数   时间限制:1.0s   内存限制:256.0MB      问题描述 自己实现一个比较字符串大小的函数,也即实现strcm ...

  2. warning: assignment from incompatible pointer type [enabled by default]

    kernel 编译产生这个警告的原因是 不兼容指针类型的赋值 这个原因很有可能是因为返回值和正在接受这个指针类型名不相同. // vim arch/arm/mach-omap2/usb-host.c ...

  3. Python写自己主动化之邮件发送(匿名)

    为了可以实现邮件发送功能.首先.我们须要了解一下邮件的发送过程是什么样子的,此处不再具体说明,请大家自行搜索或查看p=438">http://www.sogouqa.com/?p=43 ...

  4. Entity Framework管理实体关系(二):管理一对二关系

    在上一篇文章中,简单的介绍了使用Fluent API如何管理一对一的实体关系,在这篇文章中,接着介绍Fluent API如何管理一对多的实体关系. 要在数据库中配置一对多关系,我们可以依赖EF约定,还 ...

  5. golang 数组以及slice切片

    老虞学GoLang笔记-数组和切片   数组 Arrays 数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值.在初始化后长度是固定的,无法修改其 ...

  6. jQuery客户端分页

    01 <script src="/js/jquery-1.4.1.js" type="text/javascript"></script> ...

  7. 5G的7位电话号码,去重,内存20mb,代码实现。

    转自:http://www.aboutyun.com/thread-11139-1-1.html 答案:首先,这个题考的不是分布式7位数,至少要用int来保存,那么int为4字节,20MB内存 10^ ...

  8. chrome 常用快捷键(可以摆脱鼠标哦)

      Ctrl+N                                打开新窗口. Ctrl+T                                打开新标签页. Ctrl+Sh ...

  9. Java 构造方法的执行过程(猜测)

    先说明一点,这篇帖子的内容都是我自己思考的结果,如有误,请务必及时告诉我,非常感谢. 起由: public class NewThread implements Runnable{ Thread t; ...

  10. C 字符串操作函数

    针对C风格的字符串(char p[n];): 长度(strlen).追加(strcat, strncat).比较(strcmp, strncmp).查找(strchr, strstr)等. --带n的 ...