HTTP详解 Hyper Text Transfer Protocol

版本:

HTTP0.9-->结构简单、纯文本格式、GET

HTTP1.0-->增加了HEAD、POST等新方法;增加了响应状态码,标记可能的错误原因;引入了协议版本号概念;引入了HTTP Header概念,让HTTP处理请求和响应更加灵活;传输的数据不再仅限于文本。

HTTP1.1-->增加了PUT、DELETE等新的方法;增加了缓存管理和控制;明确了连接管理,允许持久连接;允许相应数据分块(chunked),利于传输大文件;强制要求Host头,让互联网主机托管成为可能。

HTTP2-->二进制协议,不再是纯文本;可发起多个请求,废弃了1.1里的管道;使用专用算法压缩头部,减少数据传输量;允许服务器主动向客户端推送数据;增强了安全性,“事实上”要求加密通信。

HTTP3-->QUIC

HTTP 协议确立了一种计算机之间交流通信的规范,以及相关的各种控制和错误处理方式。

传输:专门用来在两点之间传输数据的约定和规范

文本: HTTP 传输的不是 TCP/UDP 这些底层协议里被切分的杂乱无章的二进制包,而是完整的、有意义的数据,可以被浏览器、服务器这样的上层应用程序处理。

超文本:“超越了普通文本的文本”,它是文字、图片、音频和视频等的混合体,最关键的是含有“超链接”,能够从一个“超文本”跳跃到另一个“超文本”,

HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范

CDN:Content Delivery Network  内容分发网络。应用了HTTP协议里的缓存和代理技术,代替源站响应客户端的请求。可以缓存源站的数据,让浏览器的请求不用“千里迢迢”地到达源站服务器,直接在“半路”就可以获取响应。如果 CDN 的调度算法很优秀,更可以找到离用户最近的节点,大幅度缩短响应时间。

URI:协议名+主机名+路径

输入网址按下回车:

使用IP地址访问Web服务器-->

浏览器从地址栏的输入中获得服务器的 IP 地址和端口号;浏览器用 TCP 的三次握手与服务器建立连接;浏览器向服务器发送拼好的报文;服务器收到报文后处理请求,同样拼好报文再发给浏览器;浏览器解析报文,渲染输出页面。

使用域名访问Web服务器-->

DNS解析有可能会给出CDN服务器的IP地址

请求报文和响应报文的结构基本相同:

起始行-->描述请求或响应的基本信息

头部字段集合-->使用key-value形式更详细地说明报文

消息实体-->实际传输的数据

header和body之间有空行

发送get请求的时候,http报文经常是只有header而没body

请求行:简要描述了客户端想要如何操作服务器端的资源  = 请求方法(GET/POST)+请求目标(URI)+版本号(HTTP协议版本)  

状态行:响应报文里的起始行,代表服务器响应的状态    =版本号+状态码+原因

头部字段:请求头和响应头的结构是基本一样的,唯一的区别是起始行

    头部字段是key-value格式,key和value之间用:分隔,最后用CRLF换行表示字段结束

HTTP 协议规定了非常多的头部字段,实现各种各样的功能,但基本上可以分为四大类:通用字段:在请求头和响应头里都可以出现;请求字段:仅能出现在请求头里,进一步说明请求信息或者额外的附加条件;响应字段:仅能出现在响应头里,补充说明响应报文的信息;实体字段:它实际上属于通用字段,但专门描述 body 的额外信息。

Host 字段,它属于请求字段,只能出现在请求头里,它同时也是唯一一个 HTTP/1.1 规范里要求必须出现的字段,也就是说,如果请求头里没有 Host,那这就是一个错误的报文。Host 字段告诉服务器这个请求应该由哪个主机来处理,当一台计算机上托管了多个虚拟主机的时候,服务器端就需要用 Host 字段来选择,有点像是一个简单的“路由重定向”。

User-Agent 是请求字段,只出现在请求头里。它使用一个字符串来描述发起 HTTP 请求的客户端,服务器可以依据它来返回最合适此浏览器显示的页面。

Date 字段是一个通用字段,但通常出现在响应头里,表示 HTTP 报文创建的时间,客户端可以使用这个时间再搭配其他字段决定缓存策略。

Server 字段是响应字段,只能出现在响应头里。它告诉客户端当前正在提供 Web 服务的软件名称和版本号。

 Content-Length,它表示报文里 body 的长度,也就是请求头或响应头空行后面数据的长度。

标准请求方法:

GET:获取资源,可以理解为读取或者下载数据;

HEAD:获取资源的元信息;

POST:向资源提交数据,相当于写入或上传数据;

PUT:类似 POST;

DELETE:删除资源;

CONNECT:建立特殊的连接隧道;

OPTIONS:列出可对资源实行的方法;

TRACE:追踪请求 - 响应的传输路径。

GET/HEAD

GET:它的含义是请求从服务器获取资源,这个资源既可以是静态的文本、页面、图片、视频,也可以是由 PHP、Java 动态生成的页面或者其他格式的数据。

HEAD 方法与 GET 方法类似,也是请求从服务器获取资源,服务器的处理机制也是一样的,但服务器不会返回请求的实体数据,只会传回响应头,也就是资源的“元信息”。

POST/PUT

向 URI 指定的资源提交数据,数据就放在报文的 body 里。PUT 的作用与 POST 类似,也可以向服务器提交数据,但与 POST 存在微妙的不同,通常 POST 表示的是“新建”“create”的含义,而 PUT 则是“修改”“update”的含义。

DELETE 方法指示服务器删除资源,因为这个动作危险性太大,所以通常服务器不会执行真正的删除操作,而是对资源做一个删除标记。当然,更多的时候服务器就直接不处理 DELETE 请求。

CONNECT 是一个比较特殊的方法,要求服务器为客户端和另一台远程服务器建立一条特殊的连接隧道,这时 Web 服务器在中间充当了代理的角色。

OPTIONS 方法要求服务器列出可对资源实行的操作方法,在响应头的 Allow 字段里返回。它的功能很有限,用处也不大,有的服务器(例如 Nginx)干脆就没有实现对它的支持。

TRACE 方法多用于对 HTTP 链路的测试或诊断,可以显示出请求 - 响应的传输路径。它的本意是好的,但存在漏洞,会泄漏网站的信息,所以 Web 服务器通常也是禁止使用。

在 HTTP 协议里,所谓的“安全”是指请求方法不会“破坏”服务器上的资源,即不会对服务器上的资源造成实质的修改。按照这个定义,只有 GET 和 HEAD 方法是“安全”的,因为它们是“只读”操作,只要服务器不故意曲解请求方法的处理方式,无论 GET 和 HEAD 操作多少次,服务器上的数据都是“安全的”。而 POST/PUT/DELETE 操作会修改服务器上的资源,增加或删除数据,所以是“不安全”的。

所谓的“幂等”实际上是一个数学用语,被借用到了 HTTP 协议里,意思是多次执行相同的操作,结果也都是相同的,即多次“幂”后结果“相等”。很显然,GET 和 HEAD 既是安全的也是幂等的,DELETE 可以多次删除同一个资源,效果都是“资源不存在”,所以也是幂等的。POST 和 PUT 的幂等性质就略费解一点。按照 RFC 里的语义,POST 是“新增或提交数据”,多次提交数据会创建多个资源,所以不是幂等的;而 PUT 是“替换或更新数据”,多次更新一个资源,资源还是会第一次更新的状态,所以是幂等的。

URI=URL+URN

scheme ://  host:port path    ?query

scheme协议名,表示资源应该使用哪种协议来访问

资源所在的主机名,主机名加端口号

标记资源所在位置path

客户端和服务器看到的 URI 是不一样的。客户端看到的必须是完整的 URI,使用特定的协议去连接特定的主机,而服务器看到的只是报文请求行里被删除了协议名和主机名的 URI。

查询参数 query 有一套自己的格式,是多个“key=value”的字符串,这些 KV 值用字符“&”连接,浏览器和服务器都可以按照这个格式把长串的查询参数解析成可理解的字典或关联数组形式。

URI 引入了编码机制,对于 ASCII 码以外的字符集和特殊字符做一个特殊的操作,把它们转换成与 URI 语义不冲突的形式。URI 转义的规则有点“简单粗暴”,直接把非 ASCII 码或特殊字符转换成十六进制字节值,然后前面再加上一个“%”。

响应报文=响应头(状态行+头字段)+响应体

状态行:协议版本号  状态码  原因

1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作;

2××:成功,报文已经收到并被正确处理;

3××:重定向,资源位置发生变动,需要客户端重新发送请求;

4××:客户端错误,请求报文有误,服务器无法处理;

5××:服务器错误,服务器在处理请求时内部发生了错误。

1xx:101 Switching Protocols  同意变更协议

2xx:200 ok

  204 No content    响应头后没有body数据

  206 Partial Content   body里的数据只是资源的一部分

3xx:301  永久重定向,含义是此次请求的资源已经不存在了,需要改用改用新的 URI 再次访问。

  302临时重定向,意思是请求的资源还在,但需要暂时用另一个 URI 来访问。

  301 和 302 都会在响应头里使用字段 Location 指明后续要跳转的 URI,最终的效果很相似,浏览器都会重定向到新的 URI。

  

  “304 Not Modified” 是一个比较有意思的状态码,它用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。它不具有通常的跳转含义,但可以理解成“重定向已到缓存的文件”(即“缓存重定向”)。

4xx:400 Bad Request

  403 Forbidden”实际上不是客户端的请求出错,而是表示服务器禁止访问资源。

  “404 Not Found”它的原意是资源在本服务器上未找到,所以无法提供给客户端。

  405 Method Not Allowed:不允许使用某些方法操作资源,例如不允许 POST 只能 GET;406 Not Acceptable:资源无法满足客户端请求的条件,例如请求中文但只有英文;408 Request Timeout:请求超时,服务器等待了过长的时间;409 Conflict:多个请求发生了冲突,可以理解为多线程并发时的竞态;413 Request Entity Too Large:请求报文里的 body 太大;414 Request-URI Too Long:请求行里的 URI 太大;429 Too Many Requests:客户端发送了太多的请求,通常是由于服务器的限连策略;431 Request Header Fields Too Large:请求头某个字段或总体太大;

5xx:

  “501 Not Implemented”表示客户端请求的功能还不支持

  “502 Bad Gateway”通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误,但具体的错误原因也是不知道的。

  “503 Service Unavailable”表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503。

HTTP 协议是一个“灵活可扩展”的传输协议。HTTP 协议是一个“可靠”的传输协议。HTTP 协议是一个应用层的协议。HTTP 协议使用的是请求 - 应答通信模式。HTTP 协议是无状态的。传输的实体数据可缓存可压缩、可分段获取数据、支持身份认证、支持国际化语言等。

实体数据body

数据类型与编码:text,image,audio/video,application  编码类型gzip,deflate,br

Accept:客户端告诉服务器希望接收什么样的数据

Content-Type:服务器告诉客户端实际发送了什么样的数据

Accept-Encoding:客户端支持的压缩格式

Content-Encoding:实际使用的压缩格式

语言类型与编码:

语言类型使用的头字段

Accept-Language:标记了客户端可理解的自然语言

Content-Language:告诉客户端数据使用的实际语言类型

Accept-Charset:字符集,编码方式

Content-Type:charset=

HTTP传输大文件的方法

数据压缩:Accept-Encoding     Content-Encoding

分块传输:chunked分块传输编码  响应报文中用头字段Transfer-Encoding:chunked来表示

“Transfer-Encoding: chunked”和“Content-Length”这两个字段是互斥的,也就是说响应报文里这两个字段不能同时出现,一个响应报文的传输要么是长度已知,要么是长度未知(chunked)

每个分块包含两个部分,长度头和数据块;长度头是以 CRLF(回车换行,即\r\n)结尾的一行明文,用 16 进制数字表示长度;数据块紧跟在长度头后,最后也用 CRLF 结尾,但数据不包含 CRLF;最后用一个长度为 0 的块表示结束,即“0\r\n\r\n”。

范围请求:允许客户端在请求头里使用专用字段来表示只获取文件的一部分,相当于是客户端的“化整为零”。“Accept-Ranges: bytes”

请求头 Range 是 HTTP 范围请求的专用字段,格式是“bytes=x-y”,其中的 x 和 y 是以字节为单位的数据范围。 x、y 表示的是“偏移量”,范围必须从 0 计数,例如前 10 个字节表示为“0-9”,第二个 10 字节表示为“10-19”,而“0-10”实际上是前 11 个字节。Range 的格式也很灵活,起点 x 和终点 y 可以省略,能够很方便地表示正数或者倒数的范围。假设文件是 100 个字节,那么:“0-”表示从文档起点到文档终点,相当于“0-99”,即整个文件;“10-”是从第 10 个字节开始到文档末尾,相当于“10-99”;“-1”是文档的最后一个字节,相当于“99-99”;“-10”是从文档末尾倒数 10 个字节,相当于“90-99”。

服务器收到 Range 字段后,需要做四件事。第一,它必须检查范围是否合法,比如文件只有 100 个字节,但请求“200-300”,这就是范围越界了。服务器就会返回状态码 416,意思是“你的范围请求有误,我无法处理,请再检查一下”。第二,如果范围正确,服务器就可以根据 Range 头计算偏移量,读取文件的片段了,返回状态码“206 Partial Content”,和 200 的意思差不多,但表示 body 只是原数据的一部分。第三,服务器要添加一个响应头字段 Content-Range,告诉片段的实际偏移量和资源的总大小,格式是“bytes x-y/length”,与 Range 头区别在没有“=”,范围后多了总长度。例如,对于“0-10”的范围请求,值就是“bytes 0-10/100”。

有了范围请求之后,HTTP 处理大文件就更加轻松了,看视频时可以根据时间点计算出文件的 Range,不用下载整个文件,直接精确获取片段所在的数据内容。不仅看视频的拖拽进度需要范围请求

常用的下载工具里的多段下载、断点续传也是基于它实现的,要点是:

先发个 HEAD,看服务器是否支持范围请求,同时获取文件的大小;开 N 个线程,每个线程使用 Range 字段划分出各自负责下载的片段,发请求传输数据;下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用 Range 请求剩下的那一部分就可以了。

多段数据

刚才说的范围请求一次只获取一个片段,其实它还支持在 Range 头里使用多个“x-y”,一次性获取多个片段数据。

HTTP的连接管理

短连接:每次发送请求前需要先与服务器建立连接,收到响应报文后会立即关闭连接。因为客户端与服务器的整个连接过程很短暂,不会与服务器保持长时间的连接状态,所以就被称为“短连接”

长连接:持久连接,连接保活,连接复用

连接相关的头字段:

由于长连接对性能的改善效果非常显著,所以在 HTTP/1.1 中的连接都会默认启用长连接。不需要用什么特殊的头字段指定,只要向服务器发送了第一次请求,后续的请求都会重复利用第一次打开的 TCP 连接,也就是长连接,在这个连接上收发数据。

也可以在请求头里明确地要求使用长连接机制,使用的字段是 Connection,值是“keep-alive”。

不管客户端是否显式要求长连接,如果服务器支持长连接,它总会在响应报文里放一个“Connection: keep-alive”字段。

因为 TCP 连接长时间不关闭,服务器必须在内存里保存它的状态,这就占用了服务器的资源。如果有大量的空闲长连接只连不发,就会很快耗尽服务器的资源,导致服务器无法为真正有需要的用户提供服务。

在客户端,可以在请求头里加上“Connection: close”字段,告诉服务器:“这次通信后就关闭连接”。服务器看到这个字段,就知道客户端要主动关闭连接,于是在响应报文里也加上这个字段,发送之后就调用 Socket API 关闭 TCP 连接。

服务器端通常不会主动关闭连接,但也可以使用一些策略。

使用“keepalive_timeout”指令,设置长连接的超时时间,如果在一段时间内连接上没有任何数据收发就主动断开连接,避免空闲连接占用系统资源。

使用“keepalive_requests”指令,设置长连接上可发送的最大请求次数。比如设置成 1000,那么当 Nginx 在这个连接上处理了 1000 个请求后,也会主动断开连接。

队头阻塞

“队头阻塞”与短连接和长连接无关,而是由 HTTP 基本的“请求 - 应答”模型所导致的。

因为 HTTP 规定报文必须是“一发一收”,这就形成了一个先进先出的“串行”队列。队列里的请求没有轻重缓急的优先级,只有入队的先后顺序,排在最前面的请求被最优先处理。

如果队首的请求因为处理的太慢耽误了时间,那么队列里后面的所有请求也不得不跟着一起等待,结果就是其他的请求承担了不应有的时间成本。

性能优化

因为“请求 - 应答”模型不能变,所以“队头阻塞”问题在 HTTP/1.1 里无法解决,只能缓解,有什么办法呢?

这在 HTTP 里就是“并发连接”(concurrent connections),也就是同时对一个域名发起多个长连接,用数量来解决质量的问题。

这个就是“域名分片”(domain sharding)技术,还是用数量来解决质量的思路。

重定向:

“Location”字段属于响应字段,必须出现在响应报文里。但只有配合 301/302 状态码才有意义,它标记了服务器要求重定向的 URI

浏览器收到 301/302 报文,会检查响应头里有没有“Location”。如果有,就从字段值里提取出 URI,发出新的 HTTP 请求,相当于自动替我们点击了这个链接。

301 俗称“永久重定向”(Moved Permanently),意思是原 URI 已经“永久”性地不存在了,今后的所有请求都必须改用新的 URI。

浏览器看到 301,就知道原来的 URI“过时”了,就会做适当的优化。比如历史记录、更新书签,下次可能就会直接用新的 URI 访问,省去了再次跳转的成本。搜索引擎的爬虫看到 301,也会更新索引库,不再使用老的 URI。

302 俗称“临时重定向”(“Moved Temporarily”),意思是原 URI 处于“临时维护”状态,新的 URI 是起“顶包”作用的“临时工”。

303 See Other:类似 302,但要求重定向后的请求改为 GET 方法,访问一个结果页面,避免 POST/PUT 重复操作;

307 Temporary Redirect:类似 302,但重定向后请求里的方法和实体不允许变动,含义比 302 更明确;

308 Permanent Redirect:类似 307,不允许重定向后的请求变动,但它是 301“永久重定向”的含义。

重定向的应用场景:

一个最常见的原因就是“资源不可用”,需要用另一个新的 URI 来代替。另一个原因就是“避免重复”,让多个网址都跳转到一个 URI,增加访问入口的同时还不会增加额外的工作量。

重定向的问题:

第一个问题是“性能损耗”。很明显,重定向的机制决定了一个跳转会有两次请求 - 应答,比正常的访问多了一次。第二个问题是“循环跳转”。如果重定向的策略设置欠考虑,可能会出现“A=>B=>C=>A”的无限循环,不停地在这个链路里转圈圈,后果可想而知。

HTTP 协议特别规定,浏览器必须具有检测“循环跳转”的能力,在发现这种情况时应当停止发送请求并给出错误提示。

HTTP的Cookie机制:响应头字段 Set-Cookie 和请求头字段 Cookie。

当用户通过浏览器第一次访问服务器的时候,服务器肯定是不知道他的身份的。所以,就要创建一个独特的身份标识数据,格式是“key=value”,然后放进 Set-Cookie 字段里,随着响应报文一同发给浏览器。浏览器收到响应报文,看到里面有 Set-Cookie,知道这是服务器给的身份标识,于是就保存起来,下次再请求的时候就自动把这个值放进 Cookie 字段里发给服务器。因为第二次请求里面有了 Cookie 字段,服务器就知道这个用户不是新人,之前来过,就可以拿出 Cookie 里的值,识别出用户的身份,然后提供个性化的服务。

Cookie 就是服务器委托浏览器存储在客户端里的一些数据,而这些数据通常都会记录用户的关键识别信息。所以,就需要在“key=value”外再用一些手段来保护,防止外泄或窃取,这些手段就是 Cookie 的属性。

Cookie常见属性:

设置 Cookie 的生存周期,也就是它的有效期,让它只能在一段时间内可用,一旦超过这个期限浏览器就认为是 Cookie 失效,在存储里删除,也不会发送给服务器。Cookie 的有效期可以使用 Expires 和 Max-Age 两个属性来设置。

设置 Cookie 的作用域,让浏览器仅发送给特定的服务器和 URI,避免被其他网站盗用。“Domain”和“Path”指定了 Cookie 所属的域名和路径,浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。

属性“HttpOnly”会告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问,浏览器的 JS 引擎就会禁用 document.cookie 等一切相关的 API,脚本攻击也就无从谈起了。另一个属性“SameSite”可以防范“跨站请求伪造”(XSRF)攻击,设置成“SameSite=Strict”可以严格限定 Cookie 不能随着跳转链接跨站发送,而“SameSite=Lax”则略宽松一点,允许 GET/HEAD 等安全方法,但禁止 POST 跨站发送。

还有一个属性叫“Secure”,表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在。

Cookie的应用:

身份识别,保存用户的登录信息,实现会话事务

广告跟踪

HTTP的缓存控制:

浏览器发现缓存无数据,于是发送请求,向服务器获取资源;服务器响应请求,返回资源,同时标记资源的有效期;浏览器缓存资源,等待下次重用。

服务器标记资源有效期使用的头字段是“Cache-Control”,里面的值“max-age=30”就是资源的有效时间,相当于告诉浏览器,“这个页面只能缓存 30 秒,之后就算是过期,不能用。”这里的 max-age 是“生存时间”(类似 TTL,Time-To-Live),时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻),而不是客户端收到报文的时刻,也就是说包含了在链路传输过程中所有节点所停留的时间。

“max-age”是 HTTP 缓存控制最常用的属性,此外在响应报文里还可以用其他的属性来更精确地指示浏览器应该如何使用缓存:

no-store:不允许缓存,用于某些变化非常频繁的数据,例如秒杀页面;

no-cache:它的字面含义容易与 no-store 搞混,实际的意思并不是不允许缓存,而是可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本;

must-revalidate:又是一个和 no-cache 相似的词,它的意思是如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。

即使有“Last-modified”和“ETag”,强制刷新(Ctrl+F5)也能够从服务器获取最新数据(返回 200 而不是 304),F5是304

强制刷新是请求最新,不使用条件请求

当你点“刷新”按钮的时候,浏览器会在请求头里加一个“Cache-Control: max-age=0”。因为 max-age 是“生存时间”,max-age=0 的意思就是“我要一个最最新鲜的西瓜”,而本地缓存里的数据至少保存了几秒钟,所以浏览器就不会使用缓存,而是向服务器发请求。服务器看到 max-age=0,也就会用一个最新生成的报文回应浏览器。

Ctrl+F5 的“强制刷新”又是什么样的呢?它其实是发了一个“Cache-Control: no-cache”,含义和“max-age=0”基本一样,就看后台的服务器怎么理解,通常两者的效果是相同的。

那么,浏览器的缓存究竟什么时候才能生效呢?别着急,试着点一下浏览器的“前进”“后退”按钮,再看开发者工具,你就会惊喜地发现“from disk cache”的字样,意思是没有发送网络请求,而是读取的磁盘上的缓存。

重定向跳转功能,也可以发现浏览器使用了缓存

在“前进”“后退”“跳转”这些重定向动作中浏览器不会“夹带私货”,只用最基本的请求头,没有“Cache-Control”,所以就会检查缓存,直接利用之前的资源,不再进行网络通信。

条件请求

浏览器可以用两个连续的请求组成“验证动作”:先是一个 HEAD,获取资源的修改时间等元信息,然后与缓存数据比较,如果没有改动就使用缓存,节省网络流量,否则就再发一个 GET 请求,获取最新的版本。

但这样的两个请求网络成本太高了,所以 HTTP 协议就定义了一系列“If”开头的“条件请求”字段,专门用来检查验证资源是否过期,把两个请求才能完成的工作合并在一个请求里做。而且,验证的责任也交给服务器,浏览器只需“坐享其成”。条件请求一共有 5 个头字段,我们最常用的是“if-Modified-Since”和“If-None-Match”这两个。需要第一次的响应报文预先提供“Last-modified”和“ETag”,然后第二次请求时就可以带上缓存里的原值,验证资源是否是最新的。如果资源没有变,服务器就回应一个“304 Not Modified”,表示缓存依然有效,浏览器就可以更新一下有效期,然后放心大胆地使用缓存了。

Last-modified”很好理解,就是文件的最后修改时间。ETag 是什么呢?ETag 是“实体标签”(Entity Tag)的缩写,是资源的一个唯一标识,主要是用来解决修改时间无法准确区分文件变化的问题。使用 ETag 就可以精确地识别资源的变动情况,让浏览器能够更有效地利用缓存。

Cache 和 Cookie 的相同点是:都会保存到浏览器中,并可以设置过期时间。
不同点:
1. Cookie 会随请求报文发送到服务器,而 Cache 不会,但可能会携带 if-Modified-Since(保存资源的最后修改时间)和 If-None-Match(保存资源唯一标识) 字段来验证资源是否过期。
2. Cookie 在浏览器可以通过脚本获取(如果 cookie 没有设置 HttpOnly),Cache 则无法在浏览器中获取(出于安全原因)。
3. Cookie 通过响应报文的 Set-Cookie 字段获得,Cache 缓存完整报文。
4. 用途不同。Cookie 常用于身份识别,Cache 则是由浏览器管理,用于节省带宽和加快响应速度。
5. Cookie 的 max-age 是从浏览器拿到响应报文时开始计算的,而 Cache 的 max-age 是从响应报文的生成时间(Date 头字段)开始计算。

HTTP的代理服务:

所谓的“代理服务”就是指服务本身不生产内容,而是处于中间位置转发上下游的请求和响应,具有双重身份:面向下游的用户时,表现为服务器,代表源服务器响应客户端的请求;而面向上游的源服务器时,又表现为客户端,代表客户端发送请求。

代理最基本的一个功能是负载均衡。因为在面向客户端时屏蔽了源服务器,客户端看到的只是代理服务器,源服务器究竟有多少台、是哪些 IP 地址都不知道。于是代理服务器就可以掌握请求分发的“大权”,决定由后面的哪台服务器来响应请求。代理中常用的负载均衡算法你应该也有所耳闻吧,比如轮询、一致性哈希等等,这些算法的目标都是尽量把外部的流量合理地分散到多台源服务器,提高系统的整体资源利用率和性能。

在负载均衡的同时,代理服务还可以执行更多的功能,比如:

健康检查:使用“心跳”等机制监控后端服务器,发现有故障就及时“踢出”集群,保证服务高可用;

安全防护:保护被代理的后端服务器,限制 IP 地址或流量,抵御网络攻击和过载;

加密卸载:对外网使用 SSL/TLS 加密通信认证,而在安全的内网不加密,消除加解密成本;

数据过滤:拦截上下行的数据,任意指定策略修改请求或者响应;

内容缓存:暂存、复用服务器响应

缓存代理:支持缓存控制的代理服务

HTTP 的服务器缓存功能主要由代理服务器来实现(即缓存代理),而源服务器系统内部虽然也经常有各种缓存(如 Memcache、Redis、Varnish 等),但与 HTTP 没有太多关系

代理服务收到源服务器发来的响应数据后需要做两件事。第一个当然是把报文转发给客户端,而第二个就是把报文存入自己的 Cache 里。

下一次再有相同的请求,代理服务器就可以直接发送 304 或者缓存数据,不必再从源服务器那里获取。这样就降低了客户端的等待时间,同时节约了源服务器的网络带宽。

客户端的缓存只是用户自己使用,而代理的缓存可能会为非常多的客户端提供服务。所以,需要对它的缓存再多一些限制条件。

猜你喜欢

转载自www.cnblogs.com/liushoudong/p/12625689.html