接《 WEB加速,协议先行 ( 上)》,下面我们看一下TLS协议的优化。
TLS协议最大的性能问题也是它的握手。所以优化的目标也非常明确,就是减少完全握手,提升简化握手的比例。协议层面提供了两种机制,session id和session ticket,我相信接触过的同学都非常清楚,网上的资料也非常多,关于原理和过程我就不多做介绍了。
这里我主要是分享两点:
1.通过提升简化握手比例,iOS Qzone的SSL握手时间提升了50%,从200ms节省到了100ms。
2.虽然session ticket是一种更加优秀的机制,因为它不需要服务端做缓存,但是iOS目前还不支持session ticket,要想实现简化握手,必须要支持session id,并且最好是实现分布式session cache来提升简化握手比例。
然后我们再来看一下完全握手,因为有很多场景下必须要进行完全握手,比如用户第一次打开浏览器或者App,用户关闭tab页面再打开,用户手机或者系统重启等,都需要进行完全握手,因为前面提到的sesison ticket还是session id都是基于内存的,客户端重启之后再发起握手默认就无法携带上这些信息,必须进行完全握手。
针对完全握手该如何优化?
优化思路类似TFO,即在完全握手的第二个阶段,即密钥交换阶段,提前发送应用层数据,节省这一个RTT对性能的影响。
上图左边是普通握手,可以看出必须要进行四次握手,两个RTT之后才能发送绿色的HTTP加密数据。上图右边就是false start,抢跑的意思。在第二阶段,clientKeyExchange消息发出的时候,将HTTP GET的应用层数据加密发出来了。相当于节省了一个RTT。
如何支持false start?
很简单。因为现在最新的客户端都已经支持了这个特性,所以对于服务端来廛,我们只需要将支持PFS(完美前向密码)算法的密码套件配置在前面就行了,比如ECDHE,DHE算法配置在最前面。协商好密码套件后,客户端就能提前发送数据,实现false start。False start对完全握手的优化效果也很明显,大概提升了30%。
接下来我们再看一下OCSP的问题。OCSP是在线证书状态检查协议,这个检查和证书本身的签名校验不是一回事。因为有一些情况,单纯检验签名是发现不了的。比如我们申请了一张有效期一年的证书,但不幸的是,申请下来的第一个月,私钥被内部人员泄露了,或者CA本身的数据库被黑客攻击了,我们需要主动撤销这张证书的信任关系。那就只能主动告诉CA这张证书不安全,然后客户端自己再去CA那边查询一下。因为这个时候证书本身的签名是没有问题的,如果不去额外查一下,在证书本身过期之前,永远也发现不了证书被撤销的事实。
OCSP的过程发生在客户端接收到server hello和certificate消息后,这个时候它会根据证书里的OCSP域名,发起OCSP请求,如上图左边所示。OCSP stapling的意思,简单来说就是服务端代理CA实现的OCSP内容的签发。服务端会提前向CA站点请求好OCSP内容,并保存在本地,在握手的时候,将OCSP内容和证书一起发送给客户端,这样客户端就不需要自己主动去请求CA查询OCSP内容了。这样看来OCSP Stapling至少节省了三个RTT,效果应该非常不错。但事实上,OCSP Stapling的效果并不会特别突出,因为客户端有缓存。一般来讲会有7天,也就是说客户端7天中才会查询一次OCSP。对于一个用户经常访问的页面来讲,这个概率可能只有千分之一甚至万分之一。所以对用户的访问体验来讲,提升的效果也比较有限。
然后我们再看一下dynamic record size。为什么需要做这个动态调整呢?是因为TLS协议本身的HOL(队头阻塞)。
Record是TLS协议处理的最小单位,最大不能超过16K,一些服务器比如Nginx默认的大小就是16K。由于一个record必须经过数据一致性校验才能进行加解密,所以一个16K的record,就算丢了一个字节,也会导致已经接收到的15.99K数据无法处理,因为它不完整。
比如上图右边所示,假设一个record需要6个TCP segment传输完成,如果最后一个segment丢了,那么上层应用程序必须HANG在那里等,无法继续处理。
上述就是TLS协议层面的队头阻塞,那如何解决呢?也有两个方案:
1.Nginx高版本支持一个配置指令ssl_buffer_size,可以将它设置成4K,这样就算有HOL,影响的也只是4K数据,而不是之前的16K。
预建连接也可以根据具体的用户场景和用户行为来建立。比如:
1.首页提前预建子页面的连接。当用户打开百度首页的时候,它通常会发起搜索,那我们可以提前给搜索结果页面建立连接。
2.根据用户行为预测。比如用户进入QQ空间首页时,我们可以根据用户的浏览习惯,他是经常访问QQ相册,还是经常访问QQ商城来预建不同的连接。
预建好的连接也有可能会超时断开,比如HTTPS的超时时间是1分钟,HTTP2有可能是3分钟,那过了几分钟后连接就断开了,用户再次发起请求时又需要建立新连接,如何避免这种情况?
可以使用长连接保持。即我们使用JS周期性比如每分钟发起一次长连接保持的请求,stgw就提供一个空页面,JS访问这个页面我们返回1个字节,作用就是为了维持住这个连接,不让它中断。
总的来说,HTTPS的访问速度是可以超越HTTP1.1的,这里面最核心的一点是,HTTPS可以使用HTTP2,可以多路复用,而HTTP1.1不行。
上图的数据是两年前H5 QQ空间优化的效果,数据虽然有点老,不过思路和优化方法是一致的,没有过时。
前面提到了很多HTTP2的特性,性能也很强大,那HTTP2是未来吗?或者更准确地说,HTTP2是下一个十年,最有性能优势,最具有统治力的WEB协议吗?
可以说是。因为它的许多特性,包括多路复用,头部压缩,server push,优先级等,设计得十分先进,性能也十分优良,解决了许多WEB性能问题。也可以说不是,为什么?因为当前的HTTP2协议是构建在TCP和TLS之上的,由此导致了一系列问题:
1.TCP连接耗时。比如需要TCP三次握手才能建立连接,就算是有了TFO,也需要操作系统才能支持,有许多系统目前也不支持TFO。而且TFO本身,在第一次获取Cookie时,也需要一次额外的RTT才能实现接下去的快速握手。
2.TLS连接耗时,当前的TLS1.2至少需要1个RTT才能建立TLS连接。
3.TLS安全问题,TLS目前并没有针对TCP头部进行一致性校验,从而存在TCP头部被篡改的风险,比如修改滑动窗口数,修改TCP序列号等。
4.加剧TCP队头阻塞。TCP为了实现可靠性和数据的有序性,发生segment丢包后需要重传,就算丢包序列号之后的包提前到达了,也需要等待丢失的包重传才能通知应用层来读取数据。这就是TCP的队头阻塞问题,而HTTP2的多路复用,加剧了TCP的队头阻塞,因为一条连接上同时发送的数据变多了。队头阻塞的影响也就更严重了。
5.重传的模糊性问题。由于TCP重传segment的序列号和原始segment的序列号相同。在判断该segment及后续segment是否需要重传的时候,很容易迷糊。
6.拥塞控制,需要操作系统的支持,升级成本高。
以上种种,影响了HTTP2的性能,所以从这个角度来看,也可以说HTTP2并不是未来最有性能优势的协议,那什么才是呢?我觉得最有竞争力的一个协议就是QUIC。
让我们拥抱QUIC。什么是QUIC协议?简单来说就是使用UDP实现的HTTP2。它具体以下特性:
1.继承了HTTP2的大部分特性,包括多路复用,头部压缩,server push,优先级等。
2.当前支持0RTT握手,等TLS1.3发布后,也会使用TLS1.3的0RTT握手协议。
3.使用UDP传输,没有TCP连接建立的耗时。
4.针对IP Packet进行加密,减少了队头阻塞的程度。
5.针对UDP头部进行一致性校验,就算是修改了UDP头部也能及时发现。
由于篇幅关系,关于QUIC的原理和详细功能就不再做介绍了。感兴趣的同学可以关注我们后续的报道,如果有机会,下一次架构师峰会,也可以专门给大家重点分享。
上述提到的许多协议优化特性,我们都已经集成到了腾讯云CLB负载均衡。大家如果感兴趣的话,可以使用腾讯云的产品和CLB来体验一下。
注:本篇内容来自“腾讯技术工程官方号”,公众号ID:tegwzx
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。