前言
HTTP(HyperText
Transfer
Protocal
) 是超文本传输协议,http
最初被打造出来就是为了传输文本内容,用作学术交流,将html
文本布局传来传去,那个时候是没有后端的概念的,数据全是写死的,类似电子书。本期就带大家回顾下http的发展历程,认清咩个版本的优缺点~
HTTP/0.9
- 客户端发送
get
请求,比如请求一个/index.html
- 服务端接收请求,读取对应的
html
文件,以ASCII
的字符流返回给客户端
特点
- 只有请求行,没有请求头和请求体,因此0.9版本的http也被称之为
单行协议
- 因此也没有响应头
- 传输的内容是以
ASCII
的字符流
推动0.9到1.0的发展是网景公司
,世界上第一款浏览器就是网景公司开发的,自从他推出浏览器后,万维网(World
Wide
Web
)就不再是仅仅做学术交流了,互联网高速发展,万维网联盟(W3C
)成立,http工作组
同时成立,这个工作组专门致力于http协议
的更新,以前查html
,css
,js
就是在w3c
上查找,类似现在的mdn
随着互联网需求的增加,0.9不再满足,1.0诞生
0.9只能传文本格式,用户接触浏览器不仅仅要看文本,还需要看图片,音频等格式
HTTP/1.0
-
相比较0.9,支持多种类型文件的传输,且不限于
ASCII
编码方式 -
既然多种文件,那么就需要信息告诉浏览器如何加载这些文件,因此引入请求头,响应头来让客户端和服务端更加深入的交流,并且是
key-value
的形式
为什么有了请求头和响应头就能支持多种文件的数据传输
请求头
accept: text/html
告诉服务端我期望接收到一个html
的文件
accept-encoding: gzip, deflate, br
告诉服务端以这种方式压缩
accept-language: zh-CN
告诉服务端以中文的格式返回
响应头
content-encoding: br
告诉浏览器压缩方式是br
content-type: text/html; charset=utf-8
告诉浏览器以这种方式,编码加载
1.0时,比如展示一个页面,用到几个js文件,css文件,用一次就会单独用http
请求一次,文件不多还好,但是随着需求增加,一个页面可能会用到很多js
文件,以及第三方库,或者说图片资源,展示一个页面会发很多次请求,非常影响性能
HTTP/1.1
-
持久连接,一个
tcp
连接建立,可以传输多个http
请求,减少了大量的tcp
连接和断开连接带来的开销持久连接带来了队头阻塞的问题,这个问题没办法解决
-
Chunk transfer
机制处理动态数据包的长度
1.0时一个页面展示每用到一个js
脚本,图片等都需要重新建立一次连接,做无畏的重复,因此1.1推出了持久连接或者长连接来解决这个问题
1.1可以建立一次tcp
连接后,发送多个http
请求,最后tcp
关闭连接,持久连接在1.1是默认开启的,当然你也可以通过改请求头中的Connection
字段,Connection: keep-alive || close
,close
就是关闭的
实际开发中,页面所有的资源不可能全部一次性请求回来,比如有个按钮,点击后才会发请求,假设我页面初次加载的tcp
请求还没有关闭,就点击了按钮,于是就会再次建立一次持久连接,这种建立是有数量限制的,通常为6-8个keep-alive
,也就是tcp
持久连接
凡事有利有弊,这种方式可以1次tcp
连接 n 次http
请求,我们设想一个情景,如果 n 个http
请求有个http
请求的数据很多,造成很慢,后面的http
还会过来吗?不能,当年的http
请求需要顺序,因此这就导致了http
的队头阻塞
队头阻塞
管线化
:批量化发请求(放弃)
针对这个http
队头阻塞问题,http
工作组想过一个解决方案,管线化
,一次性把http
请求全部发出去,这些http
请求都会打上顺序标记,保证接收的顺序,谷歌和火狐以前采用过这个方案,但是不清楚出于什么原因放弃了这个方案
后面请求头中增加了一个host
字段,这就导致了虚拟机技术的成熟,浏览器的同源策略就是通过host
字段判断域名是否一致,虚拟机也会被分配ip
,虚拟机和主机的ip
是不同的,host
当初就是用来区分虚拟机的域名地址的
另外,1.0时,需要在响应头中设置数据的大小,字段content-Length: 1024
比如这个就是1024个字节的大小,但是有时候后端也不清楚自己发送的数据多大,因为数据可能是动态的,这就导致了客户端不清楚自己是否接收完毕,1.1于是推出片段化数据,将数据分割成若干个任意大小的数据块,每个数据块发送时带有自身的长度,最后会发送一个长度为0的数据块来标志发送完毕,这就有点像是柯里化函数,最后不传参数才会执行函数
浏览器的cookie
也是这个时候加入的,cookie
虽然是浏览器的存储但是大部分是归于后端负责的,前端可以读取cookie
的内容,另外cookie
的内容也可以被设置为可读取
和不可读取
,一般为了安全性会设置不可读取
,这就是为了防止脚本攻击
HTTP/2.0
1.1的问题
- 带宽用不满,多条
tcp
连接竞争带宽导致每条tcp
连接中能被分配的带宽大大降低 tcp
的慢启动:拥塞控制导致一定会慢启动,但是会推迟页面关键资源加载时间http
队头阻塞问题,阻塞时会浪费带宽
1.1存在队头阻塞问题,以及6个持久连接,也就是说一个浏览器只能同时存在6个tcp
的建立,一个tcp
建立有多个http
请求,其实这6个持久连接会共享网络速度,假设1000M宽带,网速最终就是1000/6M的效果,也就是说对网速的利用率很低,其实造成网速低的原因还有个就是tcp
的慢启动,到达理想速度有个过程,这个慢启动就是为了解决网络拥塞问题
tcp
慢启动有个问题就是,对于很小的css
,js
文件,就没必要耗时这么久,文件大还好,文件小但是文件又很重要就很难受
多路复用
- 一个域名只使用一个
tcp
长连接 - 将每个请求分成一帧一帧的数据进行传输并打上标记,同时发送给服务端,且可以在重要资源请求中标记为加急,服务端接收到带有各种编号的数据帧后,可以区分哪个数据帧加急,优先处理和响应该请求的数据帧(通过引入了二进制分帧层实现多路复用)
首先,tcp
的慢启动我们肯定无法进行优化的,只能进行规避,因此我就尽可能的少点tcp
的建立连接,2.0的多路复用就是一个域名只使用一个tcp
的长连接,6个变1个,这样就不会产生多个keep-alive
占用带宽问题,因此网速大大提高
另外,多路复用还将每个请求都划分成一帧一帧的样子,每帧都会标记一些数据,比如标记加急,这样就可以解决http
队头阻塞问题,里面就是因为引入了二进制分帧层
2.0同时引入了https
HTTP/HTTPS
首先一个请求行大概如下这样
GET /image/logo.png HTTP/1.1
这里提到get
,顺带带大家认清get
和post
的区别
GET vs POST
get
和post
本质上没有很多区别,真要说区别需要从用法上来说
get
没有加密效果!
要从用法上说就得扯上 副作用
和 幂等
-
副作用:对服务器上的资源有变更
比如,搜索是不会对数据发生变更的,注册会有
-
幂等:注册10次和20次不是幂等,新增了数据,但是更改文章10次和20次是幂等,改来改去还是那么多数据
get
多作用于无副作用,幂等的场景,比如搜索
post
多作用于有副作用,无幂等的场景,比如注册
从技术角度来说,get
请求能缓存,post
不能缓存
浏览器浏览页面会有历史页面的,历史页面就有url
,get
请求的数据放在url
中,因此,get
请求会被浏览器缓存住
要说安全,post
请求确实是会安全一点,post
请求放在请求体中,get
请求拼接在url中
另外url
的长度是有限的,所以get
请求数据有限制,而post
不会
post
请求支持更多的编码类型,并且不对数据类型做限制
用过koa
的小朋友都清楚,koa
默认是不支持post
的,需要另外安装依赖,就是因为post
的数据类型太多了,不支持
总结
get
用于无副作用幂等的场景,post用于有副作用不幂等的场景get
请求能缓存,post
不能post
相对安全一点,post
的参数是在请求体中,get
的参数是拼接在url
中url
的长度有限制,所以get
请求会受影响post
支持更多的编码类型,且不对数据类型做限制
这里也顺带放下面试官喜欢问你的状态码
http状态码
200
请求成功,请求在服务端被正确处理
204
响应成功,没有数据
205
服务器处理成功,浏览器应重置文档视图
206
服务器成功处理了部分get请求
301
资源永久重定向 资源 后端换地方不作任何操作就是404
302
资源临时重定向
303
让你查看其他地址
304
请求的资源没有修改,服务端不会返回任何资源
400
请求语法错误,服务器看不懂
401
请求没有携带信息,比如token认证失败
403
请求被拒绝 敏感词
404
找不到资源
500
服务器内部错误,无法完成请求
501
服务器不支持当前请求所需的功能
503
服务器系统维护或者超载,暂时无法处理客户端的请求
回到http/https的比较
https
是加密后的http
,这个s
就是TLS
因此https = http + tls
TLS
位于传输层之上,应用层之下,TLS
中包含了对称加密
和非对称加密
对称加密
对称加密需要客户端,服务端双方都知道加密的方式,以及解密的方式,或者说双方都有相同的密钥,都知道如何加密和解密
对称加密有个很大问题,双方如何清楚共同的密钥?通过传输的方式,密钥也是通过网络传输,这个过程要是被截取就完蛋了
非对称加密
非对称加密不同,非对称加密会有个公钥
和私钥
,公钥用于加密,私钥用于解密,比如服务端发送一个公钥给客户端,客户端会用这个公钥进行加密,创建一个密钥,用公钥来加密这个密钥,给到服务端,服务端用独有的私钥进行解密得到密钥,两个过程都不怕被截取
tls
是先用非对称加密让双方都有密钥,再对称加密进行数据传输
http2.0
本身是没有问题的,问题主要存在tcp
身上,因为这些版本的http
都是基于tcp
,tcp
自身就有慢启动问题,不够高效
HTTP/3.0
http2.0
的问题在于tcp
,因为tcp
有个慢启动问题,其实tcp
还有个问题就是tcp
的队头阻塞,因为tcp
需要保证数据包传输的顺序,数据在传输的过程会出现丢包的问题,tcp
有个超时重传的机制,为保证顺序就需要等待,这个过程就导致了队头阻塞
因此2.0的问题就是tcp的慢启动
和``队头阻塞`
这个时候你肯定想,既然tcp
有问题就去解决tcp
的问题,实则不行,因为tcp
作用于物理层,比如交换机就是物理层,里面的tcp
要是变更了,全球的交换机就需要淘汰掉,就像是js
的null
被当做obj
一样,改不了了
像是这种tcp
的问题无法解决,被称之为tcp僵化
其实导致tcp僵化
的原因还有一点是操作系统,tcp
协议是通过操作系统的内核实现的,应用程序只能使用不能修改
因此http
工作组非常无奈,想要解决这些问题就只能不用tcp协议了
3.0基于的协议是QUIC
协议
quic
协议同样面临着tcp
同样的挑战,因为quic
协议需要硬件的支持,比较麻烦,但是又比更新tcp
好,因为新增个quic
协议至少不会让设备崩掉,更新tcp
会很麻烦
这也就是为何3.0目前还没有普及的原因
QUIC协议
quic
协议不是从0打造的,它是基于udp
协议,udp
的特点是高效,虽然不可靠,quic
协议在udp
上实现了类似于tcp
的多路复用,可靠性传输等功能
- 实现了类似于
tcp
的流量控制和可靠性传输 - 集成了
TLS
加密 - 实现了
HTTP2
中的多路复用
缺点:需要浏览器,服务器,操作系统支持quic
协议,普及开还需要一段时间
最后
一段话总结下http
发展史:
0.9
是最初版本,此时的http被称之为单行协议
,因为只有请求行
,没有请求头请求体,以ASCII
码的形式传输,只能支持html
文件类型,后面随着文件类型的增加,1.0
诞生,可以支持多种文件类型
的传输,并且不仅仅只支持ASCII这一种编码,引入了请求头响应头
,因为里面的字段信息才可以区分文件类型等操作,但是1.0有个缺陷就是一个文件对应一个tcp
的建立,后面1.1诞生,推出了keep-alive
长连接,一个tcp
的建立与断开之间可以有多个http请求
,但是keep-alive的数量有限,6-8个,并且keep-alive带来了http队头阻塞
的问题,就是一个http请求万一很慢,会阻塞后面的http请求,以前推出过管线化
方法去解决,但是因为某些原因不采用了,至今无果,此时1.1还推出了host
字段,导致后面虚拟机
技术的成熟,并且1.0时的数据包会包含content-Length
字段,但是有时候后端的数据是动态的,因此1.1推出Chunk transfer
机制来处理动态数据,它是通过切片的形式进行传输,最终发送一个长度为0
的数据块来标志发送完毕,因为1.1的队头阻塞问题,以及6个keep-alive共同占用网速
的原因,对带宽的利用率很低,2.0诞生,2.0的多路复用,将6个keep-alive变成了一个
,有效提高宽带利用率,并且通过二进制分帧层
实现了对数据帧的传输顺序管理,解决了http队头阻塞问题,但是由于tcp僵化
问题,比如无法解决它的慢启动和队头阻塞问题,3.0摒弃了tcp协议
,采用了quic协议
,这个协议基于udp,udp高效但是不可靠,于是quic又实现了类似于tcp的流量控制
和可靠性传输
,并且还有加密
,实现了多路复用
,总之很强大,只是目前还没有普及开
如果你对春招感兴趣,可以加我的个人微信:
Dolphin_Fung
,我和我的小伙伴们有个面试群,可以进群讨论你面试过程中遇到的问题,我们一起解决
另外有不懂之处欢迎在评论区留言,如果觉得文章对你学习有所帮助,还请"点赞+评论+收藏"一键三连,感谢支持!