• Web缓存解决方案


    缓存是构建于HTTP统一接口之上的最有用功能之一。可以利用缓存减少终端用户感知到的延时,增加可靠性,减少带宽使用和成本,降低服务器负载。缓存无处不在,可以在服务器网络里,内容分发网络(Content delivery network,简称CDN)或是客户端网络里(通常被称为转发代理Forward Proxy)。

    1. 如何设置过期缓存头
      当缓存可以在不访问源服务器时做出尽可能多的响应时,它是最高效的。设计过期缓存(Expiration Caching)就是为了降低源服务器收到的请求数量,同时减少应用程序使用的带宽。过期缓存基于Cache-Control和Expires这两个头,它们指导客户端和缓存在一段指定时间内保存从服务器返回的表述副本。在这个时间窗口以内,甚至超出该时间窗口,缓存可以对后续请求做出响应,无需访问源服务器。

      下面是Cache-Control指令列表及其适用性:

      public 默认指令。当请求是经过身份验证的,但您仍希望共享缓存提供缓存响应时,也可以使用该指令。
      private当响应专属于某个客户端或用户时,使用该指令。任意客户端缓存(如浏览器缓存或转发代理)都可以缓存表述,但诸如服务器缓存或网络之类的共享缓存则不能进行缓存。在基于客户端或用户身份验证来提供表述的时候添加该指令。
      no-cache和no-store 通过这些指令可以避免缓存存储或提供已经缓存的响应。如非必要,一般不使用这些指令。
      max-age该指令的值即为新鲜寿命,单位为秒。
      s-maxage这个指令与max-age类似,但只对共享缓存有意义。在源服务器同时设置了max-age和s-maxage时,缓存会使用s-maxage头。实际上,单独设置max-age指令就足够了。
      must-revalidate要求缓存在提供陈旧的表述前先检查源服务器。
      proxy-revalidate只应用于共享缓存。
      最佳过期缓存的关键是为资源表述计算一个合理的新鲜寿命值。如果有历史信息,例如表述的更新日志,可以参考它们来建立基线寿命。如果没有此类数据,可以先从一个合理的推测值出发,在获取更多信息后对其做出调整。通常这些信息源自于您发现某个客户端看不到最近更新的表述。
      类似Squid这样的缓存还支持两个Cache-Control头的扩展指令——state-if-error和state-while-revalidate。服务器可以用stale-if-error来告诉缓存,它们可以在指定时间间隔内继续提供陈旧的响应。
    2. 何时设置过期缓存头
      HTTP规定了什么是能缓存的,什么是不能缓存的,缓存也可能只实现了部分HTTP缓存协议。
      为所有带成功响应码的GET和HEAD请求的响应设置过期缓存头。虽然POST是可缓存的,但缓存会把该方法视为不可缓存的。其他方法无需设置过期缓存头。
      除了带200(OK)响应码的成功响应,还可以考虑缓存待3**和4**响应码的HTTP头,这能减少由客户端错误带来的流量。这种做法被称为消极缓存(negative caching)。
       
      300(multiple choices)带这个状态码的表述不太可能经常发送变化,缓存此类应答可以降低服务器负载。
      301(Moved Permanently)当资源永久移动时,那些将URI存储在数据库中的客户端可能不会修改这些URI。在这种情况下,缓存可以不用访问源服务器,直接提供重定向响应。
      400(Bad Request)当服务器返回该状态码时,客户端按理说不该重复请求,但出于软件错误或恶意攻击等原因,有些客户端会重复发起请求。
      403(Forbidden)如果服务器永久拒绝服务该资源,请为其添加缓存。
      404(Not Found)该资源不存在,而且没必要让服务器仅为失败生成表述。
      405(Method Not Allowed)客户端可能会因为软件错误而重复发送此类请求。
      410(Gone)资源不再存在了,所以缓存应该尽可能久地提供错误响应。
    3. 何时以及如何在客户端使用过期缓存头
      除非你是在构建一个封装在压缩包内的、需要用户安装并运行的客户端应用程序,否则应该避免在客户端应用程序中实现对过去缓存的支持。可以在客户端网络中部署一个转发代理缓存,避免在客户端代码中实现自己的缓存层。
      通常情况下,客户端应独立于过期缓存之外。理论上是有可能构建支持HTTP缓存协议的客户端应用程序的。举例来说,常见的浏览器实现了过期缓存,并将表述存储在内存或文件系统之中。实际上,在客户端应用程序中构建并维护一个缓存是项很麻烦的工作。它涉及正确地实现no-store,no-cache和must-revalidate这样的过期指令,还要遵循Vary头的内容。在同一个运行时内放入缓存还会造成客户端应用程序代码与缓存代码争夺内存与CPU资源,这会让客户端的调优变得更加困难。
      相较之下,在客户端和服务器之间放置一个转发代理缓存更容易一些。不涉及任何开发活动,你就可以坐享经过精心测试的、健壮的缓存基础设施所带来的好处,无须自己构建。这么做还为日后扩展留下了空间,你可以架设一个所有客户端共享的转发代理服务器集群。
      如果客户端和服务器在同一个网络里,不一定需要转发代理。服务器上可以部署一个被所有客户端共享的缓存。但如果客户端是在和一个其他网络中的第三方Web服务交互,那么使用转发代理可以帮助减少客户端与服务器之间往返请求次数。
    4. 如何支持复合资源的缓存
      基于那些对新旧程度有强烈要求的数据来制定缓存决策,根据它们的变更频率来设置过期头。
      相比其它类型资源,复合资源实现缓存更为复杂,此类资源包含与其他资源重叠的数据,它们可能会有不同的过期时间。
      复合资源是便利性与缓存效率之间权衡的一个好例子。对客户端而言,复合资源用起来很方便,但服务器要提供最新的资源却代价很大。
      一个解决办法是:将复合资源打散成三个资源,但这就强迫客户端发起多次请求来获取数据。换言之,服务器需要考虑利害关系并做出权衡。当资源的表述粒度较粗时,就会发生这种情况,有可能会以独立资源的方式来提供表述中所含的数据。
    5. 如何保持新鲜且温暖的缓存
      缓存支持的众多挑战之一就是保持缓存的“新鲜”(最新状态)和“温暖”(非空),就算客户端没有发起请求时也是如此。以一个照片分享服务为例,在用户上传照片后,所有针对这些照片的缓存都是空的,因此服务器不得不生成照片的表述。类似的,当引入一个新的缓存时,它是空的,随着客户端开始发起请求后,它才会慢慢有内容。当缓存处于新鲜状态时,其中包含最新的表述。一个“温暖”的缓存能避免冷启动问题。但是,预先保持缓存的新鲜与温暖不在HTTP范畴之内。
      尽可能地让过期时间与更新频率保持同步。当这点很难做到时,实现一些后台进程来监控数据库变更,定时执行无条件GET请求来刷新缓存。在定时调度这些请求时,一定要考虑到数据库复制的延迟。
      如果正在使用Squid,可以使用HTTP缓存频道扩展(HTTP cache channel extension)向缓存传播资源更新。
      在HTTP中,当客户端提交PUT,POST或DELETE请求时,要求缓存作废表述。此后,客户端再对同一资源发起GET或HEAD请求时,缓存就能从源服务器获得一个新的表述。尽管这种做法不太高效(因为缓存无法保持非空状态),但它却保证了客户端能获得最新的表述。
      现实中,你的网络里可能有多个应用程序会读写相同的数据存储,只要其中任意应用程序的写操作绕过HTTP缓存和服务器,那么就会导致缓存的表述变旧。
      考虑以下场景:
      服务器上可能会有每晚执行的定时任务,更新摘要数据表以反映日间的工作。这个更新可能直接发生在数据库层面上,不会有任何HTTP请求。但你可能缓存了这个资源,一旦定时任务执行了,存储在缓存中的应答就旧了。
      广泛分布的数据存储在一天里可能会周期性地进行复制。数据的改变不会反映在全部的缓存里。
      大型应用程序可能会有一个或多个客户端或服务是使用自定义协议来更新数据的。只要使用这些服务就可以在不通过HTTP的情况下改变数据库,此时缓存并不知道资源发生了变化。
  • 相关阅读:
    PetaLinux 生成 Zynq 操作系统
    单片机、微控制器和微处理器有什么区别
    嵌入式基础概念系列(1) —— GPIO
    学中杂记
    Spring学习笔记
    jdbc一点小笔记
    JSP学习
    Servlet学习的一些笔记
    接触Struts2的ModelDriven<>接口
    android-dialog的位置
  • 原文地址:https://www.cnblogs.com/missyxu/p/3382244.html
Copyright © 2020-2023  润新知