掌握HTTP缓存——从请求到响应的一切过程(下)过去,一些小网站根本不需要使用CDN,或者根本负担不起它的价格,但近几年这种现象发生了很大的变化。CDN市场上有很多按次付费和非企业的提供商,这使得CDN成为了一项人人都能负担得起的服务。本文介绍如何使用这个简单易用的缓存服务。在上一篇文章《精通HTTP缓存——从请求到响应全过程(上)》中,我们讨论了使用HTTP头来解决缓存问题。在这篇文章中,我们将介绍缓存和cookie之间的关系。Cookies你已经知道缓存头是如何工作的,现在让我们看看cookie在缓存中的作用。首先,Cookie的设置也在HTTP响应头中,名称为Set-Cookie。设置cookie的目的是为了识别用户,这意味着您需要为每个用户设置一个cookie。想象一个缓存场景,你会缓存一个包含Set-Cookie的HTTP响应,并且在缓存期间,每个人都会得到相同的cookie和相同的用户会话吗?你绝对不想要这个。此外,用户会话状态的变化可能会影响响应内容的变化。一个简单的场景:电商购物车。您要么为用户提供一个空的购物车,要么为用户提供一个包含许多用户选择的商品的购物车。出于同样的原因,您也不希望缓存它,毕竟每个用户都应该有自己的购物车。一种解决方法是在运行时通过JavaScript设置cookie,例如GoogleAnalytics。GA通过JS设置了一个cookie,但是这个cookie既不影响渲染也不设置Set-Cookieheader。GA会在目标站点上添加一个类似于“您通过GoogleAnalytics进行跟踪”的图标,但只要在运行时添加这些更改,就应该没问题。正确处理cookie和缓存首先要了解您网站的cookie是如何工作的。cookie是否只在特定时间使用(例如在用户登录过程中使用)?原则上,cookie是否注入到所有响应中?正如上一节所述,每当服务器返回一个set-Cookie响应时,您希望能够保证它不会被缓存。那么问题就变成了,当你返回一个带有“用户特定”内容(比如购物车)的响应时,CDN/代理服务器会做什么?如果不设置Set-Cookie,是否允许缓存?如果设置了Set-Cookie,它会自动丢弃所有Cache-Control标头吗?事实上,如果你从应用层面来看,你可以实现你最喜欢的网络应用。至于cookies和CDN,是自动设置的。或者以Apache的.htaccess为例说明:#1)如果没有设置cookie,允许缓存HeadersetCache-Control"publicmax-age=3600""expr=-zresp('Set-Cookie')#2)如果设置了cookie,则不允许缓存HeadealwaysremoveCache-Control"expr=-nresp('Set-Cookie')#2a)第二篇的另一种形式,如果设置了cookie,则缓存时间设置为0HeadersetCache-control"no-cachemax-age=0must-revalidate""expr=-nresp('Set-Cookie')规则1:如果Set-Cookie没有设置,则为Cache-Control设置一个默认值;规则2:如果Set-设置了Cookie,IgnoreCache-Control;Rule2a:rule2的另一种表现形式,设置最大缓存时间为0设置一个cookie取决于不同的因素,比如session时间因素,如果你有一个高安全性的web应用并且设置一个5分钟的session时间,那么为每个响应设置一个新的cookie并不过分。并且假设您的应用程序甚至没有“用户特征”,也就是说,一切对所有用户都是通用的,那么设置任何类型的cookie就毫无意义。所以下面的例子是否适合你很大程度上取决于你有什么类型的应用程序。一起来看看吧,我先给出这个例子的上下文:假设你有一个新网站,你所有的文章都在路径http://www.foobar.tld/news/item/下。现在您希望能够确保到/news/item/的所有路径不包含Set-Cookie,因为您确定不需要cookie。#通用的PHP重定向方式,在重定向规则中写入"?path=$1"RewriteCond%{REQUEST_FILENAME}!-dRewriteCond%{REQUEST_FILENAME}!-fRewriteRule^(.*)$index.php?path=$1[NC,L,QSA]RewriteRule^$index.php[NC,L,QSA]#利用查询中的path=来判断HeaderalwaysunsetSet-Cookie通过这个设置可以保证所有访问/news/item/的路径不包含Set-Cookie。是否设置cookie取决于您自身的应用特性。通过设计进行缓存有许多设计选项可以使您的Web应用程序高度可缓存。由于这篇文章只是一篇文章而不是一本书,我不能深入到每个点,但我可以强调一下一般方法。我也以电子商务为例。假设电子商务网站首页顶部位置展示的是正在销售的商品,生成这些商品需要多次数据库操作,开销比较大,所以希望缓存起来。但是,问题出在购物车上,它是为那些登录用户准备的,所以想要的结果是:topitems是一样的,购物车是为登录用户展示的。那么优化策略首先要为每个用户提供一个与登录状态无关的“通用”页面。然后通过JavaScript为已经生成的网页提供购物车。从用户的角度来看,最终的展示形式是一样的。所以现在你有两个请求(整页请求+购物车请求)而不是一个请求(整页请求,包括购物车)。好的,现在你可以分离出昂贵的部分,最上面的项目,并缓存它们。此方法或其扩展不适用于已开发的项目。因为它可能会改变很多界面和视图层(MVC架构)的内容。最好从一开始就设计它。缓存失效:破坏和清除使用max-age和s-maxage,您已经可以很好地控制给定响应的缓存时间。但这还不足以应对所有情况。这些设置是在返回响应时预设的,但现实情况往往是不知道应该将响应设置为多长时间过期。回想刚才电商首页的例子:假设它包含10个实体显示在顶部位置。您为此主页设置max-age=900以确保它每15分钟刷新一次。现在,其中一个实体因为发布时间太长,要被撤销了,那么你需要删除之前缓存的响应,现在还没有15分钟,怎么办?别担心,这是一个常见问题,有很多方法可以解决。首先解释一下这个名词:Cachebusting是用来解决浏览器的长期缓存问题。它使用版本标识符告诉浏览器文件有新版本。此时浏览器不会从本地缓存中获取内容,而是会向源服务器请求新版本的文件。缓存清除的详细介绍在这里:WhatisCacheBusting?。缓存清除是指直接从缓存中删除内容(即响应),以便缓存可以立即更新。用于版本管理的缓存清除通常用于CSS文件和JS文件。通常可以使用确切的版本号、哈希字符串或时间戳作为标识符,如下例所示:数字版本号:style-v1.css,style.css?v=1哈希字符串版本:style.css?d3b07384d113edec49eaa6238ad5ff00Timestampversion:styles.css?t=1486398121此时发布程序时只需要注意文件的版本即可。例如,一个HTML页面包含一个这种形式的CSS文件。CSS文件将被缓存,所以如果你想让你的新CSS文件工作,只需用最新的版本号命名它。如果您不做任何更改,此HTML仍将使用缓存中的旧CSS文件,即使您更新文件也是如此。缓存清除不同的CDN提供商有不同的方法来清除缓存。许多提供商基于开源软件Varnish构建自己的CDN服务,因此常见的做法是在HTTP请求中使用PURGE结构,例如:PURGE/news/item/i-am-obsoleteHTTP/1.1Host:www。foob??ar.tld使用此请求通常需要权限认证或来源确认(即IP白名单),但不同供应商的要求是不同的。清除一个或几个缓存项很容易,但在某些情况下,并不是那么简单。比如博客场景,博客里面有关于作者的栏目,现在想要更改关于作者的一些内容,那么就需要手动清理所有包含作者信息的页面。你确实可以一个一个手动清理它们,但是如果你有成千上万的网页受到影响,问题就变得麻烦了。下面介绍一种解决方案。ProxyTag“ProxyTag”这个名字来自CDN提供商Fastly。不同的供应商给它起不同的名字。比如还有一个“缓存标签”,Varnish称之为Hashtwo/Xkey。这里就不详细介绍了。其他供应商。不管它叫什么,它们的目的都是一样的:标记响应。这样您就可以轻松地从缓存中删除相关标签,甚至不知道缓存的是什么。仍然以为例,源返回一个带有代理标签的响应:HTTP/1.1200OKContent-Type:text/htmlContent-Length:123Surrogate-Key:top-10company-acmecategory-foodstuff这个标签示例中有:top-10、company-acme和category-foodstuff。这里有一个实际的电商场景来理解它的意思:这个响应包含了电商首页的前10个商品,这些商品都是ACME提供的,它们的目录类别都设置为食品。设置标签后,当item发生变化时,只需要删除包含company-acme和top-10的标签即可。是不是很简单?同样,对于不同的CDN提供商,如何清除缓存的具体操作方法也不同。点击《掌握 HTTP 缓存——从请求到响应过程的一切(下)》阅读原文。【本文为专栏作者“虎子打哈”原创文章,转载请联系作者获得授权】点此阅读更多该作者好文