【业务学习】2019-05-09 http1.1&2.0的基本原理

Grape
全部视频:https://segmentfault.com/a/11…

http的定义

    HTTP是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议。

http的结构

《【业务学习】2019-05-09 http1.1&2.0的基本原理》

我们目前用到最多的是http1.x协议,header和body我们都不陌生,那么startline是什么呢?startline是我们所说的request_line或status_line,也就是
GET /HTTP/1.1或者HTTP/1.1 200 OK这种字段。
在叙述http的各种工作方式之前,我们先熟悉一下TCP/IP模型:

《【业务学习】2019-05-09 http1.1&2.0的基本原理》

http的发展:

http1.0

Http1.0:http1.0是默认没有keep-alive的,在数据请求的时候会先进性应用层以下的处理过程才会到应用层,在这里我们只说传输层和应用层,在http1.0中,每一次的请求都会进行建立tcp连接(三次握手),然后进行http请求,这样每次与服务器交互,都需要新开一个连接!我们的每个链接的请求链路都如下图所示:

《【业务学习】2019-05-09 http1.1&2.0的基本原理》

想象一下,每发起一个请求就经过上述如此长的一个流程,这将耗去多少资源。

http1.1

基于这个问题,我们的http迎来了http1.1版本,1.1版本相对于1.0版本有什么改动呢?

  • http增加了host字段
  • HTTP 1.1中引入了Chunked transfer-coding,范围请求,实现断点续传(实际上就是利用HTTP消息头使用分块传输编码,将实体主体分块传输)
  • HTTP 1.1管线化(pipelining)理论,客户端可以同时发出多个HTTP请求,而不用一个个等待响应之后再请求

    • 注意:这个pipelining仅仅是限于理论场景下,大部分桌面浏览器仍然会选择默认关闭HTTP pipelining!
    • 所以现在使用HTTP1.1协议的应用,都是有可能会开多个TCP连接的!

Http1.1基于上述耗费资源的问题给予了根本的处理,默认长链接,什么意思呢? 不去在每一个http请求的时候都去进行http连接,只建立一个tcp链接去处理多个请求,当然,这里的每个请求是串行的,即只是不用去进行tcp连接,还是得排队,并且这样子可能会引起线头阻塞(例如发送100个请求,第一个被阻塞导致后边99个也不能请求)问题。对于http1.1默认的工作模式如下图所示:

《【业务学习】2019-05-09 http1.1&2.0的基本原理》

到这我们想象一下这种模式好么,有什么缺点?可以优化吗?
在此之上我们已经抛出了两个问题1.需要排队 2.可能引起线头阻塞。对于第一个问题,http1.1已经给出了解决方案,即pipline,而第二个问题刚开始有一种过渡的方案,即spdy协议(google推出的一项增强http的协议,功能包括数据流的多路复用、请求优先级以及HTTP报头压缩,有兴趣的可以研究一下),然后再到现在的http2.0。
首先我们先说一下什么是pipline,pipline是一项实现了多个http请求但不需要等待响应就能够写进同一个socket的技术,仅有http1.1规范支持http管线化,1.0并不支持。什么意思呢?我们看上图是在一个tcp连接的串行的去处理,那么开启了pipline之后就会变成下边这个样子:

《【业务学习】2019-05-09 http1.1&2.0的基本原理》

我们可以看到发送http请求不再是先发送然后等待response再发送下个请求了,这样子我们可以看成是所有的请求统一开始,但是这有一个问题,HTTP Pipelining其实是把多个HTTP请求放到一个TCP连接中一一发送,而在发送过程中不需要等待服务器对前一个请求的响应;只不过,客户端还是要按照发送请求的顺序来接收响应!这就导致了它虽然解决了排队问题,但是他也仅仅是解决了单方排队的问题,最后接受数据还是按照请求的顺序来接受相应,为什么呢?因为他们不知道哪个是第一个哪个是第二个。这样同样会存在线头阻塞的问题。
总结一下就是在http1.0的时候我们是流水线,一个接一个的完成任务,http1.1的时候呢我们工人的能力提升了,可以一次发出多个工作需求了,但是还没有掌握技巧,还是得按照条例等待工作全部到来的时候一个一个按顺序处理。

http2.0

接下来就是我们的http2.0,看他如何解决了之前的问题。解决线头阻塞,在http2.0中其实是用了一个stream的结构,给每一个stream分配一个标示即streamId就可以来解决线头阻塞的问题。那么http2究竟是何方神圣呢?
首先,说起http2,我们不得不提一下https,http2是基于https的一个协议,对于https我找了一篇写的比较好的文章,Wireshark 抓包理解 HTTPS 请求流程
文章开头我们对比了http1和http2的结构,看起来好像完全不一样,但是实际上并非如此,http2以帧作为最小单位,看了下边的图我们会发现原来http2只是做了层封装,其实本质还是headers和body,只不过http2是以更高级功能更多的方式进行了展示。

《【业务学习】2019-05-09 http1.1&2.0的基本原理》

http1.x vs http2.0

关于http2好在哪里,那我们得从http1坏出发,因为有对比才会有伤害。

  • http1连接数限制,对于同一个域名,浏览器最多只能同时创建 6~8 个 TCP 连接 (不同浏览器不一样)。为了解决数量限制,出现了 域名分片 技术,其实就是资源分域,将资源放在不同域名下 (比如二级子域名下),这样就可以针对不同域名创建连接并请求,以一种讨巧的方式突破限制,但是滥用此技术也会造成很多问题,比如每个 TCP 连接本身需要经过 DNS 查询、三步握手、慢启动等,还占用额外的 CPU 和内存,对于服务器来说过多连接也容易造成网络拥挤、交通阻塞等。那么,http2是怎么做的呢?http2采用了多路复用技术,在一个 TCP 连接上,我们可以向对方不断发送帧,每帧的 stream_id标明这一帧属于哪个流,然后在对方接收时,根据 stream_id拼接每个流的所有帧组成一整块数据。把 HTTP/1.1 每个请求都当作一个流,那么多个请求变成多个流,请求响应数据分成多个帧,不同流中的帧交错地发送给对方,这就是 HTTP/2 中的多路复用。同时呢,我们知道http1的body长度是在header带过来的,那么如果是以http2的形式去传输肯定会出问题,所以http2将body上架了length字段,每一个流都有自己的长度,最后根据流的头部长度是否等于各个流的长度来确定是否合包。同时呢,这样分包合包也解决了线头阻塞的问题。那么问题又来了,怎么确定没有丢包?同一个stream秩序有没有乱?这点tcp会保证包的有序性且保证了包不会丢失。
  • Header 内容多,而且每次请求 Header 不会变化太多,没有相应的压缩传输优化方案。http2在此用hpack算法来压缩首部长度,其原理就是维护一个静态索引表和动态索引表的索引空间,hpack其原理就是匹配当前连接存在的索引空间,若某个键值已存在,则用相应的索引代替首部条目,比如 “:method: GET” 可以匹配到静态索引中的 index 2,传输时只需要传输一个包含 2 的字节即可;若索引空间中不存在,则用字符编码传输,字符编码可以选择哈夫曼编码,然后分情况判断是否需要存入动态索引表中,以这种形式节省了很多的空间。
  • 明文传输不安全。http1使用明文传输,不安全。那么http2就用二进制分帧层来解决这个问题,帧是数据传输的最小单位,以二进制传输代替原本的明文传输,原本的报文消息被划分为更小的数据帧。
  • 为了尽可能减少请求数,需要做合并文件、雪碧图、资源内联等优化工作,但是这无疑造成了单个请求内容变大延迟变高的问题,且内嵌的资源不能有效地使用缓存机制。对于这种情况,http2推出了服务端推送,浏览器发送一个请求,服务器主动向浏览器推送与这个请求相关的资源,这样浏览器就不用发起后续请求,其主要是针对资源内联做出的优化。
  • 应用层的重置连接,对于 HTTP/1 来说,是通过设置 tcp segment 里的 reset flag 来通知对端关闭连接的。这种方式会直接断开连接,下次再发请求就必须重新建立连接。HTTP/2 引入 RST_STREAM 类型的 frame,可以在不断开连接的前提下取消某个 request 的 stream,表现更好。

扩展

关于http我们就说这么多,如果想了解的更多读者可以自行用wireshark抓包看一下。推荐两个比较好的抓包工具:wireshark和charles。

参考文章:

    原文作者:NoSay
    原文地址: https://segmentfault.com/a/1190000020252678
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞