简单描述一下Http的连接流程:经过tcp三次握手之后,客户端和服务端建立连接,然后服务端解析客户端的请求的Header和内容部分(post请求参数放在内容行),然后返回结果,如果有多次请求,就重复上面过程。已有的优化策略有哪些。
由于每次请求都会进行tcp三次握手,因此便有了长连接,Http 1.1之后默认打开长连接,这样就不必每次向服务器请求的时候,进行重新连接。
我们打开一个页面的时候,会加载多个资源,那么每次请求一个资源,等待返回结果之后,再请求下一个资源,这样等整个资源请求完,由于网络请求存在时延,如果有N个资源的话,需要N个RTT(round trip time)的时间才能请求完成, 那么是否可以在一次请求(一个RTT时间)的时候,将结果都返回出来呢?这就是pipeline技术。
从上面的pipeline技术可以看到,一个RTT的时间就可以返回所有的结果,但是为什么这个功能默认是关闭的呢?因为pipeline技术会出现队头阻塞的问题,我们知道Http是无状态的协议,那么返回资源的顺序必须按照请求资源的顺序,因此后面的请求需要等待前面的请求,一旦前面的请求结果出现阻塞,那么后面的请求结果就只能处于等待状态。因此基本上处于阻塞状态。然后为了减少资源的请求时间,就出现了并行连接,这个不是什么新鲜的技术,就是多开启几个线程,建立多个连接,这个虽然一定程度上可以减少资源的请求时间,但是服务器端的压力可能还会因此增加,更加降低效率。
Chrome浏览器一般开启6-8个连接并行请求资源。
前面我们谈到pipeline技术的机制,由于http是无状态的协议,所以返回结果需要按照请求的顺序返回,因此存在阻塞问题。前面我们也提到了tcp协议,tcp协议也有分片传输,而且每片传输的时候,没有管什么顺序,只需要最后接收端组装的时候,按照顺序组装即可,同时它也不需要知道数据内容是文本还是图片。因此就有人想到是否http协议也采用这种方式,返回结果不需要按照请求顺序,也不管什么类型,由接收端进行拼接。这就是多路复用技术,也就是一条连接通道,多个请求同时复用,结果返回也不需要按照请求顺序,采用流的方式传输,我们也把它称之为帧,最开始采用的是google的SPDY,后来Http 2将SPDY的技术变成标准,SPDY就废弃啦,如果Http 1.1是文本协议,那么Http 2就是二进制协议。
我们再来看看Http 2的协议
帧由 Frame Header 和 Frame Payload 组成。之前在 http/1.x 中的 header 和 body 都放在 Frame Payload 中。
Type 字段用来表示该帧中的 Frame Payload 保存的是 header 数据还是 body 数据。除了用于标识 header/body,还有一些额外的 Frame Type。
Length 字段用来表示 Frame Payload 数据大小。
Frame Payload 用来保存 header 或者 body 的数据。
Stream Identifier 用来标识该 frame 属于哪个 stream。这句话可能感觉略突兀,这里要明白 Stream Identifier 的作用,这个是引出 http2 的第二个特性多路复用中需要用到的。为了区分不同的请求的结果。用一个图来表示可能更好理解。
Stream Identifier就相当于标记上面的不同颜色的流(stream)。
5.头部压缩
再次提到Http是无状态协议,那么服务端是不会保存客户端状态的(session也是依赖cookie的),因此我们需要客户端在http协议的头部携带服务端要求的细节,同时服务端也会在Http头部返回细节。如果头部保存的数据太多,就会影响传输效率,比如保存的cookie太多了,而多次请求的时候,实际上头部改动比较小,那么是否可以将头部的每个字段进行压缩编码,服务端和客户端共同维护,下次只发送编码即可。Http 2提供了HPack编码的方式。我们用图示。
当我们要发送Method: Get头部时,直接发送2即可。
上面列的都是已经形成标准的优化,还有哪些是平时经常用到的优化。
DNS优化:我们知道当我们请求服务器之前,首先要向DNS服务器请求,将域名转化为IP,那么我们本地可以维护一个域名和IP,适当的时候自动更新,那么在请求的时候,直接读取本地维护的IP即可。
Https握手简化优化:我们知道Https比Http慢的原因在于tcp三次握手之后,还有TLS握手,可以通过简化这部分的握手来优化。
1.利用session id,当我们第一次连接之后,客户端会生成session id,当下一次客户端再次和服务端连接的时候,通过携带的session id如果在服务端可以找到,那么就认为客户端是可信赖的。
2.利用session ticket,也在客户端连接的时候会附带上,当再次连接的时候,直接解密session ticket,如果解密成功,则认为是可信赖,直接进行数据传输。
3.对于分布式系统,当在一个服务器生成的session id,把它同步到其他服务器,那么当客户端连接其他服务器的时候,直接可以通过这个id判断是否可信赖。
4.利用tcp连接优化,前面介绍tcp三次握手的时候,数据域没有携带数据的,那么可以在tcp三次握手的时候,就携带数据,减少握手次数。
5.证书验证优化,TLS握手过程中,客户端需要验证证书的有效性,这一部分可以直接通过服务器验证,将结果缓存,将验证结果返回给客户端,客户端在本地校验结果是否真实。
在项目开发过程,可能还有更多的优化技巧,这些后面慢慢总结