你了解HTTP吗?如何减少HTTP请求确保你的性能最优。

------------------------武汉加油!中国加油!跨过了病情我们都去见最想见的人---------------------2020-01-31------------------------------------------------------------------------------------

1.HTTP简介

**超文本传输协议**(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的www文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。1960年美国人Ted Nelson构思了一种通过计算机处理文本信息的方法,并称之为超文本(hypertext),这成为了HTTP超文本传输协议标准架构的发展根基。Ted Nelson组织协调万维网协会(World Wide Web Consortium)和互联网工程小组共同合作研究,最终发布了一系列的RFC,其中著名的RFC 2616定义了HTTP1.1。
1.0版本
1995年5月,HTTP/1.0版本发布,内容大大增加。 首先,任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像,视频,二进制文件,这为互联网的发展奠定了基础。

其次,除了GET命令,还引入POST命令和HEAD命令,丰富了浏览器与服务器的互动手段。
再次,HTTP请求和回应的格式也变了。除了数据部分,每次通信都必须包括头信息(HTTP Header),用来描述一些元数据。
其他的新增功能还包括状态码(status code)、多字符集支持,多部分发送(multipart type),权限(authorization)、缓存(cache)、内容编码(content encoding)等。

请求格式
下面是一个1.0版本的HTTP请求的例子。 GET/HTTP/1.0 User-Agent:Mozilla/5.0(Macintosh:Intel Mac OS X 10_10_5)

Accept:
可以看到,这个格式与0.9版本有很大的变化。
第一行是请求命令,必须在尾部添加协议版本(HTTP、1.0)。后面就是多行头信息,描述客户端的情况。

回应格式
服务器的回应格式如下 HTTP/1.0 200 OK Content-Type:text/plain Content-Length:137582 Exprise:T胡,05 Dec 1997 16:00:00 GMT Last-Modified:Wed,5 August 1996 15:55:28 GMT Server:Apache 0.84 回应的格式是“头信息+一个空行(\r\n)+数据”。其中,第一行是“协议版本+状态码+状态描述”
Content-Type 字段
关于字符的编码,1.0版本规定,头信息必须是ASCII码,后面的数据可以是任何格式。因此,服务器回应的时候,必须告诉客户端,数据是什么格式,这就是Content-Type字段的作用。 下面是一些常见的Content-Type字段的值。

text/plain
text/html
text/css
image/jpeg
image/png
image/svg+xml
audio/mp4
video/mp4
application/javascript
applicatiobn/pdf

这些数据类型统称为MIME type,每个值包括一级类型和二级类型,之间用斜杠分隔。除了预定义的类型,厂商也可以自定义类型。
application/vnd.debian.binary-package
上面的类型表明,发送的是Debian系统的二进制数据包
MIMEtype还可以在尾部使用分号,添加参数。
Content-Type:text/html;charset=utf-8
上面的类型表明,发送的是网页,而且编码是UTF-8。
客户端请求的时候,可以使用Accept字段声明自己可以接受哪些数据格式。
Accept:/
上面代码中,客户端声明自己可以接受任何格式的数据。
MIMEtype不仅用在HTTP协议,还可以用在其他地方,比如HTML网页。
<metahttp-equiv="Content-Type"content=“text/html;charset=UTF-8”/>
–等同于–
<metacharset=“utf-8”/>
Content
Content-Encoding
Encoding字段
字段
由于发送的数据可以是任何格式,因此可以把数据压缩后再发送。Content-Encoding字
段说明数据的压缩方法。
Content-Encoding:gzip
Content-Encoding:compress
Content-Encoding:deflate
客户端在请求时,用Accept-Encoding字段说明自己可以接受哪些压缩方法。
Accept-Encoding:gzip,deflate
缺点:
HTTP/1.0版的主要缺点是,每个TCP连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。
TCP连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slowstart)。所以,HTTP1.0版本的性能比较差。随着网页加载的外部资源越来越多,这个问题就愈发突出了。
为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段。
Connection:keep-alive
这个字段要求费无奇不关闭TCP连接,一遍其他请求复用,服务器同样回应这个字段。
Connection:keep-alive
一个可以重复使用的TCP连接就成立了,直到客户端或服务器主动关闭连接,但是,这不是标准字段,不同实现的行为可能不一致,因此不是根本的解决方法

1.1版本
1997年1月,HTTP/1.1版本发布,只此1.0版本晚了半年,他进一步完善了HTTP协议,一直用到了今天。
持久连接
1.1版的最大变化就是引入了持久连接,即TCP连接默认不可关闭,可以被多个请求复用,不用声明Connection:keep-alive; 客户端和服务器发现对方一段时间没有活动,就可以关闭连接,不过,规范的做法是,客户端在最后一个请求时,发送Connection:close,明确要求服务器关闭TCP连接。 Connection:close 目前:对于同一个域名,大多数浏览器允许同时建立6个持续连接。
管到机制
1.1版还引入了管到机制,即在同一个TCP连接里面,客户端可以同时发送多个请求,这样就进一步改进了HTTP协议的效率。 举例来说,客户端要请求两个资源,以前的做法是,在同一个TCP连接里面,先发送A请求,然后等待服务器做出回应,收到后再发送B请求。管到机制则是允许浏览器同时发送A和B请求,但是服务器还是按照顺序,先回应A请求,再回应B请求
Content-length字段
一个TCP连接现在可以传送多个回应,势必就要有一种机制,区分数据包是属于哪一个回应的,这就是Content-length字段的作用,声明本次回应的数据长度。 Content-length:3495 在1.0版本中,Content-Length字段不是必需的。因为浏览器发现拂去其关闭了TCP连接,就表明收到的数据包已经全了。
分块传输编码
使用Content-length字段的前提条件是,服务器发送回应之前,必须知道回应的数据长度。 对于一些很耗时的动态操作来说,这意味着服务器要等到所有操作完成,才能发送数据,虽然这样的效率不高。更好的处理的方法是,产生一块数据,就发送一块,采用“流模式(stream)”取代“缓存模式(buffer)” 因此,1.1版规定可以不使用Content-Type字段,而使用“分块传输编码”(chunked transfer encoding)只要请求或回应的头信息有Transfer-encoding字段,就表明回应将由数量未定的数据块组成。 Transfer-Encoding:chunked;
其他功能
1.1版还新增了许多动词方法:PUT、PATCH、HEAD、OPTIONS、DELETE 另外,客户端请求的头信息新增了Host字段,用来指定服务器的域名。 Host:www.example.com 有了Host字段,就可以将请求发往同一台服务器的不同网站,为虚拟主机的兴起打下基础。
缺点
虽然1.1版允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为“队头堵塞”。 为了避免这一个问题,只有两种办法,一是减少请求数,二是同时多开持久连接。这导致了很多的网页优化技巧,比如合并脚本和样式表、将图片嵌入CSS代码,域名分片。如果HTTP协议能设置的更好一点,这些额外的工作也是可以避免的。
2.关于减少http请求数
关于减少http请求数,是前端开发性能优化的一个非常重要的方面,所以在基本所有的优化原则里面,都有这一条原则:减少http请求数。 先不考虑其他的,我们先考虑为什么减少http请求可以优化性能。

减少http请求有这样几个优点:
(1)减少DNS请求所耗费的时间。
且不说对错,因为从基本来说,减少http请求数的确可以减少DNS请求和解析耗费的时间。
(2)减少服务器压力
这个通常是被考虑最多的,每个http请求都会耗费服务器资源,特别是一些需要计算合并等操作的服务器,耗费服务器的cpu资源可不是开玩笑的事情,硬盘可以用钱来买,资源就没有这么廉价了。
(3)减少http请求头。
当我们对服务器发起一个请求的时候,我们会携带着这个域名下的cookie和其他一些信息在http头部里,然后服务器响应请求的时候也会待会一些cookie之类的头部信息,这些信息有时候会很大,在这种请求和响应的时候会影响带宽性能。

3.解释原因
(1)什么是DNS请求和解析呢? 简单来说,例如:www.taobao.com,这样一个url,其中www部分被称为主机名(hostname),taobao这部分是二级域,com则是一级域,如果是这样的一个网址:www.ali.tao.com那么ali就是三级域。

当我们去请求一个url的时候,首先会到本地服务器里去寻找缓存是否有解析结果,如果没有解析结果,就去根域名服务器请求,根域名服务器返回给本地域名服务器一个所查询的域名的主域名服务器ip地址,然后我们再去请求刚才返回的ip地址的域名服务器,然后返回下一级域名的ip地址,直到我们找到域名中所指的服务器ip,然后将结果缓存起来下次使用,并返回此结果。

一个第一次请求的url的DNS解析过程可能耗费是很高的,但是解析一次后,结果就会被缓存起来,之后再请求的时候牛不用走上面这一套复杂的解析过程了。

关于一个正常的DNS请求到底会耗费多少时间,这二没有定论,要看网速状况和地域,但是考虑一个dns解析解析过后就会被缓存起来,像淘宝这样的大网站,来的都是回头客,我们是否可以忽略DNS解析花费的时间呢?

在前端优化里还有一个优化方法,那就是增加hostname,例如,淘宝图片服务器,分为img01,img02,img03等主机名,我们在一个页面里的图片放在不同的hostname下,这样就可以同时下载多个图片了,浏览器http连接数的限制可以被缓解一下。

为什么对于淘宝的图片来说,使用不同的hostname是个更优的方案呢?

首先,因为淘宝网的特殊性,淘宝网大多数访问者都是回头客,他们的电脑里缓存着dns记录,这种情况,如果是小网站或者新兴网站可能要考虑,因为新用户比较多,可能dns请求的消耗更大一下,而且第一印象对于这些网站来说更为重要。

再者,淘宝里的图片很多,一个页面里面通常会用到几十张甚至几百张图片,这种情况下,我们更需要突破浏览器的http连接数的限制,以便加快加载速度,这时候加载速度的考虑优先级远远高于DNS影响,二yslow中队DNS的着重考虑可能更偏向中小网站,图片比较少的网站。

对于DNS请求或者tcp请求之类的分配和解析的消耗,还有一个办法就是keeplive,让你的链接保持keeplive,这样可以只建立一次连接,然后传输多个文件,可以有效减少建立连接的时间。

(2)减少服务器压力
过多的http请求对于服务器来说是很危险的,如果你的服务器不是很强,请把这一条考虑放在第一位,其他的优化策略都只是优化,二这里涉及到的是服务器,你要保证你的服务器能正常运转。

当然如果你是在淘宝的话,你就可以安心坐下来跟一群牛人谈论为什么要忽略http对服务器的影响,因为我们要记住:我们是前端开发工程师,我们是在做前端优化,后台和我们无关,因为我们有足够强大的技术支持和硬件支持,当网站的技术发展到一定程度的时候我们的关注点应该是向用户那里偏重,因为用户看到的才是我们最终要展示的,用户感受到的体验和速度才是我们要达到的速度。后台我们做的再快,前台呈现慢了,我们的服务器消耗少了,省钱了,但是用户放弃了我们一切都是白费。

(3)合并脚本和样式
减少http请求数的一个方法,对于前端来说,那就是合并脚本和样式文件,称为combo,通过将多个文件合并成一个文件,然后一次性传输到客户端,这样可以减少http请求,的确是个有效的方法,甚至对于一些特殊的页面,例如首页,我们把样式和脚本都写在了页面里,根本没有分离出来,他们不会产生http请求,当然,也不会被缓存,这是被牺牲的代价.为什么我们要这么做,因为首页的访问量很大?这样可以有效减少http请求数?恩,这只是一部分原因,的确这样做有这样的好处,而且对于assets服务器不够强大的网站来说,在并发量大的首页上实行这一套是很有效的.但是,淘宝访问量最大的页面并不是首页,而是detail页面,也就是商品详情页!这才是我们讨论的重点,为什么首页采用combo甚至写在页面里,而detail则按照正常的样式和脚本来引用.首页是类似静态的页面,detail则是应用型的.首页没有脚本,依然可以起到导向的作用,但是detail页脚本没有运行起来的话,甚至无法购买商品.其实在这里这样讨论并不能明显看出问题所在,因为淘宝在这些方面也不是很成熟,detail页引用了大量脚本和样式,很多内容是多余的过期的.
这从本质上来说代表两种网页类型,一种是内容型,一种则是应用型.对于内容型的网站,脚本并不是很重要(甚至样式),因为没有脚本,用户仍然可以浏览页面,只是可能有些效果看不到而已,所以我们可以把脚本合并起来,一起放在body底部,在页面内容都加载完后,再一次性加载进来.而对于应用型的网页,让应用跑起来才是最重要的,因为没有应用这个网页就变得没有意义了,这时候,按需加载脚本是一种趋势,我们需要先把应用的基本框架和功能按需加载进来,让它们分别运行起来,而不是一起等脚本加载完再一起初始化,我们需要应用能够快速响应用户,而且还是说到CDN,当CDN变得足够强的时候,连接数已经不是瓶颈,我们应该更多考虑怎么让网页更快的展现给用户,对于无需脚本也可以提供服务的内容型的网页,将脚本放在页面底部,合并起来(减少连接数,我们仍然需要减少连接数,在不需要太快的使用脚本的情况下),而对于应用型的网页,我们需要尽快让功能运转,甚至让他们一部分一部分按优先级初始化,这时候就要将脚本分开,按需加载。

(4) 减少 http 请求头
http 头是个庞大的家伙,你打开 taobao.com 的首页,alert 一下 document.cookie,会发现 淘宝网的 cookie 是如此 庞大,甚至比小型网页都大,每次你请求淘宝的服务器都会往返一 次这些数据,还有一些其他的头部信息,占用的空间也不小,可想而知这种消耗有多大. 然后其实自从用了 CDN,这一切都无需考虑太多,因为 CDN 和淘宝主站不在一个域名 下,cookie 不会互相污染,而 CDN 的域名下基本是没有 cookie 和头部信息的,所以每次请 求静态资源的时候,不会带着主站的 cookie 到处跑,而只是传输资源的主题内容,所以这对于性能的影响在使用 cdn 之后会变得很小.但是如果你的静态资源服务器和主服务器在一 个域名下,那就要控制好 cookie 和其他头部信息的大小了,因为每次传送都会传送他 们.
(4) 如何减少 HTTP 请求
在终端用户响应的时间中,有 80%~90%时间用于下载 HTML 文档引用的所有组件。这部分 时间包括下载页面中的图像、样式表、脚本、Flash 等。因此,改善响应时间的最简单、 也是最有效的途径就是减少组件的数量,并由此减少 HTTP 请求的数量。 减少页面组件数量的方法其实就是简化页面设计。那么有没有一种方法既能保持页面内容 的丰富又能减少页面组件的数量?可以很容易想到的方法就是合并多个组件。 CSS Sprites (雪碧图) 雪碧图可以将多张图片合并成为一张图片,然后使用 CSS 的 background-position 属性, 将其设置到背景图片期望的位置上。 例如有个 id 为#nav 的导航栏,导航栏包含四个链接,每个链接被包围在一个 LI 中,他 们使用同一背景图片。每个 LI 都有一个不同的类,通过 background-position 属性指定 了期望的偏移量。
{% codeblock lang:css %} 
ul#nav li { float: left; width: 30px; height: 30px; 
background-image: url(./images/sprites.png); }
.home { background-position: 0 0;} 
.articles { background-position: -31px 0;}
 .tags { background-position: -62px 0;}
  .about { background-position: -93px 
0;}
 {% endcodeblock %}

雪碧图已经被广泛使用,一般用在网站上的小图标这类,数量多、体积小、不常更新的图片上,例如:淘宝首页的 Sprites。它不仅降低了下载量,而且实际上,合并后的图片会比分离的图片的总和要小,这是因为它降低了图片自身的开销(颜色表、格式信息,等等)。
内联图片 (data:URL)
通过使用 data:URL 模式可以在 Web 页面中包含图片但无需额外的 HTTP 请求。
规范中对它的描述为允许将小块数据内联为‘立即(immediate)数’
数据就在 URL 自身之中,格式为
{% codeblock %} data:[][;base64], {% endcodeblock %}
其实就是所谓的 Base64 图片格式。由于 data:URL 是内联在页面中的,所以在跨越不同页面时不会被缓存(document 一般不设置缓存)。
所以,更聪明的做法是使用 CSS 并将内联图片作为背景,并将该 CSS 作为外部样式表引用,这样内联图片就能缓存在样式表中了。
合并脚本和样式表
一个页面会引入多个脚本或者样式表,如果可以将这些单独的文件合并到一个文件中,可以减少 HTTP 请求的数量并缩短最终用户的响应时间。
在理想情况下,一个页面应该使用不多于一个的脚本和样式表。
然而在实际的开发环境中是很难完成的。在大型的、复杂的 Web 应用中,我们需要使用JavaScript 的模块化的思想,将所有东西合并到一个单独的文件中看起来就是一种倒退。因此,解决的方法是遵守编译型语言的模式,保持 JavaScript 的模块化,而在生成过程中从一组特定的模块生成一个目标文件。


最后总之,优化原则不是绝对的,对于不同的场景应该考虑不同的侧重点,别人的解决方案对于你来说不一定是最优的,应该针对自己的网站规模和类型进行适度的优化,不能盲目追求标准和最佳实践

发布了60 篇原创文章 · 获赞 17 · 访问量 6402

猜你喜欢

转载自blog.csdn.net/qq_42177478/article/details/104109341