自我托管你的静止资产

212 阅读10分钟

最快的胜利之一--也是我推荐我的客户做的第一件事--让网站变得更快,起初看起来是违反直觉的:你应该自行托管所有的静态资产,放弃别人的CDN/基础设施。在这篇短文中,我希望非常直截了当地概述 "站外 "托管你的静态资产的缺点,以及在你自己的网站上托管它们的压倒性好处。

我说的是什么?

对于开发者来说,链接到公共/CDN URL上的静态资产(如库或插件)的情况并不少见。一个典型的例子是jQuery,我们可能会这样链接到它。

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>

这样做有很多明显的好处,但我在本文后面的目的是要驳斥这些说法,或者说明其他成本如何大大超过它们。

  • 它很方便。像这样包含文件只需要很少的努力或脑力。复制和粘贴一行HTML,你就完成了。简单。
  • 我们可以访问CDN。 code.jquery.com 是由CDNStackPath提供的。通过链接到这个来源上的资产,我们得到了CDN质量的交付,而且是免费的
  • **用户可能已经有了该文件的缓存。**如果website-a.com 链接到https://code.jquery.com/jquery-3.3.1.slim.min.js ,而用户从那里去到website-b.com ,后者也链接到https://code.jquery.com/jquery-3.3.1.slim.min.js ,那么用户将在他们的缓存中已经有这个文件。

风险:速度减慢和中断

我不会在这篇文章中说得太详细,因为我有一整篇关于第三方恢复能力以及与速度减慢和中断有关的风险的文章。我只想说,如果你有任何重要的资产由第三方供应商提供服务,而该供应商正在遭受速度减慢或天灾人祸的影响,这对你来说是一个非常糟糕的消息。你也会受到影响。

如果你有任何阻碍渲染的CSS或同步JS托管在第三方域,_现在就_去把它带到你自己的基础设施上。关键资产太有价值了,不能放在别人的服务器上。

风险:服务关闭

这种情况不太常见,但如果供应商决定他们需要关闭服务,会发生什么?这正是Rawgit在2018年10月所做的事情,然而(在写这篇文章时)在GitHub上粗略的代码搜索仍然产生了超过一百万个对现在已经日落的服务的引用,并且有近20000个现场网站仍然在链接到它

非常感谢保罗-卡尔瓦诺(Paul Calvano),他非常好心地为我查询了HTTPArchive

风险:安全漏洞

另一个需要考虑的问题是简单的信任问题。如果我们把外部来源的内容带到我们的页面上,我们必须希望到达的资产是我们所期望的,并且它们只做我们期望的事情。

想象一下,如果有人设法控制了一个供应商,如code.jquery.com ,并开始提供受影响或恶意的有效载荷,将会造成多大的损失。这是不值得一想的。

缓解措施。子资源的完整性

到目前为止,本文所提到的所有供应商都使用了子资源完整性(SRI),这一点值得称赞。SRI是一种机制,供应商提供一个你期望并打算使用的确切文件的哈希值(技术上是一个哈希值,然后进行Base64编码)。然后,浏览器可以检查你收到的文件确实是你要求的文件。

<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
        integrity="sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8="
        crossorigin="anonymous"></script>

同样,如果你必须链接到一个外部托管的静态资产,确保它是支持SRI的。你可以使用这个方便的生成器自己添加SRI。

惩罚。网络交涉

我们付出的最大和最直接的惩罚之一是打开新的TCP连接的成本。我们需要访问的每一个新的源点都需要打开连接,而这可能是非常昂贵的:DNS解析、TCP握手和TLS协商都会增加,而且连接的延迟越高,情况就越糟糕。

我将使用一个直接来自Bootstrap自己的 "入门"的例子。他们指导用户包括以下四个文件。

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="..." crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="..." crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="..." crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="..." crossorigin="anonymous"></script>

这四个文件被托管在三个不同的源头,所以我们将需要打开三个TCP连接。这需要多少钱?

好吧,在一个相当快的连接上,将这些静态资产托管在网站外是311ms,或1.65倍,比自己托管它们要慢。

通过链接到三个不同的源头来提供静态资产,我们累计损失了805ms的网络协商时间。完整的测试。

好吧,这并不可怕,但我的一个客户Trainline发现,通过减少300ms的延迟,客户每年多花了800万英镑。这是一个相当快的方式来赚取八百万。

通过简单地将我们的资产转移到主机域,我们完全消除了任何额外的连接开销。全面测试。

在一个较慢的、高延迟的连接上,情况要糟糕得多。在3G上,外部托管的版本慢了1.765秒,令人瞠目结舌。 我想这是为了使我们的网站更快?

在一个高延迟的连接上,网络开销总计高达5.037秒。这些都是完全可以避免的。全面测试。

将资产转移到我们自己的基础设施上,使加载时间从5.4秒左右降至3.6秒。

通过自我托管我们的静态资产,我们不需要再打开任何连接。全面测试。

如果这还不足以成为自我托管静态资产的一个令人信服的理由,我不知道什么才是!

缓解措施。preconnect

自然地,我的整个观点是,如果你能够自我托管,你不应该在场外托管任何静态资产。然而,如果你的手被绑住了,那么你可以使用 preconnect 资源提示,预先打开一个TCP连接到指定的来源。

<head>

  ...

  <link rel="preconnect" href="https://code.jquery.com" />

  ...

</head>

为了加分,将这些部署为HTTP头文件会更快。

**注意:**即使你实现了preconnect ,你仍然只能在你损失的时间中做出一点贡献:你仍然需要打开相关的连接,而且,特别是在高延迟的连接上,你不可能完全偿还前期的开销。

惩罚。丧失优先权

第二个惩罚是在协议层面上的优化,我们在跨域分割内容的时候就错过了。如果你通过HTTP/2运行--现在,你应该是--你可以获得优先权。同一TCP连接中的所有数据流(也就是资源)都有一个优先级,浏览器和服务器协同工作,建立一个所有这些优先级数据流的依赖树,这样我们就能更快地返回关键资产,也许会推迟交付不太重要的资产。

为了充分理解优先级的好处,Pat Meenan关于这个主题的文章可以作为一个很好的入门读物。

**注:**从技术上讲,由于H/2的连接凝聚,只要请求共享相同的IP地址,就可以在不同的域中互相优先处理。

如果我们把我们的资产分割到多个领域,我们就必须开辟几个独特的TCP连接。我们不能交叉引用这些连接中的任何优先级,所以我们失去了以深思熟虑和精心设计的方式交付资产的能力。

比较两个HTTP/2的依赖树,分别是站外和自我托管的版本。

注意到我们如何需要为每个来源建立新的依赖关系树吗?流ID 1和3一直在重复出现。

通过将所有内容托管在同一来源下,我们可以建立一个更完整的依赖关系树。每个流都有一个唯一的ID,因为它们都在同一个树上。

有趣的是:数字为奇数的流ID是由客户端发起的;数字为偶数的流ID是由服务器发起的。老实说,我不认为我曾经在野外看到过偶数的ID。

如果我们从一个域中提供尽可能多的内容,我们就可以让H/2做它的事情,并更完整地确定资产的优先次序,以希望得到更及时的响应。

惩罚。缓存

总的来说,静态资产主机似乎在建立长效的max-age 指令方面做得很好。这是有道理的,因为静态资产的版本URL(如上)永远不会改变。这使得执行一个合理的、积极的缓存策略变得非常安全和明智。

也就是说,这并不总是这样,通过自我托管你的资产,你可以设计出更多的定制缓存策略

神话:跨域缓存

一个更有趣的观点是资产的跨域缓存的力量。也就是说,如果很多网站链接到同一个CDN托管的版本,比如jQuery,那么用户肯定已经在他们的机器上拥有这个确切的文件了。有点像点对点的资源共享。这是我听到的支持使用第三方静态资产提供者的最常见的论点之一。

不幸的是,似乎没有公开的证据来支持这些说法:没有任何证据表明这确实是事实。相反,保罗-卡尔瓦诺最近的研究暗示,情况可能正好相反。

在CSS和网页字体的第一方与第三方资源的年龄上存在着明显的差距。95%的第一方字体的年龄超过1周,而50%的第三方字体的年龄不到1周。这就为自我托管网络字体提供了一个强有力的理由。

一般来说,第三方的内容似乎比第一方的内容缓存得更少。

更重要的是,Safari已经完全禁用了这一功能,因为担心隐私被滥用,所以在写这篇文章的时候,共享缓存技术无法对全球16%的用户发挥作用。

简而言之,虽然理论上很好,但没有证据表明跨域缓存有任何效果。

神话:进入CDN

使用静态资产供应商的另一个通常被吹捧的好处是,他们可能正在运行具有CDN功能的强大基础设施:全球分布、可扩展、低延迟、高可用性。

虽然这是绝对正确的,但如果你关心性能,你应该已经从CDN运行你自己的内容。随着现代托管解决方案的价格上涨(本网站是由Cloudflare提供的,它是免费的),没有什么借口不从CDN中提供你自己的资产。

换句话说:如果你认为你的jQuery需要一个CDN,你将需要一个CDN来处理所有的事情。去找一个吧。

自我托管你的静态资产

真的没有什么理由把你的静态资产放在别人的基础设施上。所认为的好处往往是一个神话,即使不是这样,这种权衡也是不值得的。从多个源头加载资产显然是比较慢的。在接下来的几天里,花10分钟时间审核你的项目,并获取你自己控制下的任何站外静态资产。