HTTP协议学习---(三)进阶篇

一 HTTP协议body数据格式

http协议是明文传输的,为了具体描述body中的数据需要在header中加入一些描述字段。

1 MIME type
MIME 把数据分成了八大类,每个大类下再细分出多个子类,形式是“type/subtype”的字符串(常用的四大类以下)
text:即文本格式的可读数据,我们最熟悉的应该就是 text/html 了,表示超文本文档,此外还有纯文本 text/plain、样式表 text/css 等。
image:即图像文件,有 image/gif、image/jpeg、image/png 等。
audio/video:音频和视频数据,例如 audio/mpeg、video/mp4 等。
application:数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有 application/json,application/javascript、application/pdf 等,另外,如果实在是不知道数据是什么类型,就会是 application/octet-stream,即不透明的二进制数据。

2 Encoding type
gzip:GNU zip 压缩格式,也是互联网上最流行的压缩格式;
deflate:zlib(deflate)压缩格式,流行程度仅次于 gzip;
br:一种专门为 HTTP 优化的新压缩算法(Brotli)

3 请求与响应的协商
HTTP 协议也使用 Accept 请求头字段和 Content 实体头字段,用于客户端和服务器就语言与编码进行“内容协商”
例如访问https://www.baidu.com/
在这里插入图片描述
在这里插入图片描述

其中q指的是权重,与对应的类型中间用’;’隔开,最大值也是默认值为1,最小值为0表示不接受,*/*指的是除前面之外的其他类型。
Accept表示客户端希望得到的MIME type,中间用’,’隔开。
Content-Type表示实际返回的MIME type。
Accept-Encoding表示客户端支持的压缩算法(省略表示不支持压缩)
Content-Encoding表示服务端返回数据采用的压缩格式(省略表示不压缩)。
Accept-Language表示希望返回的语言
Content-Language表示实际使用的语言类型
Accept-Charset表示支持的字符集(基本不用,字符集基本都支持)
没有Content-Charset,会在Content-Type最后直接加上返回的字符集。

不同服务器协商的结果是不一定的,有的时候,服务器会在响应头里多加一个 Vary 字段,记录服务器在内容协商时参考的请求头字段,给出一点信息,例如:
Vary: Accept-Encoding,User-Agent,Accept
这个 Vary 字段表示服务器依据了 Accept-Encoding、User-Agent 和 Accept 这三个头字段,然后决定了发回的响应报文。

二 HTTP协议大文件传输方式

1 数据压缩
Accept-Encoding中用到的基本只对纯文本数据有很好的压缩效率,但是不适用于音频视频类的大型二进制数据,因为本身压缩率已经很高了,用gzip等可能会起到反作用。

2 chunked
Transfer-Encoding: chunked,把大文件拆分成小文件分块传输,然后再由接收端拼接。注意,因为http是一问一答的形式,所以在接收端未完全获取所有的数据块完成拼接之前,这个请求就一直不会完成。这里的分块与TCP的分块没有任何关系,TCP本身就会对内容进行分块传输,但是HTTP不关注这些内容,相当于HTTP本身先对数据进行分块,下层的TCP在对分块之后的数据在分块。
Transfer-Encoding: chunked”和“Content-Length”这两个字段是互斥的,也就是说响应报文里这两个字段不能同时出现。

3 数据块的格式
①每个分块包含两个部分,长度头和数据块;
②长度头是以 CRLF(回车换行,即\r\n)结尾的一行明文,用 16 进制数字表示长度;
③数据块紧跟在长度头后,最后也用 CRLF 结尾,但数据不包含 CRLF;
④最后用一个长度为 0 的块表示结束,即“0\r\n\r\n”。
在这里插入图片描述

4 范围请求
在处理完大数据传输之后,对于音频视频可能存在获取大数据块中的某些数据块,所以需要用到范围请求。范围请求不是 Web 服务器必备的功能,可以实现也可以不实现,所以服务器必须在响应头里使用字段“Accept-Ranges: bytes”明确告知客户端:“我是支持范围请求的”。如果不支持的话该怎么办呢?服务器可以发送“Accept-Ranges: none”,或者干脆不发送“Accept-Ranges”字段
请求头 Range 是 HTTP 范围请求的专用字段,格式是“bytes=x-y”,其中的 x 和 y 是以字节为单位的数据范围。
注意 x、y 表示的是“偏移量”,范围必须从 0 计数,例如前 10 个字节表示为“0-9”,第二个 10 字节表示为“10-19”,而“0-10”实际上是前 11 个字节。以下为多种表达方式
①“0-”表示从文档起点到文档终点,相当于“0-99”,即整个文件;
②“10-”是从第 10 个字节开始到文档末尾,相当于“10-99”;
③“-1”是文档的最后一个字节,相当于“99-99”;
④“-10”是从文档末尾倒数 10 个字节,相当于“90-99”。
服务器收到请求之后会做四件事:
①它必须检查范围是否合法,比如文件只有 100 个字节,但请求“200-300”,这就是范围越界了。服务器就会返回状态码 416,意思是“你的范围请求有误,我无法处理,请再检查一下”。
②如果范围正确,服务器就可以根据 Range 头计算偏移量,读取文件的片段了,返回状态码“206 Partial Content”,和 200 的意思差不多,但表示 body 只是原数据的一部分。
③服务器要添加一个响应头字段 Content-Range,告诉片段的实际偏移量和资源的总大小,格式是“bytes x-y/length”,与 Range 头区别在没有“=”,范围后多了总长度。例如,对于“0-10”的范围请求,值就是“bytes 0-10/100”。
④剩下的就是发送数据了,直接把片段用 TCP 发给客户端,一个范围请求就算是处理完了。
例:
请求
在这里插入图片描述

响应
在这里插入图片描述

5 补充
范围请求与压缩算法同时使用的时候,里面的range表示的是压缩之前的数据块,因为压缩一般都是一边压缩一边发送数据,所以并不知道压缩后总数据块的大小。
断点续传的实现原理:
先发送一个HEAD请求看服务器是否支持chunked,如果支持,则开N个线程,每个线程的range填写本线程请求资源的大小。完了之后再进行合并即可,即使中断,也只需要再请求本线程的range即可。
一次请求多段数据:
需额外加入一个MIME type,multipart/byteranges”,表示报文的 body 是由多段字节序列组成的,并且还要用一个参数“boundary=xxx”给出段之间的分隔标记。
在这里插入图片描述

例:
请求
在这里插入图片描述

响应
在这里插入图片描述

三 HTTP协议连接管理

1 早期的 HTTP 协议使用短连接,收到响应后就立即关闭连接,效率很低;

2 HTTP/1.1 默认启用长连接,在一个连接上收发多个请求响应,提高了传输效率;

同时因为TCP有慢启动拥塞窗口等特性,新建立的链接发送数据一般比较慢,所以长连接会更快一些。

3 服务器会发送“Connection: keep-alive”字段表示启用了长连接;

4 报文头里如果有“Connection: close”就意味着长连接即将关闭;

5 过多的长连接会占用服务器资源,所以服务器会用一些策略有选择地关闭长连接;
nginx的连接处理策略:
①使用“keepalive_timeout”指令,设置长连接的超时时间,如果在一段时间内连接上没有任何数据收发就主动断开连接,避免空闲连接占用系统资源。
②使用“keepalive_requests”指令,设置长连接上可发送的最大请求次数。比如设置成 1000,那么当 Nginx 在这个连接上处理了 1000 个请求后,也会主动断开连接。

6 “队头阻塞”问题会导致性能下降,可以用“并发连接”和“域名分片”技术缓解。
队头阻塞是指因为http是应答模式,所以所有的请求都会排队等待发送,当有请求占用时间较长时会阻塞剩余请求,导致整体变慢。
并发连接指的是浏览器与一个域名建立多个连接,发送请求时就有多个队列分摊。
域名分片指的是如果浏览器限制了一个域名只能建立N个链接,那么可以对于域名进行分片,请求时客户端与多个域名建立连接即可。例如www.baidu.com可以被分片为www1.baidu.com www2.baidu.com,再浏览器层面www1与www2都指向www的IP即可

7 补充
DDOS攻击:
利用长连接特性对服务器建立大量的连接请求导致服务器资源用尽从而拒绝提供服务
Connection:
请求头中Connection除了keep-alive以及close之外,还有一个Upgrade,配合101状态码表示切换协议。

四 HTTP协议重定向跳转

1重定向是服务器发起的跳转,要求客户端改用新的 URI 重新发送请求,通常会自动进行,用户是无感知的;

2 301/302 是最常用的重定向状态码,分别是“永久重定向”和“临时重定向”;

3响应头字段 Location 指示了要跳转的 URI,可以用绝对或相对的形式,一定要与3xx状态码连用才可以;

4重定向可以把一个 URI 指向另一个 URI,也可以把多个 URI 指向同一个 URI,用途很多;

5使用重定向时需要当心性能损耗,还要避免出现循环跳转。

6 补充
300状态码少用,一般指返回多个可跳转地址,让用户自己选择。
重定向报文里也可以使用Refresh字段实现延迟重定向。
例:Refresh:5;url=xxx 表示延迟5秒后重定向到XXX
与跳转有关的还有一个Referer和Referrer-Policy。表示浏览器跳转的来源,可以用于后续统计或者防盗链等。

五 HTTP的COOKIE机制

1 cookie的传递
响应头字段Set-Cookie以及请求头字段Cookie

2 cookie的属性
①生命周期
Expires过期时间 具体的时间
Max-age(优先使用) 相对时间 单位是秒,计算方法为浏览器收到报文的时
间加上过期时间计算出过期的具体时间,设置为0表示立即失效。
如果不指定这两个属性则表示只在浏览器运行期间有限,关闭浏览器则失效。
②作用域
Domain 所属的域名
Path 路径 全路径表示/
③安全性
XSS(跨站脚本)攻击指的是通过JS执行脚本document.cookie等方式获取数据进行攻击
HttpOnly
此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问
SameSite
“SameSite=Strict”可以严格限定 Cookie 不能随着跳转链接跨站发送, 而“SameSite=Lax”则略宽松一点,允许 GET/HEAD 等安全方法,但禁止 POST 跨 站发送。
Secure
表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送

六 HTTP的缓存机制

1 响应头中的Cache-Control
max-age
缓存最大存活时间,单位秒。告知浏览器缓存的持续时间
no-store
不允许浏览器缓存该资源
no-cache
允许缓存资源,但是使用前需要访问服务器看是否有最新的资源,如果有就用最新的,如果没有就用浏览器本地的缓存
must-revalidate
允许缓存资源,在有效期内可以直接使用,但过了有效期就要访问服务器看是否允许使用。
在这里插入图片描述

2 请求头中的Cache-Control
与响应头中类似,当请求头中如果有Cache-Control: max-age=0表示获取最新的资源,当请求头中有Cache-Control: no-cache含义与max-age一样,表示请求最新的资源。

3 条件请求
与普通请求不同,成功返回的是304而不是200,200表示有新的资源
浏览器用“Cache-Control”做缓存控制只能是刷新数据,不能很好地利用缓存数据,又因为缓存会失效,使用前还必须要去服务器验证是否是最新版。浏览器可以用两个连续的请求组成“验证动作”:先是一个 HEAD,获取资源的修改时间等元信息,然后与缓存数据比较,如果没有改动就使用缓存,节省网络流量,否则就再发一个 GET 请求,获取最新的版本。但是这样成本太高,因此 HTTP 协议就定义了一系列“If”开头的“条件请求”字段,专门用来检查验证资源是否过期,也就是校验过期的责任交给浏览器。
首先请求资源第一次的响应报文需要提供两个字段:
Last-modified
最近一次的更新时间
ETag
资源的唯一标识,因为Last-modified只能精确到秒,在秒之内的改变无法区分。ETag有强弱之分,强表示资源在字节级别上必须完全相同,弱表示资源整体的语义不变。
条件请求一共有 5 个头字段:
if-Modified-Since
If-None-Match
If-Unmodified-Since
If-Match
If-Range
注意:请求头中同时有If-None-Match、If-Modified-Since、Cache-Control,对于服务器来说,If-None-Match、If-Modified-Since的优先级高,也就是即使请求头有Cache-Control: no-cache,走的也是条件请求,而不是直接返回最新完整数据

七 HTTP的代理服务

1 代理的作用
①负载均衡 分散入口流量
②健康检查 使用心跳机制连接后端服务器,发现故障阻断服务
③安全防护 限制IP或者流量
④加密卸载 SSL/TSL加密解密,外网加密,内网解密
⑤数据过滤 拦截数据修改字段
⑥内容缓存 缓存资源减少后端压力

2 代理信息记录
代理服务器使用Via字段表名代理身份,在请求头中没经过一个代理,就需要在Via字段中追加代理的信息。非官方通常采用“X-Forwarded-For”(只记录IP信息,第一个是客户端的IP,之后是代理/服务器的IP)和“X-Real-IP”(只记录一个客户端IP)

3 代理协议
因为X-Forwarded-For以及X-Real-IP这些字段在请求头中,如果要获取真实客户端IP需要解析请求头,但在有些场景下是不允许的或者被加密了的,所以由代理软件公司HAProxy定义了一个事实标准的代理协议。
代理协议分为两版,V1,V2。V1是明文,V2是二进制格式。
它在 HTTP 报文前增加了一行 ASCII 码文本,相当于又多了一个头。这一行文本其实非常简单,开头必须是“PROXY”五个大写字母,然后是“TCP4”或者“TCP6”,表示客户端的 IP 地址类型,再后面是请求方地址、应答方地址、请求方端口号、应答方端口号,最后用一个回车换行(\r\n)结束。这样代理服务器只需要解析报文前的之一行数据即可,不用理会报文内的实际数据。
例:
在这里插入图片描述

八 HTTP的代理缓存服务

1 Cache-Control缓存属性区分
除了基本的max-age、no-store、no-cache 和 must-revalidate之外,由于加入了代理,对于一些资源代理有没有权限缓存的权限做的限制,private表示资源只能在客户端缓存,public表示资源可以在代理与客户端都缓存。

2 代理端缓存失效后的重新验证以及其他字段
在这里插入图片描述

3 客户端缓存失效后的重新验证
在这里插入图片描述

4 缓存清理
比较常用的是服务端向代理服务器发送一个PURGE(自定义的)请求方式的请求,来删除资源
九 HTTP2
1 http2概览
HTTP 协议取消了小版本号,所以 HTTP/2 的正式名字不是 2.0;
HTTP/2 在“语义”上兼容 HTTP/1,保留了请求方法、URI 等传统概念;
HTTP/2 使用“HPACK”算法压缩头部信息,消除冗余数据节约带宽;
HTTP/2 的消息不再是“Header+Body”的形式,而是分散为多个二进制“帧”; HTTP/2 使用虚拟的“流”传输消息,解决了困扰多年的“队头阻塞”问题,同时实现了“多路复用”,提高连接的利用率;
HTTP/2 也增强了安全性,要求至少是 TLS1.2,而且禁用了很多不安全的密码套件。
2 具体实现
HTTP/2 必须先发送一个“连接前言”字符串,然后才能建立正式连接;
HTTP/2 废除了起始行,统一使用头字段,在两端维护字段“Key-Value”的索引表,使用“HPACK”算法压缩头部;
HTTP/2 把报文切分为多种类型的二进制帧,报头里最重要的字段是流标识符,标记帧属于哪个流;
流是 HTTP/2 虚拟的概念,是帧的双向传输序列,相当于 HTTP/1 里的一次“请求 - 应答”;
在一个 HTTP/2 连接上可以并发多个流,也就是多个“请求 - 响应”报文,这就是“多路复用”。

十 HTTP的优化

1 整体方向
性能优化是一个复杂的概念,在 HTTP 里可以分解为服务器性能优化、客户端性能优化和传输链路优化;
服务器有三个主要的性能指标:吞吐量、并发数和响应时间,此外还需要考虑资源利用率;
客户端的基本性能指标是延迟,影响因素有地理距离、带宽、DNS 查询、TCP 握手等;
从服务器到客户端的传输链路可以分为三个部分,我们能够优化的是前两个部分,也就是“第一公里”和“中间一公里”;
有很多工具可以测量这些指标,服务器端有 ab、top、sar 等,客户端可以使用测试网站,浏览器的开发者工具。

2 细节 花钱购买硬件、软件或者服务可以直接提升网站的服务能力,其中最有价值的是 CDN; 不花钱也可以优化 HTTP,三个关键词是“开源”“节流”和“缓存”; 后端应该选用高性能的 Web 服务器,开启长连接,提升 TCP 的传输效率; 前端应该启用 gzip、br 压缩,减小文本、图片的体积,尽量少传不必要的头字段; 缓存是无论何时都不能忘记的性能优化利器,应该总使用 Etag 或 Last-modified 字段标记资源; 升级到 HTTP/2 能够直接获得许多方面的性能提升,但要留意一些 HTTP/1 的“反模式”。