百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

HTTP 2.0 原理(http基本原理)

csdh11 2025-03-23 18:35 13 浏览

架构师-网络文章汇总

HTTP 2.0是在SPDY(An experimental protocol for a faster web, The Chromium Projects)基础上形成的下一代互联网通信协议。HTTP/2 的目的是通过支持请求与响应的多路复用来较少延迟,通过压缩HTTPS首部字段将协议开销降低,同时增加请求优先级和服务器端推送的支持。

本文目的是学习HTTP 2.0的原理并研究其通信的详细细节。

1. 二进制分帧层

二进制分帧层,是HTTP 2.0性能增强的核心。
HTTP 1.x在应用层以纯文本的形式进行通信,而HTTP 2.0将所有的传输信息分割为更小的消息和帧,并对它们采用二进制格式编码。这样,客户端和服务端都需要引入新的二进制编码和解码的机制。

如下图所示,HTTP 2.0并没有改变HTTP 1.x的语义,只是在应用层使用二进制分帧方式传输。


因此,也引入了新的通信单位:

1.1 帧(frame)

HTTP 2.0通信的最小单位,包括帧首部、流标识符、优先值和帧净荷等。

其中,帧类型又可以分为:

  • DATA:用于传输HTTP消息体;
  • HEADERS:用于传输首部字段;
  • SETTINGS:用于约定客户端和服务端的配置数据。比如设置初识的双向流量控制窗口大小;
  • WINDOW_UPDATE:用于调整个别流或个别连接的流量
  • PRIORITY: 用于指定或重新指定引用资源的优先级。
  • RST_STREAM: 用于通知流的非正常终止。
  • PUSH_ PROMISE: 服务端推送许可。
  • PING: 用于计算往返时间,执行“ 活性” 检活。
  • GOAWAY: 用于通知对端停止在当前连接中创建流。

标志位用于不同的帧类型定义特定的消息标志。比如DATA帧就可以使用End Stream: true表示该条消息通信完毕。流标识位表示帧所属的流ID。优先值用于HEADERS帧,表示请求优先级。R表示保留位。
下面是Wireshark抓包的一个DATA帧:

1.2 消息(message)

消息是指逻辑上的HTTP消息(请求/响应)。一系列数据帧组成了一个完整的消息。比如一系列DATA帧和一个HEADERS帧组成了请求消息。

1.3 流(stream)

流是连接中的一个虚拟信道,可以承载双向消息传输。每个流有唯一整数标识符。为了防止两端流ID冲突,客户端发起的流具有奇数ID,服务器端发起的流具有偶数ID。
所有HTTP 2. 0 通信都在一个TCP连接上完成, 这个连接可以承载任意数量的双向数据流Stream。 相应地, 每个数据流以 消息的形式发送, 而消息由一 或多个帧组成,
这些帧可以乱序发送, 然后根据每个帧首部的流标识符重新组装。


二进制分帧层保留了HTTP的语义不受影响,包括首部、方法等,在应用层来看,和HTTP 1.x没有差别。同时,所有同主机的通信能够
在一个TCP连接上完成。

2. 多路复用共享连接

基于二进制分帧层,HTTP 2.0可以在共享TCP连接的基础上,同时发送请求和响应。HTTP消息被分解为独立的帧,而不破坏消息本身的语义,交错发送出去,最后在另一端根据流ID和首部将它们重新组合起来。
我们来对比下HTTP 1.x和HTTP 2.0,假设不考虑1.x的pipeline机制,双方四层都是一个TCP连接。客户端向服务度发起三个图片请求/image1.jpg,/image2.jpg,/image3.jpg。
HTTP 1.x发起请求是串行的,image1返回后才能再发起image2,image2返回后才能再发起image3。


HTTP 2.0建立一条TCP连接后,并行传输着3个数据流,客户端向服务端乱序发送stream1~3的一系列的DATA帧,与此同时,服务端已经在返回stream 1的DATA帧


性能对比,高下立见。HTTP 2.0成功解决了HTTP 1.x的
首阻塞问题(TCP层的阻塞仍无法解决),同时,也不需要通过pipeline机制多条TCP连接来实现并行请求与响应。减少了TCP连接数对服务器性能也有很大的提升。

3. 请求优先级

流可以带有一个31bit的优先级:

  • 0:表示最高优先级
  • 231-1:表示最低优先级

客户端明确指定优先级,服务端可以根据这个优先级作为依据交互数据,比如客户端优先级设置为.css>.js>.jpg(具体可参见《高性能网站建设指南》), 服务端按优先级返回结果有利于高效利用底层连接,提高用户体验。
然而,也不能过分迷信请求优先级,仍然要注意以下问题:

  • 服务端是否支持请求优先级
  • 会否引起队首阻塞问题,比如高优先级的慢响应请求会阻塞其他资源的交互。

4. 服务端推送

HTTP 2.0增加了服务端推送功能,服务端可以根据客户端的请求,提前返回多个响应,推送额外的资源给客户端。如下图所示,客户端请求stream 1,/page.html。服务端在返回stream 1消息的同时推送了stream 2(/script.js)和stream 4(/style.css)。


PUSH_PROMISE帧是服务端向客户端有意推送资源的信号。

  • 如果客户端不需要服务端Push,可在SETTINGS帧中设定服务端流的值为0,禁用此功能
  • PUSH_PROMISE帧中只包含预推送资源的首部。如果客户端对PUSH_PROMISE帧没有意见,服务端在PUSH_PROMISE帧后发送响应的DATA帧开始推送资源。如果客户端已经缓存该资源,不需要再推送,可以选择拒绝PUSH_PROMISE帧。
  • PUSH_PROMISE必须遵循请求-响应原则,只能借着对请求的响应推送资源。
    目前,Apache的mod_http2能够开启 H2Push on服务端推送Push。Nginx的ngx_http_v2_module还不支持服务端Push。
Apache mod_headers example

    Header add Link "</css/site.css>;rel=preload"
    Header add Link "</images/logo.jpg>;rel=preload"
12345

5. 首部压缩

HTTP 1.x每一次通信(请求/响应)都会携带首部信息用于描述资源属性。HTTP 2.0在客户端和服务端之间使用“首部表”来跟踪和存储之前发送的键-值对。首部表在连接过程中始终存在,新增的键-值对会更新到表尾,因此,不需要每次通信都需要再携带首部。


另外,HTTP 2.0使用了首部压缩技术,压缩算法使用HPACK。可让报头更紧凑,更快速传输,有利于移动网络环境。
需要注意的是,HTTP 2.0关注的是首部压缩,而我们常用的gzip等是报文内容(body)的压缩。二者不仅不冲突,且能够一起达到更好的压缩效果。

6. 一个完整的HTTP 2.0通信过程

考虑一个问题,客户端如何知道服务端是否支持HTTP 2.0?是否支持对二进制分帧层的编码和解码?所以,在两端使用HTTP 2.0通信之前,必然存在协议协商的过程。

6.1 基于ALPN的协商过程

支持HTTP 2.0的浏览器可以在TLS会话层自发完成和服务端的协议协商以确定是否使用HTTP 2.0通信。其原理是TLS 1.2中引入了扩展字段,以允许协议的扩展,其中ALPN协议(Application Layer Protocol Negotiation, 应用层协议协商, 前身是NPN)用于客户端和服务端的协议协商过程。
服务端使用ALPN,监听443端口默认提高HTTP 1.1,并允许协商其他协议,比如SPDY和HTTP 2.0。
比如,客户端在TLS握手Client Hello阶段表明自身支持HTTP 2.0


服务端收到后,响应Server Hello,表示自己也支持HTTP 2.0。双方开始HTTP 2.0通信。

6.2 基于HTTP的协商过程

然而,HTTP 2.0一定是HTTPS(TLS 1.2)的特权吗?
当然不是,客户端使用HTTP也可以开启HTTP 2.0通信。只不过因为HTTP 1. 0和HTTP 2. 0都使用同一个 端口(80), 又没有服务器是否支持HTTP 2. 0的其他任何 信息,此时 客户端只能使用HTTP Upgrade机制(OkHttp, nghttp2等组件均可实现,也可以自己编码完成)通过协调确定适当的协议:

HTTP Upgrade request
GET / HTTP/1.1
host: nghttp2.org
connection: Upgrade, HTTP2-Settings
upgrade: h2c        /*发起带有HTTP2.0 Upgrade头部的请求*/       
http2-settings: AAMAAABkAAQAAP__   /*客户端SETTINGS净荷*/
user-agent: nghttp2/1.9.0-DEV

HTTP Upgrade response    
HTTP/1.1 101 Switching Protocols   /*服务端同意升级到HTTP 2.0*/
Connection: Upgrade
Upgrade: h2c

HTTP Upgrade success               /*协商完成*/1234567891011121314

6.3 完整通信过程

TCP连接建立:


TLS握手和HTTP 2.0通信过程:


另外,在chrome中通过
chrome://net-internals/#http2命令也能捕获HTTP 2.0通信过程:

42072: HTTP2_SESSION
textlink.simba.taobao.com:443 (PROXY 10.19.110.55:8080)
Start Time: 2017-04-05 11:39:11.459

t=370225 [st=    0] +HTTP2_SESSION  [dt=32475+]
                     --> host = "textlink.simba.taobao.com:443"
                     --> proxy = "PROXY 10.19.110.55:8080"
t=370225 [st=    0]    HTTP2_SESSION_INITIALIZED
                       --> protocol = "h2"
                       --> source_dependency = 42027 (PROXY_CLIENT_SOCKET_WRAPPER)
t=370225 [st=    0]    HTTP2_SESSION_SEND_SETTINGS
                       --> settings = ["[id:3 flags:0 value:1000]","[id:4 flags:0 value:6291456]","[id:1 flags:0 value:65536]"]
t=370225 [st=    0]    HTTP2_STREAM_UPDATE_RECV_WINDOW
                       --> delta = 15663105
                       --> window_size = 15728640
t=370225 [st=    0]    HTTP2_SESSION_SENT_WINDOW_UPDATE_FRAME
                       --> delta = 15663105
                       --> stream_id = 0
t=370225 [st=    0]    HTTP2_SESSION_SEND_HEADERS
                       --> exclusive = true
                       --> fin = true
                       --> has_priority = true
                       --> :method: GET
                           :authority: textlink.simba.taobao.com
                           :scheme: https
                           :path: /?name=tbhs&cna=IAj9EOy3fngCAXBQ5kJ9yusH&nn=&count=13&pid=430266_1006&_ksTS=1491363551394_94&callback=jsonp95
                           user-agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
                           accept: */*
                           referer: https://www.taobao.com/
                           accept-encoding: gzip, deflate, sdch, br
                           accept-language: zh-CN,zh;q=0.8
                           cookie: [382 bytes were stripped]
                       --> parent_stream_id = 0
                       --> stream_id = 1
                       --> weight = 147
t=370256 [st=   31]    HTTP2_SESSION_RECV_SETTINGS
                       --> host = "textlink.simba.taobao.com:443"
t=370256 [st=   31]    HTTP2_SESSION_RECV_SETTING
                       --> flags = 0
                       --> id = 3
                       --> value = 128
t=370256 [st=   31]    HTTP2_SESSION_UPDATE_STREAMS_SEND_WINDOW_SIZE
                       --> delta_window_size = 2147418112
t=370256 [st=   31]    HTTP2_SESSION_RECV_SETTING
                       --> flags = 0
                       --> id = 4
                       --> value = 2147483647
t=370256 [st=   31]    HTTP2_SESSION_RECV_SETTING
                       --> flags = 0
                       --> id = 5
                       --> value = 16777215
t=370256 [st=   31]    HTTP2_SESSION_RECEIVED_WINDOW_UPDATE_FRAME
                       --> delta = 2147418112
                       --> stream_id = 0
t=370256 [st=   31]    HTTP2_SESSION_UPDATE_SEND_WINDOW
                       --> delta = 2147418112
                       --> window_size = 2147483647
t=370261 [st=   36]    HTTP2_SESSION_RECV_HEADERS
                       --> fin = false
                       --> :status: 200
                           date: Wed, 05 Apr 2017 03:39:11 GMT
                           content-type: text/html; charset=ISO-8859-1
                           vary: Accept-Encoding
                           server: Tengine
                           expires: Wed, 05 Apr 2017 03:39:11 GMT
                           cache-control: max-age=0
                           strict-transport-security: max-age=0
                           timing-allow-origin: *
                           content-encoding: gzip
                       --> stream_id = 1
t=370261 [st=   36]    HTTP2_SESSION_RECV_DATA
                       --> fin = false
                       --> size = 58
                       --> stream_id = 1
t=370261 [st=   36]    HTTP2_SESSION_UPDATE_RECV_WINDOW
                       --> delta = -58
                       --> window_size = 15728582
t=370261 [st=   36]    HTTP2_SESSION_RECV_DATA
                       --> fin = true
                       --> size = 0
                       --> stream_id = 1
t=370295 [st=   70]    HTTP2_STREAM_UPDATE_RECV_WINDOW
                       --> delta = 58
                       --> window_size = 15728640
t=402700 [st=32475] 

备注:

这篇文章摘抄来自网络。我打算总结一些列架构师需要的优秀文章,由于自己写会花太多时间,我决定做一个搬运工,为大家筛选优秀的文章,最后我会做成索引方便大家查找。

相关推荐

探索Java项目中日志系统最佳实践:从入门到精通

探索Java项目中日志系统最佳实践:从入门到精通在现代软件开发中,日志系统如同一位默默无闻却至关重要的管家,它记录了程序运行中的各种事件,为我们排查问题、监控性能和优化系统提供了宝贵的依据。在Java...

用了这么多年的java日志框架,你真的弄懂了吗?

在项目开发过程中,有一个必不可少的环节就是记录日志,相信只要是个程序员都用过,可是咱们自问下,用了这么多年的日志框架,你确定自己真弄懂了日志框架的来龙去脉嘛?下面笔者就详细聊聊java中常用日志框架的...

物理老师教你学Java语言(中篇)(物理专业学编程)

第四章物质的基本结构——类与对象...

一文搞定!Spring Boot3 定时任务操作全攻略

各位互联网大厂的后端开发小伙伴们,在使用SpringBoot3开发项目时,你是否遇到过定时任务实现的难题呢?比如任务调度时间不准确,代码报错却找不到方向,是不是特别头疼?如今,随着互联网业务规模...

你还不懂java的日志系统吗 ?(java的日志类)

一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...

谈谈枚举的新用法--java(java枚举的作用与好处)

问题的由来前段时间改游戏buff功能,干了一件愚蠢的事情,那就是把枚举和运算集合在一起,然后运行一段时间后buff就出现各种问题,我当时懵逼了!事情是这样的,做过游戏的都知道,buff,需要分类型,且...

你还不懂java的日志系统吗(javaw 日志)

一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...

Java 8之后的那些新特性(三):Java System Logger

去年12月份log4j日志框架的一个漏洞,给Java整个行业造成了非常大的影响。这个事情也顺带把log4j这个日志框架推到了争议的最前线。在Java领域,log4j可能相对比较流行。而在log4j之外...

Java开发中的日志管理:让程序“开口说话”

Java开发中的日志管理:让程序“开口说话”日志是程序员的朋友,也是程序的“嘴巴”。它能让程序在运行过程中“开口说话”,告诉我们它的状态、行为以及遇到的问题。在Java开发中,良好的日志管理不仅能帮助...

吊打面试官(十二)--Java语言中ArrayList类一文全掌握

导读...

OS X 效率启动器 Alfred 详解与使用技巧

问:为什么要在Mac上使用效率启动器类应用?答:在非特殊专业用户的环境下,(每天)用户一般可以在系统中进行上百次操作,可以是点击,也可以是拖拽,但这些只是过程,而我们的真正目的是想获得结果,也就是...

Java中 高级的异常处理(java中异常处理的两种方式)

介绍异常处理是软件开发的一个关键方面,尤其是在Java中,这种语言以其稳健性和平台独立性而闻名。正确的异常处理不仅可以防止应用程序崩溃,还有助于调试并向用户提供有意义的反馈。...

【性能调优】全方位教你定位慢SQL,方法介绍下!

1.使用数据库自带工具...

全面了解mysql锁机制(InnoDB)与问题排查

MySQL/InnoDB的加锁,一直是一个常见的话题。例如,数据库如果有高并发请求,如何保证数据完整性?产生死锁问题如何排查并解决?下面是不同锁等级的区别表级锁:开销小,加锁快;不会出现死锁;锁定粒度...

看懂这篇文章,你就懂了数据库死锁产生的场景和解决方法

一、什么是死锁加锁(Locking)是数据库在并发访问时保证数据一致性和完整性的主要机制。任何事务都需要获得相应对象上的锁才能访问数据,读取数据的事务通常只需要获得读锁(共享锁),修改数据的事务需要获...