当网络服务器发送页面时,为什么不发送所有必需的CSS,JS和图像?


45

当网页包含单个CSS文件和图像时,为什么浏览器和服务器会通过这种传统的耗时途径浪费时间:

  1. 浏览器发送对该网页的初始GET请求,并等待服务器响应。
  2. 浏览器向css文件发送另一个GET请求,并等待服务器响应。
  3. 浏览器向图像文件发送另一个GET请求,然后等待服务器响应。

相反,他们何时可以使用这条简短,直接,省时的路线?

  1. 浏览器向网页发送GET请求。
  2. Web服务器以(index.html后跟style.cssimage.jpg)响应

2
当然,只有在获取网页后才能发出任何请求。之后,在读取HTML时按顺序发出请求。但这并不意味着一次只发出一个请求。实际上,发出了几个请求,但有时请求之间存在依赖关系,必须先解决一些依赖关系才能正确绘制页面。浏览器有时会在满足其他要求之前暂停,然后再处理其他响应,从而使每个请求似乎一次都被处理。在浏览器方面,现实更多,因为它们往往占用大量资源。
closetnoc

20
我很惊讶没有人提到缓存。如果我已经有了该文件,则不需要将其发送给我。
科里·奥格本

2
此列表可能长数百。尽管比实际发送文件要短,但它离最佳解决方案还差得很远。
科里·奥格本

1
实际上,我从未访问过拥有100多个独特资源的网页
Ahmed

2
@AhmedElsoobky:浏览器不知道哪些资源可以在不首先获取页面本身的情况下作为缓存资源标头发送。如果检索页面告诉服务器我缓存了另一个页面,这也可能是隐私和安全的噩梦,该页面可能是由与原始页面(多租户网站)不同的组织控制的。
Lie Ryan

Answers:


63

简短的答案是“因为HTTP不是为此设计的”。

Tim Berners-Lee并未设计一种有效且可扩展的网络协议。他的一个设计目标是简单。(我在大学的网络课程的教授说,他应该把工作交给专业人士。)您概述的问题只是HTTP协议的众多问题之一。原始形式:

  • 没有协议版本,只有资源请求
  • 没有标题
  • 每个请求都需要一个新的TCP连接
  • 没有压缩

后来对该协议进行了修订,以解决许多这些问题:

  • 请求已版本化,现在请求看起来像 GET /foo.html HTTP/1.1
  • 标头添加了带有请求和响应的元信息
  • 连接被允许与 Connection: keep-alive
  • 引入了分块响应,即使文档大小事先未知,也可以重用连接。
  • 添加了Gzip压缩

在这一点上,HTTP已经尽可能地使用了,而没有破坏向后兼容性。

您不是第一个建议应将页面及其所有资源推送给客户端的人。实际上,Google设计了一种可以做到这一点的协议,称为SPDY

今天,Chrome和Firefox都可以使用SPDY而不是HTTP来支持它。在SPDY网站上,与HTTP相比,它的主要功能是:

  • SPDY允许客户端和服务器压缩请求和响应标头,当针对多个请求反复发送相似的标头(例如cookie)时,可以减少带宽使用。
  • SPDY允许通过单个连接进行多个同时多路复用的请求,从而节省了客户端和服务器之间的往返行程,并防止了低优先级的资源阻止较高优先级的请求。
  • SPDY允许服务器将知道客户端所需的资源(例如JavaScript和CSS文件)主动推送到客户端,而无需等待客户端请求它们,从而允许服务器有效利用未利用的带宽。

如果您想使用SPDY为支持该浏览器的网站提供服务,则可以这样做。例如,Apache具有mod_spdy

SPDY已成为具有服务器推送技术的HTTP版本2的基础。


2
当当好,见多识广!Web浏览器本质上是串行的,因此可以相当迅速地发出请求。一次查看日志文件将显示,一旦HTML解析完毕,对资源的请求就会很快完成。就是这样。这不是一个糟糕的系统,只是代码/资源的效率不尽如人意。
closetnoc

6
仅作记录,SPDY并不是圣杯。它在某些方面做得很好,但会带来其他问题。是一篇包含SPDY的一些要点。
2014年

3
我强烈建议对此感兴趣的任何人阅读@Jost链接中的批评。它给您一个暗示,弄清楚如何做一个非常普遍实现的事情不仅复杂,而且逐步改进,所有人都开始使用它。很容易想到一种改进,它可以使相对较大的用例子集变得更好。以使每个人都开始使用新协议的方式来使事情变得更好,因为这样做要好得多,值得付出更改的代价,这完全是另一回事,而且不容易做到。
msouth 2014年

11
他应该把工作留给专业人士:如果他做到了,他们将需要六年的时间来制定一个标准,而该标准在发布之日就已经过时了,很快就会出现十多个竞争标准。此外,专业人员是否需要得到他人的许可?他们为什么不自己做呢?
Shantnu Tiwari 2014年

2
坦率地说,当时没有合格的专业人员。没有人知道如何建立一个万维网,因为从来没有人建立过。超级媒体的概念不是蒂姆发明的,十年来他在各种本地超媒体系统上都有丰富的经验,后来他为欧洲核子研究组织撰写了“信息管理”提案,以解决“信息丢失”的问题。
Lie Ryan

14

您的Web浏览器直到从服务器下载包含这些资源链接的网页(HTML)时,才知道其他资源。

您可能想知道,为什么服务器在最初请求网页期间不只是解析自己的HTML并将所有其他资源发送到Web浏览器?这是因为资源可能分散在多个服务器上,并且Web浏览器可能不需要所有这些资源,因为它已经缓存了其中一些资源,或者可能不支持它们。

Web浏览器维护资源缓存,因此不必从承载它们的服务器上一遍又一遍地下载相同的资源。当浏览网站上都使用相同jQuery库的不同页面时,您不想每次都只是第一次下载该库。

因此,当Web浏览器从服务器获取网页时,它将检查缓存中尚不具有的链接资源,然后对这些资源进行其他HTTP请求。非常简单,非常灵活且可扩展。

Web浏览器通常可以并行发出两个HTTP请求。这与AJAX不同-它们都是加载网页的异步方法-异步文件加载和异步内容加载。使用keep-alive,我们可以使用一个连接发出多个请求,而通过流水线,我们可以发出多个请求而不必等待响应。这两种技术都非常快,因为大多数开销通常来自打开/关闭TCP连接:

活着

流水线

网页历史记录...

网页从纯文本电子邮件开始,围绕这个想法设计了计算机系统,从而形成了某种免费的交流平台。Web服务器在当时仍然是专有的。后来,以其他MIME类型的形式,例如图像,样式,脚本等,在“电子邮件规范”中添加了更多层。毕竟,MIME代表多用途Internet 邮件扩展。迟早我们有了本质上就是多媒体电子邮件通信,标准化的Web服务器和网页的工具。

HTTP要求在类似电子邮件的消息上下文中传输数据,尽管该数据通常实际上并不是电子邮件。

随着这种技术的发展,它需要允许开发人员逐步整合新功能,而又不会破坏现有软件。例如,当将新的MIME类型添加到规范中(例如JPEG)时,Web服务器和Web浏览器将需要一些时间来实现它。您不只是突然将JPEG强制加入规范中并开始将其发送到所有Web浏览器,还允许Web浏览器请求其支持的资源,这使每个人都满意并且技术不断进步。屏幕阅读器是否需要网页上的所有JPEG?可能不是。如果您的设备不支持Javascript,是否应该强迫您下载一堆Javascript文件?可能不是。Googlebot是否需要下载所有Javascript文件才能正确索引您的网站?不。

资料来源:我已经开发了基于事件的Web服务器,例如Node.js。它称为Rapid Server

参考文献:

进一步阅读:


嗯,其实,我们可以照顾到所有这些侧面的问题(喜欢的东西:缓存,内容类型header..etc),也有变通方法来解决这些问题。正如我在以上文章的评论中所建议的那样,我们可以使用类似以下标头> Cached-Resources的图像:image.jpg; style.css; 解决缓存问题..(如果您有时间,那么可以看一下上面的评论..)
Ahmed

是的,这个想法以前已经引起我的注意,但是对于HTTP而言,这只是过多的开销,并且不能解决资源可能分布在多个服务器上这一事实。此外,我认为您提出的节省时间的方法实际上不会节省时间,因为无论您如何看待数据,数据都将作为流发送,并且在保持活动状态下,100个同时发生的HTTP请求实际上变成了1个请求。您提出的技术和功能似乎已经存在。参见en.wikipedia.org/wiki/HTTP_persistent_connection
perry

@perry:您认为https://发送可验证但不保密的大型公共分发文件的替代方案的想法是什么:在URL中包含合法回复标头的某些部分的哈希,这反过来又可以包括数据有效载荷的签名或散列,浏览器是否已针对标头验证收到的数据?这样的设计不仅可以节省一些SSL握手步骤,而且更重要的是可以缓存代理。通过SSL链接获取URL,然后可以从任何地方馈送数据。
2014年

11

因为他们不知道这些资源是什么。网页所需的资产已编码为HTML。只有在解析器确定了那些资产之后,用户代理才能请求y。

此外,一旦知道了这些资产,就需要单独提供它们,以便可以提供适当的头(即内容类型),以便用户代理知道如何处理它。


2
特别是如果您使用诸如require.js之类的东西。浏览器仅询问其需要的内容。想象一下必须一次加载所有内容...
Aran Mulholland 2014年

2
这是正确的答案,而且大多数注释者似乎都没有找到答案-为了使服务器主动发送资源,它需要知道资源是什么,这意味着服务器必须解析HTML。

1
但问题是为什么Web 服务器不发送资源,不是客户端为什么不能同时请求资源。很难想象一个世界,其中服务器拥有一整套相关资产,这些资产都是一起发送的,而不依赖于解析HTML来构建该软件包。
David Meister 2014年

@DavidMeister因为服务器并不总是知道客户端想要什么-搜索引擎的网络爬虫可能不在乎CSS / JS,并且文档中还有许多其他资源链接在这些文档之外-无需发送全部RSS将数据包中的RSS信息向下馈送到Web浏览器(大多数内容可能已经在HTML中了),而feed阅读器可能只是解析<head>元素以寻找RSS替代链接来找到它-客户端可以发送它感兴趣的是什么,但随后它需要知道可用的东西,然后我们又回到了起点
Zhaph-Ben Duguid 2015年

@ Zhaph-BenDuguid我在谈论一个替代世界,以强调答案与该协议的工作方式和其他任何事物都息息相关。此外,即使没有必要,服务器一次发送所有数据的速度也可能更快。您实际上是在权衡延迟问题和带宽使用之间进行权衡。
David Meister

8

因为在您的示例中,Web服务器将始终发送CSS和图像,而不管客户端是否已经拥有CSS和图像,从而极大地浪费了带宽(从而使连接速度变慢,而不是通过减少延迟来加快连接速度,这大概是您的意图)。请注意,正是由于这个原因,CSS,JavaScript和图像文件通常以很长的过期时间发送(因为当您需要更改它们时,只需更改文件名以强制新副本,该副本将再次被缓存很长时间)。

现在,您可以尝试通过说“ 确定,但客户端可以表明它已经具有某些资源,因此服务器不会再发送它 ” 来解决带宽浪费问题。就像是:

GET /index.html HTTP/1.1
Host: www.example.com
If-None-Match: "686897696a7c876b7e"
Connection: Keep-Alive

GET /style.css HTTP/1.1
Host: www.example.com
If-None-Match: "70b26618ce2c246c71"

GET /image.png HTTP/1.1
Host: www.example.com
If-None-Match: "16d5b7c2e50e571a46"

然后仅通过一个TCP连接(使用在持久连接上使用HTTP流水线)发送未更改的文件。你猜怎么着?这是怎么了已经工作的(你也可以使用IF-Modified-Since的,而不是如果-无-匹配)。


但是,如果您真的想通过浪费大量带宽(如您的原始请求)来减少延迟,今天就可以在设计网站时使用标准的HTTP / 1.1来做到这一点。大多数人不这样做的原因是因为他们认为这样做不值得。

为此,您不需要在单独的文件中包含CSS或JavaScript,您可以使用<style><script>标记将它们包含在主HTML文件中(您甚至可能不需要手动进行操作,您的模板引擎就可以自动进行操作) 。您甚至可以使用数据URI将图像包含在HTML文件中,如下所示:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />

当然,base64编码会稍微增加带宽使用量,但是如果您不关心带宽浪费,那应该不是问题。

现在,如果您真的很在意,您甚至可以使您的Web脚本足够聪明,以同时兼顾两个方面:在首次请求(用户没有cookie的情况下)中,发送嵌入在单个HTML中的所有内容(CSS,JavaScript,图像)文件,如上所述,为文件的外部副本添加一个链接rel =“ prefetch”标签,并添加一个cookie。如果用户已经有一个cookie(例如,他曾访问过),然后送他只是一个普通的HTML <img src="example.jpg"><link rel="stylesheet" type="text/css" href="style.css">等等。

因此,第一次访问时,浏览器将只请求一个HTML文件并获取并显示所有内容。然后,它将(在空闲时)预加载指定的外部CSS,JS,图像。下次用户访问时,浏览器将请求并仅获取更改的资源(可能只是新的HTML)。

即使您在网站上单击了数百次,额外的CSS + JS + images数据也只会发送两次。比您提出的解决方案建议的要好数百倍。它永远不会(不是第一次,也没有对今后一段时期)使用超过一个延迟增加往返。

现在,如果这听起来工作量太大,并且您不想使用其他协议(例如SPDY),则已经有适用于Apache的mod_pagespeed之类的模块,可以自动为您完成某些工作(合并多个CSS / JS文件)合并成一个,自动内嵌小型CSS并将其最小化,在等待原件加载,惰性加载图像等时制作小型占位符内嵌图像,而无需修改网页的一行。


3
我认为这是正确的答案。
el.pescado 2014年

7

HTTP2基于SPDY,完全可以满足您的建议:

在较高级别,HTTP / 2:

  • 是二进制的,而不是文本的
  • 完全多路复用,而不是有序和阻塞
  • 因此可以使用一个连接进行并行处理
  • 使用头压缩​​来减少开销
  • 允许服务器主动将响应“推送”到客户端缓存

关于HTTP 2常见问题的更多信息


3

因为它不假设这些东西实际上是必需的

该协议没有为任何特定类型的文件或用户代理定义任何特殊处理。它不知道HTML文件和PNG图像之间的区别。为了执行您要执行的操作,Web服务器将必须识别文件类型,将其解析以找出正在引用的其他文件,然后根据您打算做什么来确定实际需要的其他文件。文件。这有三个大问题。

第一个问题是,没有标准,可靠的方法来识别服务器端的文件类型。HTTP通过Content-Type机制进行管理,但这对服务器无济于事,服务器必须自行弄清这些内容(部分原因是它知道要放入Content-Type中的内容)。文件扩展名得到了广泛支持,但易碎且容易上当,有时出于恶意目的。文件系统元数据不那么脆弱,但是大多数系统并不很好地支持它,因此服务器甚至不需要打扰。file如果您愿意增加内容嗅探(如某些浏览器和Unix 命令尝试的那样),则内容嗅探可能会很健壮,但是健壮的嗅探过于昂贵,无法在服务器端实际使用,廉价的嗅探还不够健壮。

第二个问题是,在计算上解析文件是昂贵的。这有点关系到第一个,因为如果要稳健地嗅探内容,则需要以多种不同的潜在方式解析文件,但是在确定文件类型之后它也适用,因为您需要弄清楚引用是什么。当您一次只处理几个文件时(例如浏览器),这并不是很糟糕,但是Web服务器必须一次处理成百上千个请求。这加起来,并且如果它走得太远,它实际上会使事情减慢的速度超过多个请求的速度。如果您曾经访问过来自Slashdot或类似网站的链接,却发现服务器由于使用率过高而极度缓慢,那么您已经看到了这一原理。

第三个问题是服务器无法知道您打算如何处理该文件。浏览器可能需要在HTML中引用文件,但可能不需要,这取决于执行文件的确切上下文。那将足够复杂,但是Web不仅仅是浏览器,还有更多:蜘蛛,提要聚合器和页面抓取混搭之间,有许多类型的用户代理不需要HTML中引用的文件:只关心HTML本身。将这些其他文件发送到此类用户代理只会浪费带宽。

最重要的是,弄清楚服务器端的这些依赖关系比它值得的麻烦更多。因此,他们让客户确定需要什么。


如果我们要开发一种新协议或修复一个已经存在的协议,我们可以以一种或另一种方式解决所有这些问题!然后,Web服务器将仅解析一次文件,然后可以根据定义的规则对文件进行分类,以便可以优先确定要首先发送的文件。.etc,并且Web服务器不必知道我打算做什么有了这些文件,它只需要知道发送什么,何时执行以及取决于哪些规则即可。(网络bot和Spider都不成问题,它们的行为将有所不同-它们具有唯一的用户代理标头- ..)
艾哈迈德(Ahmed

@AhmedElsobky:您所说的听起来更像是一种特定的实现,而不是网络协议。但是,在确定要发送的内容之前,它确实必须知道您打算对这些文件进行什么操作:否则,它将不可避免地发送许多用户不想要的文件。您无法信任User-Agent字符串,因此无法使用它们来确定用户的意图。
2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.