介绍

image.png

SSL:(Secure Socket Layer,安全套接字层),位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层。SSL通过互相认证、使用数字签名确保完整性、使用加密确保私密性,以实现客户端和服务器之间的安全通讯。该协议由两层组成:SSL记录协议和SSL握手协议。
TLS:(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS记录协议和TLS握手协议。
SSL是Netscape开发的专门用户保护Web通讯的,目前版本为3.0。最新版本的TLS 1.0是IETF(工程任务组)制定的一种新的协议,它建立在SSL 3.0协议规范之上,是SSL 3.0的后续版本。两者差别极小,可以理解为SSL 3.1,它是写入了RFC的,是SSL的升级版。

Http请求的过程与原理

HTTP是一个基于TCP/IP协议来传递数据的超文本传输协议,传输的数据类型有HTML,图片等。以访问百度有例子,看下一次Http的请求过程吧
image.png
Http请求过程

  1. 客户端进行DNS域名解析,得到对应的IP地址
  2. 根据这个IP,找到对应的服务器建立连接(三次握手)
  3. 建立TCP连接后发起HTTP请求(一个完整的http请求报文)
  4. 服务器响应HTTP请求,客户端得到html代码
  5. 客户端解析html代码,用html代码中的资源(如js,css,图片等等)渲染页面。
  6. 服务器关闭TCP连接(四次挥手)

HTTP 报文组成部分

http报文:由请求报文和响应报文组成
请求报文:由请求行、请求头、空行、请求体四部分组成
响应报文:由状态行、响应头、空行、响应体四部分组成

  • 请求行:包含http方法,请求地址,http协议以及版本
  • 请求头/响应头:就是一些key:value来告诉服务端我要哪些内容,要注意什么类型等,请求头/响应头每一个字段详解
  • 空行:用来区分首部与实体,因为请求头都是key:value的格式,当解析遇到空行时,服务端就知道下一个不再是请求头部分,就该当作请求体来解析了
  • 请求体:请求的参数
  • 状态行:包含http协议及版本、数字状态码、状态码英文名称
  • 响应体:服务端返回的数据

HTTP 请求方法(9种)

HTTP1.0:GET、POST、HEAD
HTTP1.1:PUT、PATCH、DELETE、OPTIONS、TRACE、CONNECT

方法 描述
GET 获取资源
POST 传输资源,通常会造成服务器资源的修改
HEAD 获得报文首部
PUT 更新资源
PATCH 对PUT的补充,对已知资源部分更新 菜鸟
DELETE 删除资源
OPTIONS 列出请求资源支持的请求方法,用来跨域请求
TRACE 追踪请求/响应路径,用于测试或诊断
CONNECT 将连接改为管道方式用于代理服务器(隧道代理下面有讲)

GET 和 POST 的区别

  • GET在浏览器回退时是无害的,而POST会再次发起请求
  • GET请求会被浏览器主动缓存,而POST不会,除非手动设置
  • GET请求参数会被安逗保留在浏览器历史记录里(保存在浏览器历史记录中,还可能保存在web服务器的日志中),而POST中的参数不会被保留
  • GET请求在URL中传递的参数有长度限制(浏览器限制大小不同),而POST没有限制
  • GET参数通过URL传递,POST放在Request body中
  • GET产生的URL地址可以被收藏,而POST不可以
  • GET没有POST安全,因为GET请求参数直接暴露在URL上,所以不能用来传递敏感信息
  • GET请求只能进行URL编码,而POST支持多种编码方式
  • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制
  • GET产生一个TCP数据包,POST产生两个数据包(Firefox只发一次)。GET浏览器把 http header和data一起发出去,响应成功200,POST先发送header,响应100 continue,再发送data,响应成功200

    http1.0 (1996)

  • 任意数据类型都可以发送

  • 有GET、POST、HEAD三种方法
  • 无法复用TCP连接(长连接)
  • 有丰富的请求响应头信息。以header中的Last-Modified/If-Modified-Since和Expires作为缓存标识

    http1.1 (1997)

  • 引入更多的请求方法类型PUT、PATCH、DELETE、OPTIONS、TRACE、CONNECT

  • 引入长连接,就是TCP连接默认不关闭,可以被多个请求复用,通过请求头connection:keep-alive设置
  • 引入管道连接机制,可以在同一TCP连接里,同时发送多个请求
  • 强化了缓存管理和控制Cache-Control、ETag/If-None-Match
  • 支持分块响应,断点续传,利于大文件传输,能过请求头中的Range实现
  • 使用了虚拟网络,在一台物理服务器上可以存在多个虚拟主机,并且共享一个IP地址


缺点:主要是连接缓慢,服务器只能按顺序响应,如果某个请求花了很长时间,就会出现请求队头阻塞
虽然出了很多优化技巧:为了增加并发请求,做域名拆分、资源合并、精灵图、资源预取…等等

会话复用

会话复用有两种方式:Session ID 和 Session Ticket

Session ID

Session ID:就是客户端和服务器首次连接手各自保存会话ID,并存储会话密钥,下次再连接时,客户端发送ID过来,服务器这边再查找ID,如果找到了就直接复用会话,密钥也不用重新生成
可是这样的话,在客户端数量庞大的时候,对服务器的存储压力可就大了

如果每次重连都要重新握手还是比较耗时的,所以可以对握手过程进行优化。从下图里我们看到 Client Hello 消息里还附带了上一次的 Session ID,服务端接收到这个 Session ID 后如果能复用就不再进行后续的握手过程。
image.png
除了上述的 Session 复用,SSL/TLS 握手还有一些优化技术,例如 False Start、Session Ticket 等,这方面的介绍具体可以参考这篇文章

Session Ticket

所以出来了第二种方式 Session Ticket:就是双方连接成功后服务器加密会话信息,用Session Ticket消息发给客户端存储起来,下次再连接时就把这个Session Ticket解密,验证有没有过期,如果没有过期就复用会话。原理就是把存储压力分给客户端。
这样就万无一失了吗?
No,这样也存在安全问题。因为每次要用一个固定的密钥来解密Session Ticket,一旦密钥被窃取,那所有历史记录也就被破解了,所以只能尽量避免这种问题定期更换密钥。毕竟节省了不少生成会话密钥和这些算法的耗时,性能还是提升了嘛

那刚说了1-RTT,那能不能优化到0-RTT呢
还真可以,做法就是发送Session Ticket的时候带上应用数据,不用等服务端确认。这种方式被称为PSK(Pre-Shared Key)
这样万无一失了吗?
尴了个尬,还是不行。这PSK要是被窃取,人家不断向服务器重发,就直接增加了服务器被攻击的风险
虽然不是绝对安全,但是现行架构下最安全的解决文案了,大大增加了中间人的攻击成本

http1.1优化思路

  • 首先是通过缓存避免发送 HTTP 请求;
  • 其次,如果不得不发起请求,那么就得思考如何才能减少请求的个数;
  • 最后则是减少服务器响应的体积。

    使用缓存减少请求

强缓存(cache-control):如果响应缓存没过期,不向服务器发送缓存,直接使用。
etag:过期的缓存也仍然可以提升性能,如下图所示,当客户端发现缓存过期后,会取出缓存的摘要(摘要是从第 1 次请求的响应中拿到的),把它放在请求的 Etag 头部中再发给服务器。而服务器获取到请求后,会将本地资源的摘要与请求中的 Etag 相比较,如果不同,那么缓存没有价值,重新发送最新资源即可;如果摘要与 Etag 相同,那么仅返回不含有包体的 304 Not Modified 响应,告知客户端缓存仍然有效即可,这就省去传递可能高达千百兆的文件资源。

image.png

降低 HTTP 请求的次数

重定向增加了请求的数量。尤其客户端在公网中时,由于公网速度慢、成本高、路径长、不稳定,而且为了信息安全性还要用 TLS 协议加密,这些都降低了网络性能。从上面的 REST 架构图可以看到,HTTP 请求会经过多个代理服务器,如果将重定向工作交由代理服务器完成,就能减少网络消耗,如下图所示:
image.png
更进一步,客户端还可以缓存重定向响应。RFC 规范定义了 5 个重定向响应码(如下表所示),其中客户端接收到 301 和 308 后都可以将重定向响应缓存至本地,之后客户端会自动用 url2 替代 url1 访问网络资源。
image.png

优化思路总结

1.使用缓存,通过缓存技术来避免发送 HTTP 请求。客户端收到第⼀个请求的响应后,可以将其缓存在本地磁 盘,下次请求的时候,如果缓存没过期,就直接读 取本地缓存的响应数据。如果缓存过期,客户端发送请求的时候 带上响应数据的摘要,服务器⽐对后发现资源没有变化,就发出不带包体的 304 响应,告诉客户端缓存的响应仍然 有效。

  1. 减少 HTTP 请求的次数,有以下的⽅法:
  • 将原本由客户端处理的重定向请求,交给代理服务器处理,这样可以减少᯿定向请求的次数;
  • 将多个⼩资源合并成⼀个⼤资源再传输,能够减少 HTTP 请求次数以及 头部的᯿复传输,再来减少 TCP 连 接数ᰁ,进⽽省去 TCP 握⼿和慢启动的⽹络消耗;
  • 按需访问资源,只访问当前⽤户看得到/⽤得到的资源,当客户往下滑动,再访问接下来的资源,以此达到延 迟请求,也就减少了同⼀时间的 HTTP 请求次数。


    3.通过压缩响应资源,降低传输资源的⼤⼩,从⽽提⾼传输效率,所以应当选择更优秀的压缩算法。

HTTP 2.0 (2015年)

  • 使用新的二进制协议,不再是纯文本,避免文本歧义,缩小了请求体积
  • 多路复用,同域名下所有通信都是在单链接(双向数据流)完成,提高连接的复用率,在拥塞控制方面有更好的能力提升
  • 使用HPACK算法将头部压缩,用哈夫曼编码建立索表,传送索引大大节约了带宽
  • 允许服务端主动推送数据给客户端
  • 增加了安全性,使用HTTP 2.0,要求必须至少TLS 1.2
  • 使用虚拟的流传输消息,解决了应用层的队头阻塞问题(用多路复用来解决)
  • 支持明文 HTTP 传输


缺点

  • TCP以及TCP+TLS建立连接的延时,HTTP2使用TCP协议来传输的,而如果使用HTTPS的话,还需要TLS协议进行安全传输,而使用TLS也需要一个握手过程,在传输数据之前,导致我们花掉3~4个RTT
  • TCP的队头阻塞并没有彻底解决。在HTTP2中,多个请求跑在一个TCP管道中,但当HTTP2出现丢包时,整个TCP都要开始等待重传,那么就会阻塞该TCP连接中的所有请求

    管道化

    http1.1在使用长连接的情况下,建立一个连接通道后,连接上消息的传递类似于
    请求1 -> 响应1 -> 请求2 -> 响应2 -> 请求3 -> 响应3

管理化连接的消息就变成了类似这样
请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3

管线化是在同一个TCP连接里发一个请求后不必等其回来就可以继续发请求出去,这可以减少整体的响应时间,但是服务器还是会按照请求的顺序响应请求,所以如果有许多请求,而前面的请求响应很慢,就产生一个著名的问题队头堵塞(下面有讲解决方法)
管线化的特点:

  • 管线化机制通过持久连接完成,在http1.1版本才支持
  • 只有GET请求和HEAD请求才可以进行管线化,而POST有所限制
  • 初次创建连接时不应启动管线化机制,因为服务器不一定支持http1.1版本的协议
  • 管线化不会影响响应到来的顺序,如上面的例子所示,响应返回的顺序就是请求的顺序
  • 要求客户端和服务端都支持管线化,但并不要求服务端也对响应进行管线化处理,只是要求对于管线化的请求不失败即可
  • 由于上面提到的服务端问题,开户管线化很可能并不会带来大幅度的性能提升,而且很多服务端和代理程序对管线化的支持并不好,因为浏览器(Chrome/Firefox)默认并未开启管线化支持

    如何解决 HTTP 的队头阻塞问题

http1.1协议采用的是请求-应答模式,报文必须是一发一收,就形成了一个先进先出的串行队列,没有轻重缓急的优先级,只有入队的先后顺序,排在最前面的请求最先处理,就导致如果队首的请求耗时过长,后面的请求就只能处于阻塞状态,这就是著名的队头阻塞问题。解决如下:

并发连接

因为一个域名允许分配多个长连接,就相当于增加了任务队列,不至于一个队列里的任务阻塞了其他全部任务。以前在RFC2616中规定过客户端最多只能并发2个连接,但是现实是很多浏览器不按套路出牌,所以在RFC7230把这个规定取消掉了,现在的浏览器标准中一个域名并发连接可以有6~8个,记住是6~8个,不是6个(Chrome6个/Firefox8个)
如果这个还不能满足你
继续,不要停…

域名分片

一个域名最多可以并发6~8个,那咱就多来几个域名
比如a.baidu.com,b.baidu.com,c.baidu.com,多准备几个二级域名,当我们访问baidu.com时,可以让不同的资源从不同的二域名中获取,而它们都指向同一台服务器,这样能够并发更多的长连接了
而在HTTP2.0下,可以一瞬间加载出来很多资源,因为支持多路复用,可以在一个TCP连接中发送多个请求

TLS 1.2 与 TLS1.3

TLS1.3(参见RFC8446)对性能的最大提升,在于它把 TLS 握手时间从 2 个 RTT 降为 1 个 RTT。
在 TLS1.2 的握手中,先要通过 Client Hello 和 Server Hello 消息协商出后续使用的加密算法,再互相交换公钥并计算出最终密钥。TLS1.3 中把 Hello 消息和公钥交换合并为一步,这就减少了一半的握手时间,如下图所示:
image.png
那 TLS1.3 握手为什么只需要 1 个 RTT 就可以完成呢?因为 TLS1.3 支持的密钥协商算法大幅度减少了,这样,客户端尽可以把常用 DH 算法的公钥计算出来,并与协商加密算法的 HELLO 消息一起发送给服务器,服务器也作同样处理,这样仅用 1 个 RTT 就可以协商出密钥。

加密

  • 对称加密:密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有DES、AES等;
  • 非对称加密:密钥成对出现(且根据公钥无法推知私钥,根据私钥也无法推知公钥),加密解密使用不同密钥(公钥加密需要私钥解密,私钥加密需要公钥解密),相对对称加密其加密和解密速度较慢,典型的非对称加密算法有RSA、DSA等。