admin管理员组文章数量:1608911
HTTP是什么?HTTP又不是什么?
简单来说HTTP是超文本传输协议,所以可以拆分成超文本、传输、协议
从协议上看
HTTP 是一个用在计算机世界里的协议。它使用计算机能够理解的语言确立了一种计算机之
间交流通信的规范,以及相关的各种控制和错误处理方式。
传输
HTTP 是一个“传输协议”:
- HTTP 协议是一个“双向协议“。
- 允许中间有“中转”或者“接力”:“A<===>B”=“A<=>X<=>Y<=>Z<=>B”
总的来收,HTTP 是一个在计算机世界里专门用来在两点之间传输数据的约定和规范。
超文本
所谓“超文本”,就是“超越了普通文本的文本”,例如我们最熟悉的HTML
三者结合来说
HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范
HTTP 不是什么
- 不存在“单独的实体”
- HTTP 不是互联网
- HTTP 不是编程语言,HTTP 不是 HTML
- HTTP 不是一个孤立的协议
总的来说
- HTTP 是一个用在计算机世界里的协议,它确立了一种计算机之间交流通信的规范,以
及相关的各种控制和错误处理方式。 - HTTP 专门用来在两点之间传输数据,不能用于广播、寻址或路由。
- HTTP 传输的是文字、图片、音频、视频等超文本数据。
- HTTP 是构建互联网的重要基础技术,它没有实体,依赖许多其他的技术来实现,但同
时许多技术也都依赖于它。
HTTP世界全览(上):与HTTP相关的各种概念
浏览器“Web Browser”
浏览器本质上是一个 HTTP 协议中的请求方
小结
- 互联网上绝大部分资源都使用 HTTP 协议传输;
- 浏览器是 HTTP 协议里的请求方,即 User Agent;
- 服务器是 HTTP 协议里的应答方,常用的有 Apache 和 Nginx;
- CDN 位于浏览器和服务器之间,主要起到缓存加速的作用;
- 爬虫是另一类 User Agent,是自动访问网络资源的程序。
HTTP世界全览(下):与HTTP相关的各种协议
- TCP/IP 是网络世界最常用的协议,HTTP 通常运行在 TCP/IP 提供的可靠传输基础上;
- DNS 域名是 IP 地址的等价替代,需要用域名解析实现到 IP 地址的映射;
- URI 是用来标记互联网上资源的一个名字,由“协议名 + 主机名 + 路径”构成,俗称
URL; - HTTPS 相当于“HTTP+SSL/TLS+TCP/IP”,为 HTTP 套了一个安全的外壳;
- 代理是 HTTP 传输过程中的“中转站”,可以实现缓存加速、负载均衡等功能。
常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?
OSI 网络分层模型
-
OSI,全称是“开放式系统互联通信参考模型”
-
所谓的“四层负载均衡”就是指工作在传输层上,基于 TCP/IP 协议的特性,例如 IP 地址、端口号等实现对后端服务器的负载均衡。
-
所谓的“七层负载均衡”就是指工作在应用层上,看到的是 HTTP 协议,解析 HTTP 报文里的 URI、主机名、资源类型等数据,再用适当的策略转发给后端服务器。
TCP/IP 协议栈的工作方式
- HTTP 协议的传输过程就是这样通过协议栈逐层向下,每一层都添加本层的专有数据,层层打包,然后通过下层发送出去。(相当于你寄快递的过程,比如你要寄一个玩具,玩具就是内容,包装就是TCP头,然后快递小哥拿着你的快递到达快递中心,进行打包,这个”包“就是MAC头和IP头,到达目的地,拆包(清除MAC头和IP头),然后送到目的人手上,他再拆包裹,就是卸掉TCP头,最后拿到的就是真正的内容(相当于你请求网页的内容))
小结
- TCP/IP 分为四层,核心是二层的 IP 和三层的 TCP,HTTP 在第四层;
- OSI 分为七层,基本对应 TCP/IP,TCP 在第四层,HTTP 在第七层;
- OSI 可以映射到 TCP/IP,但这期间一、五、六层消失了;
- 日常交流的时候我们通常使用 OSI 模型,用四层、七层等术语;
- HTTP 利用 TCP/IP 协议栈逐层打包再拆包,实现了数据传输,但下面的细节并不可见。
- 有一个辨别四层和七层比较好的(但不是绝对的)小窍门,“两个凡是”:凡是由操作系统
负责处理的就是四层或四层以下,否则,凡是需要由应用程序(也就是你自己写代码)负责
处理的就是七层。
域名里有哪些门道?
域名的形式
- 最右边的被称为“顶级域名”,然后是“二级域名”,层级关系向左依次降低。
域名的解析(树形查询)
例如,你要访问“www.apple”,就要进行下面的三次查询:
- 访问根域名服务器,它会告诉你“com”顶级域名服务器的地址;
- 访问“com”顶级域名服务器,它再告诉你“apple”域名服务器的地址;
- 最后访问“apple”域名服务器,就得到了“www.apple”的地址。
解决域名访问慢的关键思路:缓存
浏览器缓存->操作系统缓存->hosts->dns
小结
- 域名使用字符串来代替 IP 地址,方便用户记忆,本质上一个名字空间系统;
- DNS 就像是我们现实世界里的电话本、查号台,统管着互联网世界里的所有网站,是一
个“超级大管家”; - DNS 是一个树状的分布式查询系统,但为了提高查询效率,外围有多级的缓存;
- 使用 DNS 可以实现基于域名的负载均衡,既可以在内网,也可以在外网。
自己动手,搭建HTTP实验环境
- 现实的网络环境太复杂,有很多干扰因素,搭建“最小化”的环境可以快速抓住重点,
掌握 HTTP 的本质; - 我们选择 Wireshark 作为抓包工具,捕获在 TCP/IP 协议栈中传输的所有流量;
- 我们选择 Chrome 或 Firefox 浏览器作为 HTTP 协议中的 user agent;
- 我们选择 OpenResty 作为 Web 服务器,它是一个 Nginx 的“强化包”,功能非常丰
富; - Telnet 是一个命令行工具,可用来登录主机模拟浏览器操作;
- 在 GitHub 上可以下载到本专栏的专用项目源码,只要把 OpenResty 解压到里面即可完
成实验环境的搭建。
键入网址再按下回车,后面究竟发生了什么?
- 浏览器从地址栏的输入中获得服务器的 IP 地址和端口号;
- 浏览器用 TCP 的三次握手与服务器建立连接;
- 浏览器向服务器发送拼好的报文;
- 服务器收到报文后处理请求,同样拼好报文再发给浏览器;
- 浏览器解析报文,渲染输出页面。
小结
- HTTP 协议基于底层的 TCP/IP 协议,所以必须要用 IP 地址建立连接;
- 如果不知道 IP 地址,就要用 DNS 协议去解析得到 IP 地址,否则就会连接失败;
- 建立 TCP 连接后会顺序收发数据,请求方和应答方都必须依据 HTTP 规范构建和解析报
文; - 为了减少响应时间,整个过程中的每一个环节都会有缓存,能够实现“短路”操作;
- 虽然现实中的 HTTP 传输过程非常复杂,但理论上仍然可以简化成实验里的“两点”模
型。
HTTP报文是什么样子的
报文结构
不过与 TCP/UDP 不同的是,它是一个“纯文本”的协议,所以头数据都是 ASCII 码的文本(比TCP/UDP易读)
header
- HTTP的header一般都很大,HTTP 报文经常是只有 header 而没 body
- 不过这个“大头”也不能太大,虽然 HTTP 协议对 header的大小没有做限制
请求行
描述了客户端想要如何操作服务器端的资源。GET / HTTP/1.1,由请求方法,请求目标,版本号组成
状态行
服务器响应的状态。HTTP/1.1 200 OK,由版本号,状态码,和原因组成
头部字段
唯一的区别是起始行
GET /09-1 HTTP/1.1
Host: www.chrono
GET /09-1 HTTP/1.1
Host : www.chrono
Host后面一定要接着:,不然就会报400 Bad Request
小结
- HTTP 报文结构就像是“大头儿子”,由“起始行 + 头部 + 空行 + 实体”组成,简单地说就是“header+body”;
- HTTP 报文可以没有 body,但必须要有 header,而且header 后也必须要有空行,形象地说就是“大头”必须要带着“脖子”;
- 请求头由“请求行 + 头部字段”构成,响应头由“状态行 + 头部字段”构成;
- 请求行有三部分:请求方法,请求目标和版本号;
- 状态行也有三部分:版本号,状态码和原因字符串;
- 头部字段是 key-value 的形式,用“:”分隔,不区分大小写,顺序任意,除了规定的标准头,也可以任意添加自
定义字段,实现功能扩展; - HTTP/1.1 里唯一要求必须提供的头字段是 Host,它必须出现在请求头里,标记虚拟主机名。
应该如何理解请求方法
目前 HTTP/1.1 规定了八种方法,单词都必须是大写的形式:
- GET:获取资源,可以理解为读取或者下载数据;
- HEAD:获取资源的元信息;
- POST:向资源提交数据,相当于写入或上传数据;
- PUT:类似 POST;
- DELETE:删除资源;
- CONNECT:建立特殊的连接隧道;
- OPTIONS:列出可对资源实行的方法;
- TRACE:追踪请求 - 响应的传输路径。
请求其实就相当于指示,由客户端发出,但是决定权在服务端那里。
GET和HEAD
HEAD与GET类试,但是HEAD只获取资源的元信息,HEAD被看成是GET的简化版。例如:比如,想要检查一个文件是否存在,只要发个 HEAD 请求就可以了,没有必要用 GET 把整个文件都取下来。再比如,要检查文件是否有最新版本,同样也应该用 HEAD,服务器会在响应头里把文件的修改时间传回来。
POST/PUT
PUT 的作用与 POST 类似,也可以向服务器提交数据,但与 POST 存在微妙的不同,通常 POST 表示的是“新建”“create”的含义,而 PUT 则是“修改”“update”的含义。
安全与幂等
在 HTTP 协议里,所谓的“安全”是指请求方法不会“破”服务器上的资源,即不会对服务器上的资源造成实质的
修改。例如,GET和HEAD是获取,不会造成损害,而POST和PUT就会修改资源。
所谓的“幂等”实际上是一个数学用语,被借用到了 HTTP协议里,意思是多次执行相同的操作,结果也都是相同的,即多次“幂”后结果“相等”。
POST 是“新增或提交数据”,多次提交数据会创建多个资源,所以不是幂等的;
PUT是“替换或更新数据”,多次更新一个资源,资源还是会第一次更新的状态,所以是幂等的
POST 理解成 INSERT,把 PUT 理解成 UPDATE
小结
- 请求方法是客户端发出的、要求服务器执行的、对资源的一种操作;
- 请求方法是对服务器的“指示”,真正应如何处理由服务器来决定;
- 最常用的请求方法是 GET 和 POST,分别是获取数据和发送数据;
- HEAD 方法是轻量级的 GET,用来获取资源的元信息;
- PUT 基本上是 POST 的同义词,多用于更新数据;
- “安全”与“幂等”是描述请求方法的两个重要属性,具有理论指导意义,可以帮助我们设计系统。
你能写出正确的网址吗?
URI 不完全等同于网址,它包含有 URL 和 URN两个部分.网址实际上是 URL
URI 的格式
URI 本质上是一个字符串,这个字符串的作用是唯一地标记资源的位置或者名字
URI 的 path 部分必须以“/”开始,也就是必须包含“/”,不要把“/”误认为属于前面 authority。
我们还得到了一个结论:客户端和服务器看到的 URI 是不一样的,服务器看到的只是报文请求行里被删除了协议名和主机名的 URI。所以,你在服务端写的GetMapping全都是写path和后续的部分
URI 的查询参数
这个就不做过多解释,也就是在path后面加?就是参数的开始(不包括?),用&符号连接key和value
URI 的完整格式
主机名之前的**身份信息“user:passwd@”**因为它把敏感信息以明文形式暴露出来,存在严重的安全隐患(一斤不使用了)
第二个多出的部分是查询参数后的片段标识符“#fragment”。片段标识符仅能由浏览器这样的客户端使用,服务器是看
不到的。所以也不会存在
URI 的编码
URI 转义的规则有点“简单粗暴”,直接把非 ASCII 码或特殊字符转换成十六进制字节值,然后前面再加上一个“%”。
小结
-
URI 是用来唯一标记服务器上资源的一个字符串,通常也称为 URL;
-
URI 通常由 scheme、host:port、path 和 query 四个部分组成,有的可以省略;
-
scheme 叫**“方案名”或者“协议名”**,表示资源应该使用哪种协议来访问;
-
“host:port”表示资源所在的主机名和端口号;
-
path 标记资源所在的位置;
-
query 表示对资源附加的额外要求;
-
在 URI 里对“@&/”等特殊字符和汉字必须要做编码,否则服务器收到 HTTP 报文后会无法正确处理。
-
HTTP 协议允许在在请求行里使用完整的 URI,但为什么浏览器没有这么做呢?
没有必要,因为在请求头的字段中都有,没必要重复
-
URI 的查询参数和头字段很相似,都是 key-value 形式,都可以任意自定义,那么它们在使用时该如何区别呢?
query参数针对的是资源(uri),而字段针对的是本次请求,也就是报文。
一个是长期、稳定的,一个是短期、临时的。
响应状态码该怎么用?
Reason 部分是原因短语。状态码,以代码的形式表示服务器对请求的处理结果
状态码
这五类的具体含义是:
1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
2××:成功,报文已经收到并被正确处理;
3××:重定向,资源位置发生变动,需要客户端重新发送请求;
4××:客户端错误,请求报文有误,服务器无法处理;
5××:服务器错误,服务器在处理请求时内部发生了错误。
小结
- 状态码在响应报文里表示了服务器对请求的处理结果;
- 状态码后的原因短语是简单的文字描述,可以自定义;
- 状态码是十进制的三位数,分为五类,从 100 到 599;
- 2××类状态码表示成功,常用的有 200、204、206;
- 3××类状态码表示重定向,常用的有 301、302、304;
- 4××类状态码表示客户端错误,常用的有 400、403、404;
- 5××类状态码表示服务器错误,常用的有 500、501、502、503。
HTTP有哪些特点
- 灵活可扩展
- 可靠传输因为 HTTP 协议是基于 TCP/IP 的,而 TCP 本身是一个“可靠”的传输协议,所以 HTTP 自然也就继承了这个特性。“可靠”只是向使用者提供了一个“承诺”,会在下层用多种手段“尽量”保证数据的完整送达。并不是100%一定可以发送到另一端
- 应用层协议
- 请求 - 应答,“一发一收”“有来有去”
- 无状态,客户端和服务器永远是处在一种“无知”的状态,“没有记忆能力”
但不要忘了 HTTP 是“灵活可扩展”的,虽然标准里没有规定“状态”,但完全能够在协议的框架里给它“打个补丁”,增加这个特性。
小结
-
HTTP 是灵活可扩展的,可以任意添加头字段实现任意功能;(优点)
-
HTTP 是可靠传输协议,基于 TCP/IP 协议“尽量”保证数据的送达;(优点)
-
HTTP 是应用层协议,比 FTP、SSH 等更通用功能更多,能够传输任意数据;(优点)
-
HTTP 使用了请求 - 应答模式,客户端主动发起请求,服务器被动回复请求;(注定是不会再IM场景(即时通信)下使用,因为即时通信,需要服务端主动推送给客户端消息),所以就会萌生出websocket协议
-
HTTP 本质上是无状态的,每个请求都是互相独立、毫无关联的,协议不要求客户端或服务器记录请求相关的信息。比如,在一些长连接场景中,需要保存上下文状态,那么无状态这一点就成为缺点甚至是致命缺点了。但是在客户端-服务端通信中,如果场景不需要保存上下文信息,那么无状态就可以减少一些网络资源消耗,也就是优点了。
HTTP有哪些优点?又有哪些缺点?
- 简单、灵活、易于扩展(最重要也是最突出),容易上手,heder字段,状态码,还有body数据等都可以自己修改和扩展
- 应用广泛、环境成熟从台式机上的浏览器到手机上的各种 APP,从看新闻、泡论坛到购物、理财、“吃鸡”,你很难找到一个没有使用 HTTP 的地方。
- 无状态,
- 优点:所以就不需要额外的资源来记录状态信息,不仅实现上会简单一些,而且还能减轻服务器的负担,能够把更多的 CPU 和内存用来对外提供服务。很容易地组成集群(负载均衡),一位内服务器都是相同的(无状态)。不会因为状态不一致导致处理出错。
- 缺点:无法支持需要连续多个步骤的“事务”操作,例如电商项目中的,添加购物车其实是要验证登陆的(要知道用户的身份),但是,如果每次都询问一遍身份信息就太麻烦了,还要增加了数据的传输量。从而有了cookie技术
- 明文,协议里的报文(准确地说是 header 部分)不使用二进制数据,而是用简单可阅读的文本形式。
- 优点:为我们的开发调试工作带来极大的便利
- 缺点:毫无隐私可言
- 不安全,与“明文”缺点相关但不完全等同的另一个缺点是“不安全”。HTTP 协议也不支持“完整性校验”,数据在传输过程中容易被窜改而无法验证真伪。为了解决 HTTP 不安全的缺点,所以就出现了 HTTPS,这个我们以后再说。
- 性能,不算差,不够好,当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据
小结
- HTTP 最大的优点是简单、灵活和易于扩展;
- HTTP 拥有成熟的软硬件环境,应用的非常广泛,是互联网的基础设施;
- HTTP 是无状态的,可以轻松实现集群化,扩展性能,但有时也需要用 Cookie 技术来实现“有状态”;
- HTTP 是明文传输,数据完全肉眼可见,能够方便地研究分析,但也容易被窃听;
- HTTP 是不安全的,无法验证通信双方的身份,也不能判断报文是否被窜改;
- HTTP 的性能不算差,但不完全适应现在的互联网,还有很大的提升空间。
海纳百川:HTTP的实体数据
从 HTTP 的 body 谈起。
数据类型与编码
MIME 把数据分成了八大类(MIME 是一个很大的标准规范,但HTTP知识取了其中的一部分)
- text,即文本格式的可读数据
- image,图像文件
- audio/video,音频和视频数据
- application,数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有
application/json,application/javascript、application/pdf 等
因为 HTTP 在传输时为了节约带宽,有时候还会压缩数据,为了不要让浏览器继续“猜”,还需要有一个“Encoding type”,告诉数据是用的什么编码格式,这样对方才能正确解压缩,还原出原始的数据。
- gzip,最流行的格式,nginx也一般用这个
- deflate,流行程度仅次于gzip
- br,一种专门为 HTTP 优化的新压缩算法
数据类型使用的头字段
Accept字段标记的是客户端可理解的 MIME type,可以用“,”做分隔符列出多个类型
语言类型与编码
需要明确区分的时候也要使用“typesubtype”的形式,不过这里的格式与数据类型不同,分隔符不是“/”,而是“-”,例如:zh-CN 就表示我们最常使用的汉语。
语言类型使用的头字段
Accept-Language字段标记了客户端可理解的自然语言,也允许用“,”做分隔符列出多个类型
响应报文里用头字段Content-Language告诉客户端实体数据使用的实际语言类型
字符集在 HTTP 里使用的请求头字段是Accept-Charset,但响应头里却没有对应的 Content-Charset,而是在
Content-Type字段的数据类型后面用“charset=xxx”来表示
Accept-Charset: gbk, utf-8
Content-Type: text/html; charset=utf-8
内容协商的质量值
Accept: text/html,application/xml;q=0.9,/;q=0.8
它表示浏览器最希望使用的是 HTML 文件,权重是 1,其次是 XML 文件,权重是 0.9,最后是任意数据类型,权重
是 0.8。服务器收到请求头后,就会计算权重,再根据自己的实际情况优先输出 HTML 或者 XML。
内容协商的结果
内容协商的过程是不透明的加一个Vary字段,记录服务器在内容协商时参考的请求头字段,给出一
点信息,例如:Vary: Accept-Encoding,User-Agent,Accept
Vary 字段可以认为是响应报文的一个特殊的“版本标记“。也就是说,同一个 URI 可能会有多个不同
的“版本”,主要用在传输链路中间的代理服务器实现缓存服务
Vary 字段,缓存代理必须要存储这些不同的版本。例如:,“Vary: Accept-Encoding”“Vary: User-Agent”,下次请求的时候就对比这些字段,看是否能完全匹配
小结
- 数据类型表示实体数据的内容是什么,使用的是 MIMEtype(可以类比成你快递的物品),相关的头字段是 Accept(告诉服务器,客户端支持什么类型,以防服务器发过来的数据不认识,凡是有返回就可以带上) 和 Content-Type(请求和响应里都可以用,作用是指明body数据的类型,凡是有body数据都要带上);
- 数据编码表示实体数据的压缩方式,相关的头字段是Accept-Encoding 和 Content-Encoding;(可以类比成物品的包装方式)
- 语言类型表示实体数据的自然语言,相关的头字段是Accept-Language 和 Content-Language;
- 字符集表示实体数据的编码方式,相关的头字段是Accept-Charset 和 Content-Type;
- 客户端需要在请求头里使用 Accept 等头字段与服务器进行“内容协商”,要求服务器返回最合适的数据;
- Accept 等头字段可以用“,”顺序列出多个可能的选项,还可以用“;q=”参数来精确指定权重。
把大象装进冰箱:HTTP传输大文件的方法
数据压缩
对处理视频,音频这些媒体数据的效果不佳。但是处理文本还是不错的。在Nginx 里就会使用“gzip on”指令,启用对“text/html”的压缩。
分块传输
‘这种“化整为零”的思路在 HTTP 协议里就是“chunked”分块传输编码,在响应报文里用头字段“Transfer-Encoding: chunked”来表示,意思是报文里的 body 部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。
分块传输也可以用于“流式数据”(就是stream,它像从源头持续不断地,慢慢地”流“过来的,而不是一次性,一整块发过来的),对于未知长度的数据,还是挺好用的“Transfer-Encoding: chunked”和“Content-
Length”这两个字段是互斥的。这里补充一下:举个例子,从GitHub上下载源码包,GitHub要实时压缩实时发送,而不是一下子压缩好再发送,这样body的长度一开始就是未知的。所以就要用分块编码,压缩一部分,就发一部分,这部分的长度是已知的,但总长度只有压缩完才能知道。chunked编码用在“流式”收发数据的时候,通常数据是即时生成的,也就是动态数据。
范围请求
想获取一个大文件其中的片段数据(比如看电视剧的时候,跳过片头这个操作或者是拉进度条操作),而分块传输并没有这个能力。HTTP 协议为了满足这样的需求,提出了“范围请求”(range requests)的概念。允许客户端在请求头里使
用专用字段来表示只获取文件的一部分,相当于是客户端的**“化整为零”。服务器必须在响应头**里使用字段“Accept-
Ranges: bytes”明确告知客户端:“我是支持范围请求的”。
请求头Range是 HTTP 范围请求的专用字段格式是**“bytes=x-y”**,其中的 x 和 y 是以字节为单位的数据范围。
服务器收到 Range 字段后,需要做四件事。
1. 它必须检**查范围是否合法**,如果越界就返回416
2. 如果范围正确,服务器就可以根据 Range 头**计算偏移量**,**读取文件的片段了**,返回状态码“**206 Partial**
**Content**”,和 200 的意思差不多,但表示 body 只是原数据的一部分。
3. 服务器要添加一个响应头字段**Content-Range**,格式是**“bytes xy/length”**
4. 直接把片段用 TCP 发给客户端
实现的要点是:
- 先发个 HEAD,看服务器是否支持范围请求,同时获取文件的大小;
- 开 N 个线程,每个线程使用 Range 字段划分出各自负责下载的片段,发请求传输数据;
- 下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用 Range 请求剩下的那一部分就可以了
多段数据
它还支持在Range 头里使用多个“x-y”,一次性获取多个片段数据。这就需要使用MIME 类型:“multipart/byteranges”,表示报文的 body 是由多段字节序列组成的,并且还要用一个参数“boundary=xxx”给出段之间的分隔标记。
小结
- 压缩 HTML 等文本文件是传输大文件最基本的方法;
- 分块传输可以流式收发数据,节约内存和带宽,使用响应头字段“Transfer-Encoding: chunked”来表示,分块的格式是 16 进制长度头 + 数据块;
- 范围请求可以只获取部分数据,即“分块请求”,实现视频拖拽或者断点续传,使用请求头字段“Range”和响应头字段“Content-Range”,响应状态码必须是 206;这个“Content-Range”针对的是原文的数据,与压缩后的数据无关,因为我们在看视频的时候,拖拽进度条,拖拽的是原视频的长度,而不是压缩后的长度。
- 也可以一次请求多个范围,这时候响应报文的数据类型是“multipart/byteranges”,body 里的多个部分会用boundary 字符串分隔。
排队也要讲效率:HTTP的连接管理
短连接
因为客户端与服务器的整个连接过程很短暂,不会与服务器保持长时间的连接状态
长连接
用的就是**“成本均摊”的思路,既然 TCP 的连接和关闭非常耗时间,那么就把这个时间成本由原来的一个“请求 - 应答”均摊到多个“请求 - 应答”上**。
连接相关的头字段
由于长连接对性能的改善效果非常显著,所以在 HTTP/1.1中的连接都会默认启用长连接。“Connection: keepalive”
字段,服务器端通常不会主动关闭连接,但也可以使用一些策略。例如使用nginx的keepalive_timeout
队头阻塞
如果队首的请求因为处理的太慢耽误了时间,那么队列里后面的所有请求也不得不跟着一起等待,结果就是其他的请求承担了不应有的时间成本。
性能优化
要解决队头阻塞,就要进行并发连接,缺陷就是:很容易造成连接数过多,而从被服务器认为是恶意攻击,反而造成”拒绝服务“。
域名分片,还是用数量来解决质量的思路。多开几个域名,而这些域名都指向同一台服务器。
小结
-
早期的 HTTP 协议使用短连接,收到响应后就立即关闭连接,效率很低;
-
HTTP/1.1 默认启用长连接,在一个连接上收发多个请求响应,提高了传输效率;
-
服务器会发送**“Connection: keep-alive”字段表示启用了长连接**;
-
报文头里如果有**“Connection: close”就意味着长连接即将关闭**;
-
过多的长连接会占用服务器资源,所以服务器会用一些策略有选择地关闭长连接;
-
**“队头阻塞”问题会导致性能下降,可以用“并发连接”和“域名分片”**技术缓解。
四通八达:HTTP的重定向和跳转
跳转动作是由浏览器的使用者主动发起的,可以称为“主动跳转”
跳转是由服务器来发起的,称为“被动跳转”
以上方法统一叫“重定向”(Redirection)
重定向的过程
“Location”字段属于响应字段,必须出现在响应报文里。它标记了服务器要求重定向的 URI
注意,在重定向时如果只是在站内跳转,你可以放心地使用相对 URI。但如果要跳转到站外,就必须用绝对 URI。
重定向状态码
- 301俗称**“永久重定向”**意思是原 URI 已经“永久”性地不存在了,今后的所有请求都必须改用新的 URI。
- 302俗称“临时重定向”
- 303 See Other,要求重定向后的请求改为GET 方法
- 307 Temporary Redirect:,重定向后请求里的方法和实体不允许变动
- 308 Permanent Redirect:,不允许重定向后的请求变动,但它是 301“永久重定向”的含义
重定向的应用场景
一个最常见的原因就是“资源不可用”,需要用另一个新的URI 来代替。另一个原因就是“避免重复”
301:原来的 URI 已经不能用了(比如启用了新域名、服务器切换到了新机房、网站目录层次重构),必须用 301“永久重定向”
302:原来的 URI 在将来的某个时间点还会恢复正常,常见的应用场景就是系统维护。另一种用法就是“服务降级”,比如在双十一促销的时候,把订单查询、领积分等不重要的功能入口暂时关闭,保证核心服务能够正常运行。
重定向的相关问题
第一个问题是“性能损耗”,重定向应当适度使用,决不能滥用。
第二个问题是“循环跳转”,可能会出现“A=>B=>C=>A”的无限循环
小结
- 重定向是服务器发起的跳转,要求客户端改用新的 URI重新发送请求,通常会自动进行,用户是无感知的;
- 301/302 是最常用的重定向状态码,分别是“永久重定向”和“临时重定向”;
- 响应头字段 Location 指示了要跳转的 URI,可以用绝对或相对的形式;
- 重定向可以把一个 URI 指向另一个 URI,也可以把多个URI 指向同一个 URI,用途很多;
- 使用重定向时需要当心性能损耗,还要避免出现循环跳转。
一句话,转发是服务器行为,重定向是客户端行为。具体解释可以看这篇文章https://blog.csdn/meiyalei/article/details/2129120
让我知道你是谁:HTTP的Cookie机制
什么是 Cookie?
相当于是服务器给每个客户端都贴上一张小纸条,上面写了一些只有服务器才能理解的数据,需要的时候客户端把这些信息发给服务器,服务器看到 Cookie,就能够认出对方是谁了。
Cookie 的工作过程
这要用到两个字段:响应头字段Set-Cookie和请求头字段Cookie。
Cookie 是由浏览器负责存储的,而不是操作系统。所以,它是“浏览器绑定”的,只能在本浏览器内生效
Cookie 的属性
首先,我们应该设置 Cookie 的生存周期,以使用 Expires 和 Max-Age 两个属性来设置。“Expires”俗称“过期时间”,用的是绝对时间点,可以理解为“截止日期”(deadline)。“Max-Age”用的是相对时间。Expires 和 Max-Age 可以同时出现,但浏览器会优先采用 Max-Age 计算失效期。
其次,我们需要设置 Cookie 的作用域,让浏览器仅发送给特定的服务器和 URI,避免被其他网站盗用
“Domain”和“Path”指定了 Cookie 所属的域名和路径,
最后要考虑的就是Cookie 的安全性了,尽量不要让服务器以外的人看到。
1. **HttpOnly**,此 Cookie 只能通过浏览器 HTTP 协议传输
2. **SameSite**,可以防范“跨站请求伪造”(XSRF)攻击
3. **Secure**,表示这个 Cookie 仅能用 HTTPS 协议加密传输
这些信息,在application面板都能看到
Cookie 的应用
- 身份识别
- 广告跟踪
小结
- Cookie 是服务器委托浏览器存储的一些数据,让服务器有了“记忆能力”;
- 响应报文使用 Set-Cookie 字段发送“key=value”形式的 Cookie 值;
- 请求报文里用 Cookie 字段发送多个 Cookie 值;
- 为了保护 Cookie,还要给它设置有效期、作用域等属性,常用的有 Max-Age、Expires、Domain、HttpOnly 等;
- Cookie 最基本的用途是身份识别,实现有状态的会话事务。
生鲜速递:HTTP的缓存控制
实际上,HTTP 传输的每一个环节基本上都会有缓存,非常复杂。基于“请求 - 应答”模式的特点,可以大致分为客户端缓存和服务器端缓存,因为服务器端缓存经常与代理服务“混搭”在一起,所以今天我先讲客户端——也就是浏览器的缓存。
服务器的缓存控制
Cache-Control里面的值“maxage=30”就是资源的有效时间,这里的 max-age 是响应报文的创建时刻
no_store:不允许缓存,用于某些变化非常频繁的数据,例如秒杀页面;
no_cache:它的字面含义容易与 no_store 搞混,实际的意思并不是不允许缓存,而是可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本;(相当于吃之前必须问超市有没有更新鲜的,有就吃超市里的)
must-revalidate:又是一个和 no_cache 相似的词,它的意思是如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。(相当于保鲜期内可以吃,过期了就要问超市让不让吃。)
客户端的缓存控制
“前进”“后退”“跳转”这些重定向动作中浏览器不会“夹带私货”,只用最基本的请求头,没有“Cache-Control”,所以就会检查缓存,直接利用之前的资源,不再进行网络通信。只有用ctrl+F5刷新才会把Cache-Control”的max-age置为0,不用缓存
条件请求
ETag 是“实体标签”(Entity Tag)的缩写,是资源的一个唯一标识,主要是用来解决修改时间无法准确区分文件变化的问题。
比如,一个文件在一秒内修改了多次,但因为修改时间是秒级,所以这一秒内的新版本无法区分。
再比如,一个文件定期更新,但有时会是同样的内容,实际上没有变化,用修改时间就会误以为发生了变化,传送给浏览器就会浪费带宽。
小结
- 缓存是优化系统性能的重要手段,HTTP 传输的每一个环节中都可以有缓存;
- 服务器使用**“Cache-Control”设置缓存策略**,常用的是“max-age”,表示资源的有效期;
- 浏览器收到数据就会存入缓存,如果没过期就可以直接使用,过期就要去服务器验证是否仍然可用;
- 验证资源是否失效需要使用“条件请求”,常用的是**“if-Modified-Since”和“If-None-Match”**,收到 304 就可以复用缓存里的资源;
- 验证资源是否被修改的条件有两个:“Last-modified”和“ETag”,需要服务器预先在响应报文里设置,搭配条件请求使用;
- 浏览器也可以发送“Cache-Control”字段,使用**“max-age=0”或“no_cache**”刷新数据。
良心中间商:HTTP的代理服务
代理服务
它就是在客户端和服务器原本的通信链路中插入的一个中间环节,也是一台服务器,但提供的是“代理服务”。
代理的作用
简单的说就是“欺上瞒下”
代理最基本的一个功能是负载均衡。
在负载均衡的同时,代理服务还可以执行更多的功能,比如:
健康检查:使用“心跳”等机制监控后端服务器,发现有故障就及时“踢出”集群,保证服务高可用;
安全防护:保护被代理的后端服务器,限制 IP 地址或流量,抵御网络攻击和过载;
加密卸载:对外网使用 SSL/TLS 加密通信认证,而在安全的内网不加密,消除加解密成本;
数据过滤:拦截上下行的数据,任意指定策略修改请求或者响应;
内容缓存:暂存、复用服务器响应
代理相关头字段
首先,代理服务器需要用字段**“Via”标明代理的身份**。
Via 是一个通用字段,请求头或响应头里都可以出现。每当报文经过一个代理节点,代理服务器就会把自身的信息追加到字段的末尾,就像是经手人盖了一个章。
这个via就像微服务那个链路追踪,通过一个id识别整条链路的过程。Via 字段只解决了客户端和源服务器判断是否存在代理的问题,还不能知道对方的真实信息。
服务端为了可以知道客户端的真实IP地址,”,最常用的两个头字段是“X-Forwarded-For”和“X-Real-IP”。
X-Forwarded-For:追加的是请求方的 IP 地址(而via是没经过一个代理节点追加一个)
X-Real-IP:就是记录客户端 IP地址
代理协议
上面这些头字段存在几个问题:
- 通过这些头字段操作代理信息还必须要解析HTTP报文头,降低了转发的性能
- 要在原始头上修改原始报文,比如:在HTTPS通信中是不能被修改的
“代理协议”(The PROXY protocol),它由知名的代理软件HAProxy 所定义,也是一个“事实标准”,被广泛采用(注意并不是 RFC)。
“代理协议”有 v1 和 v2 两个版本,v1 和 HTTP 差不多,也是明文,而 v2 是二进制格式。
v1:PROXY TCP4 1.1.1.1 2.2.2.2 55555 80\r\n
PROXY +ip地址类型+客户端ip+请求方ip+请求方端口+应答方端口+\r\n结束
服务器只需要解析第一行就能拿到客户端的ip了
小结
- HTTP 代理就是客户端和服务器通信链路中的一个中间环节,为两端提供“代理服务”;
- 代理处于中间层,为 HTTP 处理增加了更多的灵活性,可以实现负载均衡、安全防护、数据过滤等功能;
- 代理服务器需要使用字段“Via”标记自己的身份,多个代理会形成一个列表;
- 如果想要知道客户端的真实 IP 地址,可以使用字段**“X-Forwarded-For”和“X-Real-IP**”;
- 专门的“代理协议”可以在不改动原始报文的情况下传递客户端的真实 IP。
补充:因为HTTP是明文传输的,请求头容易被篡改,所有**“X-Forwarded-For”**等字段不完全可信
冷链周转:HTTP的缓存代理(难点,为明白,要更多去结合nginx来学)
HTTP 的服务器缓存功能主要由代理服务器来实现(即缓存代理),而源服务器系统内部虽然也经常有各种缓存(如 Memcache、Redis、Varnish 等),但与 HTTP 没有太多关系,所以这里暂且不说。
缓存代理服务
简单来说,就是在代理服务器上加了缓存功能,让代理服务器既是客户端(要转发请求给服务端)又是服务器(可以处理请求,生成缓存),另一方面也可以说这个服务器既不是客户端也不是服务器,因为它只是数据的中转站,并不是真正的数据消费者和生产者
源服务器的缓存控制
private和public,private是表示缓存只能在客户端保存,对用户私有。public是缓存完全开放,谁都可以存,谁都可以用
must-revalidate,只要过期就必须回源服务器验证
proxy-revalidate,只要求代理的缓存过期后必须验证,客户端不必回源,只验证到代理这个环节就行了。
“s-maxage”,只限定在代理上能够存多久
“no-transform”,禁入进入缓存下来的数据做一些优化
源服务器在设置完“Cache-Control”后必须要为报文加上“Lastmodified”或“ETag”字段。否则,客户端和代理后面就无法使用条件请求来验证缓存是否有效,也就不会有 304 缓存重定向。
客户端的缓存控制
“max-stale”的意思是如果代理上的缓存过期了也可以接受,但不能过期太多**,超过 x 秒也会不要**。“min-fresh”的意思是缓存必须有效,而且必须在 x 秒后依然有效。
比如,草莓上贴着标签“max-age=5”,现在已经在冰柜里存了 7 天。如果有请求“max-stale=2”,意思是过期两天也能接受,所以刚好能卖出去。
但要是“min-fresh=1”,这是绝对不允许过期的,就不会买走。这时如果有另外一个菠萝是“max-age=10”,那么“7+1<10”,在一天之后还是新鲜的,所以就能卖出去。
only-if-cached,表示只接受代理缓存的数据,不接受源服务器的响应。如果代理上没有缓存或者缓存过期,就应该给客户端返回一个504
Purge,缓存清理
小结
- 计算机领域里最常用的性能优化手段是“时空转换”,也就是“时间换空间”或者“空间换时间”,HTTP 缓存属于后者;
- 缓存代理是增加了缓存功能的代理服务,缓存源服务器的数据,分发给下游的客户端;
- “**Cache-Control”**字段也可以控制缓存代理,常用的有“private”“smaxage”“no-transform”等,同样必须配合“Lastmodified”“ETag”等字段才能使用;
- 缓存代理有时候也会带来负面影响,缓存不良数据,需要及时刷新或删除。
HTTPS是什么?SSL/TLS又是什么?
为什么要有 HTTPS?
简单的回答是“因为 HTTP 不安全”。
什么是安全?
通常认为,如果通信过程具备了四个特性,就可以认为是“安全”的,这四个特性是:机密性、完整性,身份认证和不可否认
- 机密性,不能让不相关的人看到不该看的东西
- 完整性,指数据在传输过程中没有被窜改
- 身份认证,也就是“证明你真的是你”
- 不可否认,不能“说话不算数”“耍赖皮”
什么是 HTTPS
新的协议名“https”,默认端口号 443,
由“HTTP over TCP/IP”变成了“HTTP over SSL/TLS”
SSL/TLS
SSL 即安全套接层,在 1999 年把它改名为 TLS(传输层安全,Transport Layer Security)
OpenSSL
它是一个著名的开源密码学程序库和工具包,几乎支持所有公开的加密算法和协议,已经成为了事实上的标准,许多应用软件都会使用它作为底层库来实现 TLS 功能,包括常用的 Web 服务器 Apache、Nginx 等
小结
- 因为 HTTP 是明文传输,所以不安全,容易被黑客窃听或窜改;
- 通信安全必须同时具备机密性、完整性,身份认证和不可否认这四个特性;
- HTTPS 的语法、语义仍然是 HTTP,但把下层的协议由 TCP/IP 换成了 SSL/TLS;
- SSL/TLS 是信息安全领域中的权威标准,采用多种先进的加密技术保证通信安全;
- OpenSSL 是著名的开源密码学工具包,是 SSL/TLS 的具体实现。
固若金汤的根本(上):对称加密与非对称加密
实现机密性最常用的手段是“加密”,就是把消息用某种方式转换成谁也看不懂(密文)的乱码,只有掌握特殊“钥匙”(密钥)的人才能再转换出原始文本。加密可以分为两大类:对称加密和非对称加密
对称加密
加密和解密时使用的密钥都是同一个,目前常用的只有 AES 和ChaCha20。
AES 的意思是“高级加密标准”(Advanced Encryption Standard),密钥长度可以是128、192 或 256。它是 DES 算法的替代者,安全强度很高,性能也很好,而且有的硬件还会做特殊优化,所以非常流行,是应用最广泛的对称加密算法。
ChaCha20 是 Google 设计的另一种加密算法,密钥长度固定为 256 位,纯软件运行性能要超过 AES,曾经在移动客户端上比较流行,但 ARMv8 之后也加入了 AES 硬件优化,所以现在不再具有明显的优势,但仍然算得上是一个不错算法。
加密分组模式
对称算法还有一个“分组模式”的概念,它可以让算法用固定长度的密钥加密任意长度的明文,把小秘密(即密钥)转化为大秘密(即密文)。
最新的分组模式被称为 AEAD(Authenticated Encryption withAssociated Data),在加密的同时增加了认证的功能,常用的是 GCM、CCM 和Poly1305。
非对称加密
其中有一个很大的问题:如何把密钥安全地传递给对方,术语叫“密钥交换”
为了解决上述问题,所以,就出现了非对称加密(也叫公钥加密算法)
它有两个密钥,一个叫“公钥”(public key),一个叫“私钥”(private key)。
公钥和私钥有个特别的“单向”性
RSA 可能是其中最著名的一个,几乎可以说是非对称加密的代名词,它的安全性基于“整数分解”的数学难题,使用两个超大素数的乘积作为生成密钥的材料,想要从公钥推算出私钥是非常困难的。
ECC(Elliptic Curve Cryptography)是非对称加密里的“后起之秀”,它基于“椭圆曲线离散对数”的数学难题,使用特定的曲线方程和基点生成公钥和私钥,子算法 ECDHE 用于密钥交换,ECDSA 用于数字签名。
混合加密
在通信刚开始的时候使用非对称算法,比如 RSA、ECDHE,首先解决密钥交换的问题然后用随机数产生对称算法使用的“会话密钥”(session key),再用公钥加密
这个图的意思是:用非对称加密解决对称加密的密钥交换问题
小结
- 加密算法的核心思想是“把一个小秘密(密钥)转化为一个大秘密(密文消息)”,守住了小秘密,也就守住了大秘密;
- 对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换,常用的有 AES 和 ChaCha20;
- 非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢,常用的有 RSA 和 ECC;
- 把对称加密和非对称加密结合起来就得到了“又好又快”的混合加密,也就是 TLS 里使用的加密方式。
固若金汤的根本(下):数字签名与证书
机密性的基础上还必须加上完整性、身份认证等特性,才能实现真正的安全。
摘要算法
实现完整性的手段主要是摘要算法(Digest Algorithm),也就是常说的散列函数、哈希函数
你可以把摘要算法近似地理解成一种特殊的压缩算法,它能够把任意长度的数据“压缩”成固定长度、而且独一无二的“摘要”字符串,就好像是给这段数据生成了一个数字“指纹”。
摘要算法理解成特殊的“单向”加密算法,它只有算法,没有密钥,加密后的数据无法解密,不能从摘要逆推出原文。
完整性
真正的完整性必须要建立在机密性之上,在混合加密系统里用会话密钥加密消息和摘要,这样黑客无法得知明文,也就没有办法动手脚了。(哈希消息认证码)
数字签名
使用私钥再加上摘要算法,就能够实现“数字签名”,同时实现“身份认证”和“不可否认”。
数字签名的原理其实很简单,就是把公钥私钥的用法反过来,之前是公钥加密、私钥解密,现在是私钥加密、公钥解密。
数字证书和 CA
还有一个“公钥的信任”问题。因为谁都可以发布公钥,我们还缺少防止黑客伪造公钥的手段,也就是说,怎么来判断这个公钥就是你或者某宝的公钥呢?
这时候可以用别的私钥(CA)给公钥签名。
CA 对公钥的签名认证也是有格式的,不是简单地把公钥绑定在持有者身份上就完事了,还要包含序列号、用途、颁发者、有效时间等等,把这些打成一个包再签名,完整地证明公钥关联的各种信息,形成“数字证书”(Certificate)。
CA 怎么证明自己呢?
证书体系的弱点
如果 CA 失误或者被欺骗,签发了错误的证书,虽然证书是真的,可它代表的网站却是假的。还有一种更危险的情况,CA 被黑客攻陷,或者 CA 有恶意,因为它(即根证书)是信任的源头,整个信任链里的所有证书也就都不可信了。
针对第一种,开发出了 CRL(证书吊销列表,Certificate revocation list)和 OCSP(在线证书状态协议,Online Certificate Status Protocol),及时废止有问题的证书。
对于第二种,因为涉及的证书太多,就只能操作系统或者浏览器从根上“下狠手”了,撤销对 CA 的信任,列入“黑名单”,这样它颁发的所有证书就都会被认为是不安全的。
小结
- 摘要算法用来实现完整性,能够为数据生成独一无二的“指纹”,常用的算法是 SHA-2;
- 数字签名是私钥对摘要的加密,可以由公钥解密后验证,实现身份认证和不可否认;
- 公钥的分发需要使用数字证书,必须由 CA 的信任链来验证,否则就是不可信的;
- 作为信任链的源头 CA 有时也会不可信,解决办法有 CRL、OCSP,还有终止信任。
信任始于握手:TLS1.2连接过程解析
HTTPS 建立连接
现在是 HTTPS 协议,它需要再用另外一个“握手”过程,这个“握手”过程与 TCP 有些类似,是 HTTPS 和 TLS 协议里最重要、最核心的部分,懂了它,你就可以自豪地说自己“掌握了 HTTPS”。
TLS 协议的组成
- 记录协议(Record Protocol)规定了 TLS 收发数据的基本单位:记录(record)。
- 警报协议,职责是向对方发出警报信息,有点像是 HTTP 协议里的状态码
- 握手协议,浏览器和服务器会在握手过程中协商 TLS 版本号、随机数、密码套件等信息,然后交换证书和密钥参数,最终双方协商得到会话密钥,用于后续的混合加密系统。
- 变更密码规范协议,就是一个“通知”,告诉对方,后续的数据都将使用加密保护
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ktBVN1C1-1589182906534)(C:%5CUsers%5Cprofessor%5CDesktop%5Cjava%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%5C26%E4%B8%A8%E4%BF%A1%E4%BB%BB%E5%A7%8B%E4%BA%8E%E6%8F%A1%E6%89%8B%EF%BC%9ATLS1.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RG0qqW5b-1589182906535)(C:%5CUsers%5Cprofessor%5CDesktop%5Cjava%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%5C26%E4%B8%A8%E4%BF%A1%E4%BB%BB%E5%A7%8B%E4%BA%8E%E6%8F%A1%E6%89%8B%EF%BC%9ATLS11.jpg)]
RSA 握手过程
双向认证
上面说的是“单向认证”握手过程,建立了安全的连接之后,用账号和密码就能确认用户的身份
但为了防止账号、密码被盗,有的时候(比如网上银行)还会使用 U 盾给用户颁发客户端证书,实现“双向认证”,这样会更加安全。
小结
- HTTPS 协议会先与服务器执行 TCP 握手,然后执行 TLS 握手,才能建立安全连接;
- 握手的目标是安全地交换对称密钥,需要三个随机数,第三个随机数“Pre-Master”必须加密传输,绝对不能让黑客破解;
- “Hello”消息交换随机数,“Key Exchange”消息交换“Pre-Master”;
- “Change Cipher Spec”之前传输的都是明文,之后都是对称密钥加密的密文。
更好更快的握手:TLS1.3特性解析
TLS1.3 的三个主要改进目标:兼容、安全与性能。
小结
- 为了兼容 1.1、1.2 等“老”协议,TLS1.3 会“伪装”成 TLS1.2,新特性在“扩展”里实现;
- 1.1、1.2 在实践中发现了很多安全隐患,所以 TLS1.3 大幅度删减了加密算法,只保留了ECDHE、AES、ChaCha20、SHA-2 等极少数算法,强化了安全;
- TLS1.3 也简化了握手过程,完全握手只需要一个消息往返,提升了性能。
连接太慢该怎么办:HTTPS的优化
HTTPS 连接大致上可以划分为两个部分,第一个是建立 连接时的非对称加密握手,第二个是握手后的对称加密报文传输。
硬件优化
- 首先,你可以选择更快的 CPU
- 其次,你可以选择“SSL 加速卡”
软件优化
一个是软件升级,一个是协议优化。
小结
- 可以有多种硬件和软件手段减少网络耗时和计算耗时,让 HTTPS 变得和 HTTP 一样快,最可行的是软件优化;
- 应当尽量使用 ECDHE 椭圆曲线密码套件,节约带宽和计算量,还能实现“FalseStart”;
- 服务器端应当开启“OCSP Stapling”功能,避免客户端访问 CA 去验证证书;
- 会话复用的效果类似 Cache,前提是客户端必须之前成功建立连接,后面就可以用“Session ID”“Session Ticket”等凭据跳过密钥交换、证书验证等步骤,直接开始加密通信。
我应该迁移到HTTPS吗
“迁移到 HTTPS”已经不是“要不要做”的问题,而是“要怎么做”的问题了
小结
- 从 HTTP 迁移到 HTTPS 是“大势所趋”,能做就应该尽早做;
- 升级 HTTPS 首先要申请数字证书,可以选择免费好用的“Let’s Encrypt”;
- 配置 HTTPS 时需要注意选择恰当的 TLS 版本和密码套件,强化安全;
- 原有的 HTTP 站点可以保留作为过渡,使用 301 重定向到 HTTPS
HTTP/2特性概览
头部压缩
HTTP/2 把“头部压缩”作为性能改进的一个重点,优化的方式你也肯定能想到,还是“压缩”。开发了专门的“HPACK”算法
二进制格式
虚拟的“流”
HTTP/2 为此定义了一个“流”(Stream)的概念,它是二进制帧的双向传输序列,同一个消息往返的帧会分配一个唯一的流 ID。
用“流”同时发送多个“碎片化”的消息,这就是常说的“多路复用”( Multiplexing)——多个往返通信都复用一个连接来处理。
在“流”的层面上看,消息是一些有序的“帧”序列,而在“连接”的层面上看,消息却是乱序收发的“帧”(因为收发已经不是单纯的一问一答了)不会再出现“队头阻塞”问题,降低了延迟,大幅度提高了连接的利用率。
服务器不再是完全被动地响应请求,也可以新建“流”主动向客户端发送消息。
强化安全
为了区分“加密”和“明文”这两个不同的版本,HTTP/2 协议定义了两个字符串标识符:“h2”表示加密的 HTTP/2,“h2c”表示明文的 HTTP/2,多出的那个字母“c”的意思是“clear text”。
协议栈
小结
- HTTP 协议取消了小版本号,所以 HTTP/2 的正式名字不是 2.0;
- HTTP/2 在“语义”上兼容 HTTP/1,保留了请求方法、URI 等传统概念;
- HTTP/2 使用“HPACK”算法压缩头部信息,消除冗余数据节约带宽;
- HTTP/2 的消息不再是“Header+Body”的形式,而是分散为多个二进制“帧”;
- HTTP/2 使用虚拟的“流”传输消息,解决了困扰多年的“队头阻塞”问题,同时实现了“多路复用”,提高连接的利用率;
- HTTP/2 也增强了安全性,要求至少是 TLS1.2,而且禁用了很多不安全的密码套件。
HTTP/2内核剖析
连接前言
TLS 握手成功之后,客户端必须要发送一个“连接前言”(connection preface),用来确认建立 HTTP/2 连接。
头部压缩
“HPACK”算法是专门为压缩 HTTP 头部定制的算法,与 gzip、zlib 等压缩算法不同,它是一个**“有状态”的算法,需要客户端和服务器各自维护一份“索引表”,也可以说是“字典”(这有点类似 brotli),压缩和解压缩就是查表和更新表**的操作。
二进制帧
流与多路复用
- 流是可并发的,一个 HTTP/2 连接上可以同时发出多个流传输数据,也就是并发多请求,实现“多路复用”;
- 客户端和服务器都可以创建流,双方互不干扰;
- 流是双向的,一个流里面客户端和服务器都可以发送或接收数据帧,也就是一个“请求-应答”来回;
- 流之间没有固定关系,彼此独立,但流内部的帧是有严格顺序的;
- 流可以设置优先级,让服务器优先处理,比如先传 HTML/CSS,后传图片,优化用户体验;
- 流 ID 不能重用,只能顺序递增,客户端发起的 ID 是奇数,服务器端发起的 ID 是偶数;
- 在流上发送“RST_STREAM”帧可以随时终止流,取消接收或发送;
- 第 0 号流比较特殊,不能关闭,也不能发送数据帧,只能发送控制帧,用于流量控制。这里
流状态转换
小结
- HTTP/2 必须先发送一个“连接前言”字符串,然后才能建立正式连接;
- HTTP/2 废除了起始行,统一使用头字段,在两端维护字段“Key-Value”的索引表,使用“HPACK”算法压缩头部;
- HTTP/2 把报文切分为多种类型的二进制帧,报头里最重要的字段是流标识符,标记帧属于哪个流;
- 流是 HTTP/2 虚拟的概念,是帧的双向传输序列,相当于 HTTP/1 里的一次“请求 -应答”;
- 在一个 HTTP/2 连接上可以并发多个流,也就是多个“请求 - 响应”报文,这就是“多路复用”。
补充:其实HTTP/2从本质上来看是队头阻塞的,因为http1里的请求和应答是没有序号标识的,导致了无法将乱序的请求和应答关联起来,也就是必须等待起始请求的应答先返回,则后续请求的应答都会延迟。而http2采用了虚拟的“流”,每次请求应答都会分配同一个流id,而同一个流id里的帧又都是有序的,这样根据流id就可以标识出同一次的请求应答,不用再等待起始请求的应答先返回了,解决了“队头阻塞”。但是底层还是采用的TCP协议,该等的还是要等,只不过是同步非阻塞等而已(相当于linux的epoll模型)
htpp/2还是无状态,流状态只是表示流是否建立,单次请求响应的状态,并非会话级的状态保持(语法上有状态,语义上无状态)
每一个请求响应都是一个流,流和流之间可以并行,流内的帧还是有序串行。
未来之路:HTTP/3展望
它在 HTTP/2 的基础上又实现了质的飞跃,真正“完美”地解决了“队头阻塞”问题。
QUIC 协议
它把下层的 TCP“抽掉”了,换成了 UDP。因为 UDP 是无序的,包之间没有依赖关系,所以就从根本上解决了“队头阻塞”。
QUIC 的特点
QUIC 基于 UDP,而 UDP 是“无连接”的,根本就不需要“握手”和“挥手”,所以天生就要比 TCP 快。
为了防止网络上的中间设备(Middle Box)识别协议的细节,QUIC 全面采用加密通信,可以很好地抵御窜改和“协议僵化”(ossification)。
###QUIC 内部细节
QUIC 的基本数据传输单位是包(packet)和帧(frame),一个包由多个帧组成,包面向的是“连接”,帧面向的是“流”。
QUIC 使用不透明的“连接 ID”来标记通信的两个端点,客户端和服务器可以自行选择一组 ID 来标记自己,这样就解除了 TCP 里连接对“IP 地址 + 端口”(即常说的四元组)的强绑定,支持“连接迁移”(Connection Migration)。
比如你下班回家,手机会自动由 4G 切换到 WiFi。这时 IP 地址会发生变化,TCP 就必须重新建立连接。而 QUIC 连接里的两端连接 ID 不会变,所以连接在“逻辑上”没有中断,它就可以在新的 IP 地址上继续使用之前的连接,消除重连的成本,实现连接的无缝迁移。
小结
- HTTP/3 基于 QUIC 协议,**完全解决了“队头阻塞”问题,**弱网环境下的表现会优于HTTP/2;
- QUIC 是一个新的传输层协议,建立在 UDP 之上,实现了可靠传输;
- QUIC 内含了 TLS1.3,只能加密通信,支持 0-RTT 快速建连;
- QUIC 的连接使用“不透明”的连接 ID,不绑定在“IP 地址 + 端口”上,支持“连接迁移”;
- QUIC 的流与 HTTP/2 的流很相似,但分为双向流和单向流;
- HTTP/3 没有指定默认端口号,需要用 HTTP/2 的扩展帧“Alt-Svc”来发现。
WAF:保护我们的网络服务
Web 服务遇到的威胁
- “DDoS”攻击,有时候也叫“洪水攻击”。黑客控制多台僵尸客户端,大量向服务器发起请求,让服务器无法提供正常服务
- SQL注入,它利用了服 务器字符串拼接形成 SQL 语句的漏洞,构造出非正常的 SQL 语句,获取数据库内部的敏感 信息。
- “跨站脚本”(XSS)攻击, 它属于“JS 代码注入”,利用 JavaScript 脚本获取未设防的 Cookie。
网络应用防火墙“WAF“
WAF 就是一种**“HTTP 入侵检测和防御系统”**。
通常一款产品能够称为 WAF,要具备下面的一些功能:
1. IP 黑名单和白名单,拒绝黑名单上地址的访问,或者只允许白名单上的用户访问;
2. URI 黑名单和白名单,与 IP 黑白名单类似,允许或禁止对某些 URI 的访问;
3. 防护 DDoS 攻击,对特定的 IP 地址限连限速;
4. 过滤请求报文,防御“代码注入”攻击
5. 过滤响应报文,防御敏感信息外泄;
6. 审计日志,记录所有检测到的入侵操作。
使用 WAF 最好“不要重新发明轮子”,而是使用现有的、比较成熟的、经过实际考 验的 WAF 产品。
全面的 WAF 解决方案
这里我就要“隆重”介绍一下 WAF 领域里的最顶级产品了:ModSecurity,它可以说是 WAF 界“事实上的标准”。
小结
- Web 服务通常都运行在公网上,容易受到“DDoS”、“代码注入”等各种黑客攻 击,影响正常的服务,所以必须要采取措施加以保护;
- WAF 是一种“HTTP 入侵检测和防御系统”,工作在七层,为 Web 服务提供全面的防 护;
- ModSecurity 是一个开源的、生产级的 WAF 产品,核心组成部分是“规则引 擎”和“规则集”,两者的关系有点像杀毒引擎和病毒特征库;
- WAF 实质上是模式匹配与数据过滤,所以会消耗 CPU,增加一些计算成本,降低服务 能力,使用时需要在安全与性能之间找到一个“平衡点”。
CDN:加速我们的网络服务
为什么要有网络加速
如果仅用现有的 HTTP 传输方式,大多数网站都会访问速度缓慢、用户体 验糟糕。
什么是 CDN?
它就是专门为解决“长距离”上网络访问速度慢而诞生的一种网络应用服务。
CDN 有三个关键词:“内容”“分发”和“网络”。
网络:CDN 的最核心原则是“就近访问”
内容:“内容”其实就是 HTTP 协议里的“资源”,比如超文本、图片、视频、 应用程序安装包等等**。“我们不生产内容,我们只是内容的搬运工。”**
CDN 的负载均衡
全局负载均衡和缓存系统,对应的是DNS和缓存代理技术
CDN 的缓存代理
这里就有两个 CDN 的关键概念:“命中”和“回源”。
“命中”就是指用户访问的资源恰好在缓存系统里,可以直接返回给用户;“回源”则正相反,缓存里没有,必须用代理的方式回源站取。
提高命中率:1. 增加硬件成本;2. 使用各种缓存中间件,如:redis;3. 分层(多级缓存),减少真正的回源
WebSocket:沙盒里的TCP
为什么要有 WebSocket
其实 WebSocket 与 HTTP/2 一样,都是为了解决 HTTP 某方面的缺陷而诞生的。HTTP/2 针对的是“队头阻塞”,而 WebSocket 针对的是“请求 - 应答”通信模式。
这就导致 HTTP 难以应用在动态页面、即时消息、网络游戏 等要求**“实时通信”**的领域。
WebSocket 的特点
WebSocket 是一个真正“全双工”的通信协议,与 TCP 一样,客户端和服务器都可以随时向对方发送数据,而不用像 HTTP“你拍一,我拍一”那么“客套”。
WebSocket 的帧结构
WebSocket 更侧重于**“实时通信”,而 HTTP/2 更侧重于提高传输效率**,所以两者的帧结构也有很大的区别。
WebSocket 的握手
WebSocket 的握手是一个标准的 HTTP GET 请求,但要带上两个协议升级的专用头字 段:Connection: Upgrade”,表示要求协议“升级”;“Upgrade: websocket”,表示要“升级”成 WebSocket 协议。
小结
- HTTP 的“请求 - 应答”模式不适合开发“实时通信”应用,效率低,难以实现动态页 面,所以出现了 WebSocket;
- WebSocket 是一个“全双工”的通信协议,相当于对 TCP 做了一层“薄薄的包装”, 让它运行在浏览器环境里;
- WebSocket 使用兼容 HTTP 的 URI 来发现服务,但定义了新的协议 名“ws”和“wss”,端口号也沿用了 80 和 443;
- WebSocket 使用二进制帧,结构比较简单,特殊的地方是有个“掩码”操作,客户端 发数据必须掩码,服务器则不用;
- WebSocket 利用 HTTP 协议实现连接握手,发送 GET 请求要求“协议升级”,握手 过程中有个非常简单的认证机制,目的是防止误连接。
总结
”
内容:“内容”其实就是 HTTP 协议里的“资源”,比如超文本、图片、视频、 应用程序安装包等等**。“我们不生产内容,我们只是内容的搬运工。”**
CDN 的负载均衡
全局负载均衡和缓存系统,对应的是DNS和缓存代理技术
CDN 的缓存代理
这里就有两个 CDN 的关键概念:“命中”和“回源”。
“命中”就是指用户访问的资源恰好在缓存系统里,可以直接返回给用户;“回源”则正相反,缓存里没有,必须用代理的方式回源站取。
提高命中率:1. 增加硬件成本;2. 使用各种缓存中间件,如:redis;3. 分层(多级缓存),减少真正的回源
WebSocket:沙盒里的TCP
为什么要有 WebSocket
其实 WebSocket 与 HTTP/2 一样,都是为了解决 HTTP 某方面的缺陷而诞生的。HTTP/2 针对的是“队头阻塞”,而 WebSocket 针对的是“请求 - 应答”通信模式。
这就导致 HTTP 难以应用在动态页面、即时消息、网络游戏 等要求**“实时通信”**的领域。
WebSocket 的特点
WebSocket 是一个真正“全双工”的通信协议,与 TCP 一样,客户端和服务器都可以随时向对方发送数据,而不用像 HTTP“你拍一,我拍一”那么“客套”。
WebSocket 的帧结构
WebSocket 更侧重于**“实时通信”,而 HTTP/2 更侧重于提高传输效率**,所以两者的帧结构也有很大的区别。
WebSocket 的握手
WebSocket 的握手是一个标准的 HTTP GET 请求,但要带上两个协议升级的专用头字 段:Connection: Upgrade”,表示要求协议“升级”;“Upgrade: websocket”,表示要“升级”成 WebSocket 协议。
小结
- HTTP 的“请求 - 应答”模式不适合开发“实时通信”应用,效率低,难以实现动态页 面,所以出现了 WebSocket;
- WebSocket 是一个“全双工”的通信协议,相当于对 TCP 做了一层“薄薄的包装”, 让它运行在浏览器环境里;
- WebSocket 使用兼容 HTTP 的 URI 来发现服务,但定义了新的协议 名“ws”和“wss”,端口号也沿用了 80 和 443;
- WebSocket 使用二进制帧,结构比较简单,特殊的地方是有个“掩码”操作,客户端 发数据必须掩码,服务器则不用;
- WebSocket 利用 HTTP 协议实现连接握手,发送 GET 请求要求“协议升级”,握手 过程中有个非常简单的认证机制,目的是防止误连接。
总结
本次学习笔记来自于极客时间的http专栏,内容仅供个人复习用,如有写得不对的地方多多包涵!!
版权声明:本文标题:深入学习HTTP协议 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dongtai/1728561879a1163921.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论