1。Internet互联 万物互联已经实实在在的组建在我们生活之中。我们作为网络中的一个节点,不管是否从事IT行业,了解HTTP协议对我们使用浏览器或其他APP都有一定的帮助,这些不限于我们是如何能浏览网页的?我们是如何能使用APP交水电煤的?我们是如何使用电脑或APP买火车票的?我们是如何通过电子网站购物的?我们是如何登录一个系统就可以一直使用这个账号的? HTTP协议是应用层广泛使用的一个协议,了解它我们需要从协议的定义、协议的用法、跨服务调用(专业称为跨域请求)、数据缓存、长连接和它的版本演进为大家拨云开雾的一探究竟。2。HTTP协议定义 HTTP是HyperTextTransferProtocol的缩写,中文翻译就是超文本传输协议。顾名思义:超文本表示数据类型不在局限于文本。在互联网早期的时候只是简单的字符文字,但现在文本的涵义已经可以扩展为图片、视频、压缩包等;传输表示能双向沟通;协议表示一个约定的规范。 即HTTP是一个应用层协议,无状态,由请求和响应构成,是一个标准的客户端服务器模型。在计算机世界里一个专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。3。一个具体的HTTP请求分析 举个栗子,当我们打开头条的Web页面,通过F12打开控制台,我们挑选一个请求来分析下HTTP的结构。 这是一个http请求以及响应,这里的General可以理解成为http请求和响应的起始行的一个公共部分,说明:Genaralheaders:同时适用于请求和响应消息,但与最终消息传输的数据无关的消息头。RequestHeaders:包含更多有关要获取的资源或客户端本身信息的消息头。ResponseHeaders:包含有关响应的补充信息,如其位置或服务器本身(名称和版本等)的消息头。EntityHeaders〔其他〕:包含有关实体主体的更多信息,比如主体长(ContentLength)度或其MIME类型。 客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(requestline)、请求头部(header)、空行和请求数据四个部分组成; 3。1GeneralHeaders 通用头即可以包含在HTTP请求中,也可以包含在HTTP响应中。通用头的作用是描述HTTP协议本身。比如描述HTTP是否持久连接的Connection头,HTTP发送日期的Date头,描述HTTP所在TCP连接时间的KeepAlive头,用于缓存控制的CacheControl头等 RequestURL:请求地址RequestMethod:请求方法StatusCode:请求状态RemoteAddress:RemoteAddress来自TCP连接,表示与服务端建立TCP连接的设备IPReferrerPolicy:控制请求头中referrer的内容 HTTP的请求方法 方法 描述 1hrGET (查询)发送请求来获得服务器上的资源,请求体中不会包含请求数据,请求数据放在协议头中。 2hrPOST (新增)向服务器提交资源(例如提交表单或上传文件)。数据被包含在请求体中提交给服务器。 3hrPUT (修改)向服务器提交资源,并使用提交的新资源,替换掉服务器对应的旧资源。。 4hrDELETE (删除)请求服务器删除指定的资源。 5hrHEAD HEAD方法请求一个与GET请求的响应相同的响应,但没有响应体。 6hrOPTIONS 获取http服务器支持的http请求方法,允许客户端查看服务器的性能,比如ajax跨域时的预检等。 7hrCONNECT 建立一个到由目标资源标识的服务器的隧道。 8hrTRACE 沿着到目标资源的路径执行一个消息环回测试,主要用于测试或诊断。 9hrPATCH 是对PUT方法的补充,用来对已知资源进行局部更新。3。1。1GET和POST比较? 根据RFC规范GET的语义是从服务器获取指定的资源,这个资源可以是静态的文本、页面、图片视频等。GET请求的参数位置一般是写在URL中,URL规定只能支持ASCII,所以GET请求的参数只允许ASCII字符,而且浏览器会对URL的长度有限制(HTTP协议本身对URL长度并没有做任何规定)。POST的语义是根据请求负荷(报文body)对指定的资源做出处理,具体的处理方式视资源类型而不同。POST请求携带数据的位置一般是写在报文body中,body中的数据可以是任意格式的数据,只要客户端与服务端协商好即可,而且浏览器不会对body大小做限制。3。1。2GET和POST的安全和幂等性安全:指请求方法不会破坏服务器上的资源。幂等:多次执行相同的操作,结果都是相同的。 如果从RFC规范定义的语义来看:GET方法就是安全且幂等的,因为它是只读操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。所以,可以对GET请求的数据做缓存,这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如nginx),而且在浏览器中GET请求可以保存为书签。POST因为是新增或提交数据的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。所以,浏览器一般不会缓存POST请求,也不能把POST请求保存为书签。3。2RquestHeaders 实体头是那些描述HTTP信息的头。既可以出现在HTTPPOST方法的请求中,也可以出现在HTTP响应中。比如ContentType和Contentlength等描述实体的类型和大小的头。其它还有用于描述实体的ContentLanguage、ContentMD5、ContentEncoding以及控制实体缓存的Expires、LastModifies头等。 请求头是那些由客户端发往服务端以便帮助服务端更好的满足客户端请求的头。请求头只能出现在HTTP请求中。比如告诉服务器只接收某种响应内容的Accept头,发送Cookies的Cookie头,显示请求主机域的HOST头,用于缓存的IfMatch、IfMatchSince、IfNoneMatch头,用于只取HTTP响应信息中部分信息的Range头,用于附属HTML相关请求引用的Referer头等。 参数说明:浏览器侧的能力Accept:可以接受的媒体类型,例如:Accept:texthtml代表浏览器可以接受服务器回发的类型为texthtmlAcceptEncoding:申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate),注意:这不是指字符编码AcceptLanguage:申明自己接收的语言。语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等等,例如:AcceptLanguage:enusConnection:keepalive当一个网页打开完成后,客户端和服务器之间保持一个长连接。Referer:当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器籍此可以获得一些信息用于处理。UserAgent:客户端使用的操作系统和浏览器的名称和版本。CacheControl:CacheControl与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。Cookie:Cookie是用来存储一些用户信息以便让服务器辨别用户身份的(大多数需要登录的网站上面会比较常见),比如cookie会存储一些用户的用户名和密码,当用户登录后就会在客户端产生一个cookie来存储相关信息,这样浏览器通过读取cookie的信息去服务器上验证并通过后会判定你是合法用户,从而允许查看相应网页。当然cookie里面的数据不仅仅是上述范围,还有很多信息可以存储在cookie里面,比如sessionid等。IfModifiedSince:把浏览器端缓存页面的最后修改时间发送到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行对比。如果时间一致,那么返回304,客户端就直接使用本地缓存文件。如果时间不一致,就会返回200和新的文件内容。客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示在浏览器中T。IfNoneMatch:IfNoneMatch和ETag一起工作,工作原理是在HTTPResponse中添加ETag信息。当用户再次请求该资源时,将在HTTPRequest中加入IfNoneMatch信息(ETag的值)。如果服务器验证资源的ETag没有改变(该资源没有更新),将返回一个304状态告诉客户端使用本地缓存文件。否则将返回200状态和新的资源和Etag。3。3ResponseHeaders HTTP响应头是那些描述HTTP响应本身的头,这里面并不包含描述HTTP响应中第三部分也就是HTTP信息的头(这部分由实体头负责)。比如说定时刷新的Refresh头,当遇到503错误时自动重试的RetryAfter头,显示服务器信息的Server头,设置COOKIE的SetCookie头,告诉客户端可以部分请求的AcceptRanges头等。 参数说明:AccessControlAllowCredentials:跨域Ajax请求时是否带Cookie的设置;表示是否允许发送cookies。默认情况下,Cookies不包括在CORS请求中;设为true表示cookies可以包含在请求中一起发给服务器,如果不需要发送cookies给服务器,需删除字段。需要注意的是:除了设置AccessControlAllowCredential:true外,在ajax请求中也必须打开withCredentialsAccessControlAllowMethods:必要字段,表示服务器支持的所有跨域请求方法,只要浏览器使用的请求方法包含在内即可通过AccessControlAllowOrigin:必要字段,该站点可以被哪些网站进行跨域资源共享AccessControlExposeHeaders:必要字段,表明服务器支持的所有头信息字段,也是为了避免多次预检请求AccessControlAllowMaxAge:可选字段,单位是s,用来指定本次预检的有效期,即在给定时间内允许该条缓存回应,不会发出一条预检请求。CacheControl:控制缓存的行为浅谈http中的CacheControlConnection:Connection:KeepAlive或Connection:close,这里具体的含义是有关http请求的是否保持长连接,即链接是否复用,每次请求是复用已建立好的请求,还是重新建立一个新的请求。ContentLength:在Http1。0及之前版本中,contentlength字段可有可无;在http1。1及之后版本。如果是keepalive,则contentlength和chunk必然是二选一。若是非keepalive,则和http1。0一样。contentlength可有可无。ContentEncoding:AcceptEncoding和ContentEncoding是HTTP中用来对采用哪种编码格式传输正文进行协定的一对头部字段。(ContentEncoding中的gzip和deflate:gzip,一种由文件压缩程序Gzip,GUNzip产生的编码格式,描述于RFC1952。这种编码格式是一种具有32位CRC的LempelZiv编码(LZ77);deflate,由定义于RFC1950的ZLIB编码格式与RFC1951中描述的DEFLATE压缩机制组合而成的产物;)ContentType:代表发送端发送的实体数据的数据类型(post请求肯定要发送数据包;因此对数据包的Type有专门的限定:ContentType只能是applicationxwwwformurlencoded,applicationjson,multipartformdata或textplain中的一种。)KeepAlive:在http早期,每个http请求都要求打开一个tpcsocket连接,并且使用一次之后就断开这个tcp连接。使用keepalive可以改善这种状态,即在一次TCP连接中可以持续发送多份数据而不会断开连接。通过使用keepalive机制,可以减少tcp连接建立次数,也意味着可以减少TIMEWAIT状态连接,以此提高性能和提高httpd服务器的吞吐率(更少的tcp连接意味着更少的系统内核调用,socket的accept()和close()调用)。但是,keepalive并不是免费的午餐,长时间的tcp连接容易导致系统资源无效占用。配置不当的keepalive,有时比重复利用连接带来的损失还更大。所以,正确地设置keepalivetimeout时间非常重要。 HTTP响应消息由状态行、响应头部、空行和响应体4个部分组成,如下图所示: 3。2HTTP常见的状态码 场景的StatusCode说明: 1XX100(continue)表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。 2XX200(OK)表示从客户端发来的请求在服务器端被正常处理了。204(NoContent)该状态码代表服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分。206(PartialContent)该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求。响应报文中包含由ContentRange指定范围的实体内容。 3XX301(MovedPermanently)永久性重定向。该状态码表示请求的资源已被分配了新的URI,以后应使用资源现在所指的URI。302(Found)临时性重定向。比如在没有登录情况下访问网站个人中心,会重定向到登录页,但是你登录后,访问个人中心时,它又不会重定向到其他地方了。303(SeeOther)和302有着相同的功能,但是303明确要求客户端应该采用GET方法获取资源。304(NotModified)如果请求报文首部包含一些条件,例如:IfMatch,IfModifiedSince,IfNoneMatch,IfRange,IfUnmodifiedSince,如果不满足条件,则服务器会返回304状态码。 4XX400(BadRequest)该状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求。401Unauthorized该状态码表示发送的请求需要有认证信息。返回含有401的响应必须包含一个适用于被请求资源的WWWAuthenticate首部用以询问用户信息。当浏览器初次接收到401响应,会弹出认证用的对话窗口。第二次接收到,则不弹出,直接表示认证失败。403(Forbidden)对请求资源的访问被服务器拒绝了,一般是未获得文件系统的访问授权,问权限出现某些问题。404(NotFound)浏览器地址错误。服务器找不到对应资源。 5XX500(InternalServerError)服务器在执行时报错。503(ServiceUnavailable)服务器暂时处于超负载或正在进行停机维护,无响应。一般需要重启服务器即可。4。HTTP缓存技术 对于一些具有重复性的HTTP请求,或者静态数据文件,比如cssjshtml等,可以把这对请求响应的数据都缓存在本地,下次就直接读取本地的数据,进而提高访问性能。 HTTP缓存的2种实现方式:强制缓存和协商缓存。 强制缓存 强制缓存:不判断直接使用浏览器的本地缓存。强缓存是通过下面这两个HTTP响应头部(ResponseHeader)字段实现的,它们都用来表示资源在客户端缓存的有效期:CacheControl:相对时间;Expires:绝对时间; 如果HTTP响应头部同时有上面2个字段的话,CacheControl的优先级高于Expires。CacheControl配置比较多,建议使用CacheControl来实现强缓存进行精细化控制。具体流程如下:客户端第一次请求服务资源,服务器会在返回这个资源的同时,在Response头部加上CacheControl,并设置了过期时间大小;客户端再次请求请求服务器中的该资源时,会先通过请求资源的时间与CacheControl中设置的过期时间大小,来计算出该资源是否过期,如果没有,则使用该缓存,否则重新请求服务器;服务器再次收到请求后,会更新Response头部的CacheControl。 协商缓存 当请求的响应码是304时,服务器告诉浏览器可以使用本地缓存的资源,通常这种方式被称为协商缓存。 协商缓存就是与服务端协商之后,通过协商结果来判断是否使用本地缓存。 协商缓存通过配置头部有2种实现: 第一种:请求头部IfModifiedSince字段与响应头部中的LastModified字段LastModified:标示这个响应资源的最后修改时间;IfModifiedSince:当资源过期了,发现响应头中具有LastModified声明,则再次发起请求的时候带上LastModified的时间,服务器收到请求后发现有IfModifiedSince则与被请求资源的最后修改时间进行对比(LastModified),如果最后修改时间较新,说明资源又被改过,则返回最新资源,HTTP200OK;如果最后修改时间较旧,说明资源无新修改,响应HTTP304走缓存。 第二种:请求头部IfNoneMatch字段与响应头部中的ETag字段响应头部中Etag:唯一标识响应资源;请求头部中的IfNoneMatch:当资源过期时,浏览器发现响应头里有Etag,则再次向服务器发起请求资源时,会将请求头IfNoneMatch值设置为Etag的值。服务器收到请求后进行比对,如果没有变化返回304,如果变化了返回200。 第一种方式是基于时间实现的,第二种方式是基于一个唯一标识实现的。对比2种方式,第二种可以更加准确地判断文件内容是否被修改,避免由于时间篡改导致的不可靠问题。 如果在第一次请求资源的时候,服务端返回的HTTP响应头部同时有Etag和LastModified字段,那么客户端再下一次请求的时候,如果带上了ETag和LastModified字段信息给服务端,这时Etag的优先级更高,也就是服务端先会判断Etag是否变化了,如果Etag没有变化,然后再看LastModified,否则不做其他检查。 为什么ETag的优先级更高?这是因为ETag主要能解决LastModified存在的问题:在没有修改文件内容情况下文件的最后修改时间可能也会改变,这会导致客户端认为这文件被改动了,从而重新请求;可能有些文件是在秒级以内修改的,IfModifiedSince能检查到的粒度是秒级的,使用Etag就能够保证这种需求下客户端在1秒内能刷新多次;有些服务器不能精确获取文件的最后修改时间。 协商缓存这两个字段都需要配合强制缓存中Cachecontrol字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求。 强制缓存和协商缓存的工作流程如下: 使用ETag字段实现的协商缓存的过程:当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在Response头部加上ETag唯一标识,它根据当前请求的资源生成;当浏览器再次请求访问服务器中的该资源时,会先检查强制缓存是否过期:如果没有过期,则直接使用本地缓存;如果过期了,则会在Request头部加上IfNoneMatch字段,该字段的值就是ETag唯一标识;服务器再次收到请求后,会根据请求中的IfNoneMatch值与当前请求的资源生成的唯一标识进行比较:如果相等,则返回304;如果不相等,则返回200状态码和返回资源,并在Response头部加上新的ETag唯一标识;如果浏览器收到304的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。5。HTTP特性 HTTP当前常见到版本有HTTP1。1,HTTP2。0,HTTP3。0,特性不兼容。5。1HTTP1。1的优点 HTTP1。1的优点:简单:HTTP基本的报文格式就是headerbody,头部信息也是keyvalue简单文本的形式,易于理解,降低了学习和使用的门槛。灵活和易于扩展:HTTP协议里的各类请求方法、URIURL、状态码、头字段等每个组成要求都没有被固定死,都允许开发人员自定义和扩充。应用广泛和跨平台:互联网发展至今,HTTP的应用范围非常的广泛,从台式机的浏览器到手机上的各种APP,从看新闻、刷贴吧到购物、理财、吃鸡,HTTP的应用遍地开花,同时天然具有跨平台的优越性。 HTTP1。1的缺点: HTTP协议里有优缺点一体的双刃剑,分别是无状态、明文传输,同时还有一大缺点不安全。 HTTP1。1的性能: HTTP协议是基于TCPIP,使用了请求应答的通信模式。 长连接 HTTP1。0性能比较差,原因是每发起一个请求,都要新建一次TCP连接(三次握手),而且是串行请求,增加了通信开销。 为了解决上述TCP连接问题,HTTP1。1提出了长连接的通信方式。这种方式的好处在于减少了TCP连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。 持久连接的特点是,只要任意一端没有明确提出断开连接,则保持TCP连接状态。如果某个HTTP长连接超过一定时间没有任何数据交互,服务端就会主动断开这个连接。 管道网络传输 HTTP1。1采用了长连接的方式,则可以选择管道(pipeline)传输。即可在同一个TCP连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。因为管道传输是串行的,所以会有一个问题。如果服务端在处理A请求时耗时比较长,那么后续的请求的处理都会被阻塞住,这称为队头堵塞。 所以,HTTP1。1管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞。实际上HTTP1。1管道化技术不是默认开启,而且浏览器基本都没有支持,所以后面所有文章讨论HTTP1。1都是建立在没有使用管道化的前提。大家知道有这个功能,但是没有被使用就行了。 HTTP1。1的性能一般般,后续的HTTP2和HTTP3就是在优化HTTP的性能。5。2HTTP1。1、HTTP2、HTTP3演变 HTTP1。1相比HTTP1。0性能上的改进:长连接改善了HTTP1。0短连接造成的性能开销。支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。 但HTTP1。1还是有性能瓶颈:请求响应头部(Header)未经压缩就发送,首部信息越多延迟越大。只能压缩Body的部分;发送冗长的首部;服务器是按请求的顺序响应的,容易队头阻塞;没有请求优先级控制;请求只能从客户端开始,服务器只能被动响应。5。3HTTP2优化 HTTP2协议是基于HTTPS的,所以HTTP2的安全性也是有保障的。 那HTTP2相比HTTP1。1性能上的改进:头部压缩二进制格式并发传输服务器主动推送资源 HTTP2缺点 HTTP2通过Stream的并发能力,解决了HTTP1队头阻塞的问题,看似很好,但是HTTP2还是存在队头阻塞的问题,只不过问题不是在HTTP这一层面,而是在TCP这一层。 HTTP3做了哪些优化? 前面我们知道了HTTP1。1和HTTP2都有队头阻塞的问题:HTTP1。1中的管道(pipeline)虽然优化了请求的队头阻塞,但是没有解决响应的队头阻塞,这属于HTTP层队头阻塞。HTTP2虽然通过多个请求复用一个TCP连接解决了HTTP的队头阻塞,基于流水线的传输模式,一旦发生丢包,就会阻塞住所有的HTTP请求,这属于TCP层队头阻塞。 HTTP2队头阻塞的问题是因为TCP,所因此HTTP3把HTTP下层的TCP协议改成了UDP! UDP并不是一个可靠传输,发送是不管顺序,也不管丢包的,所以不会出现像HTTP2队头阻塞的问题。不可靠的UDP进过改造后的QUIC协议可以实现类似TCP的可靠性传输。 QUIC有以下3个特点。无队头阻塞连接建立更平滑连接迁移更稳定 HTTP3现在普及的进度非常的缓慢,不知道未来UDP是否能够逆袭TCP。 参考资料:阮一峰。HTTP协议入门。阮一峰的网络日志。小林coding图解http