你的位置:首页 > ASP.net教程

[ASP.net教程]使用Micrisoft.net设计方案 第三章Web表示模式 Web模式集群详细介绍 Page Cache(页面缓存)


在编写为用户提供动态信息的、基于 Web 的应用程序。观察到许多用户访问某个特定页面,但动态信息不发生改变。

如果动态生成的 Web 页被频繁请求并且构建时需要耗用大量的系统资源,那么,如何才能改进这类网页的响应时间?

下列影响因素作用于此上下文内的系统,在考虑问题的解决方案时必须协调这些因素:

1、生成动态 Web 页会耗用各种各样的系统资源。当 Web 服务器收到页面请求时,它通常必须从外部数据源(如数据库或 Web Service)检索所请求的信息。对这些资源的访问通常需要通过有限的资源池(如数据库连接、套接字或文件描述符)进行。因为 Web 服务器通常需要处理很多并发请求,所以对这些共享资源的争夺可能会延迟页面请求,直到资源变为可用。在将请求发送给外部数据源之后,仍然必须将结果转换为 HTML 代码以便进行显示。

2、使系统速度更快的一种显而易见的方法是购买更多的硬件。此方法可能很吸引人,因为硬件便宜(或者供应商这样说),而且不必更改程序。另一方面,更多的硬件只能在未达到其物理限制之前才会对性能有所帮助。网络限制(如数据传输速率)或等待时间使这些物理限制更明显。

3、使系统速度更快的第二种方法是减少系统处理的工作。此方法要求开发人员做更多的工作,但可以极大地提高性能。下面的内容将探讨此方法带来的挑战。  

对于频繁访问且生成比较耗资源的页面,我们通常采用缓存(页面缓存和数据缓存)的方式来解决,在使用该方案时需要考虑以下问题:

1、  缓存的时间

2、  是整页缓存,还是部分缓存

3、  页面的可变因素,比如参数、语言、设备不同所带来的变化

对频繁访问但更改不太频繁的动态 Web 页使用页面缓存。

结构

页面缓存的基本结构是相对简单的。Web 服务器维护包含预先生成的页面的本地数据存储(见图 1)。

图 1:页面缓存的基本配置

工作原理

下面的序列图阐明了页面缓存可以改进性能的原因。第一个序列图(见图 2)描述尚未缓存所需页面的初始状态(即所谓的缓存未命中)。在这种情况下,Web 服务器必须访问数据库,并生成 HTML 页,再将其存储在缓存中,然后将它返回给客户端浏览器。请注意,此过程比不进行缓存的情况稍慢,因为它执行了下列额外步骤:

  • 确定页面是否已缓存
  • 将页面转换为 HTML 代码,然后存储在缓存中

与数据库访问和 HTML 生成相比,其中的任一步骤都不应该花费很长时间。但是,因为在此情况下需要进行额外处理,所以您必须确保在系统完成与缓存未命中关联的步骤之后连续多次命中缓存(如图 2 所示)。

图 2:缓存未命中的序列(当页面不在缓存中时)

在图3所示的缓存命中情况下,页面已经处于缓存中。通过跳过数据库访问、页面生成和页面存储,缓存命中节省了循环。

图 3:缓存命中的序列(页面处于缓存中时)

实现

缓存策略是一个范围很广的主题,在单个模式中无法详尽论述。但是,在实现包括 Page Cache 的解决方案时,讨论最相关的注意事项是很重要的。

Page Cache 解决方案包含以下关键机制:

页面(或页面片段)存储

页面索引

缓存刷新

下面几段内容分别讨论这几个机制。

页面存储

页面缓存必须存储预先生成的页面,以便系统可以快速检索它们。您还希望能够存储尽可能多的页面,以便提高缓存命中的几率。在进行存储时,您通常需要对速度、 大小和成本进行权衡。较小的缓存可以驻留在内存中,而且速度可以非常快。较大的磁盘存储缓存提供的存储量较大,但是速度很慢。

要找出速度和大小的最佳平衡点,必须仔细确定缓存哪些页面。在理想情况下,应该只缓存访问频率高的页面,而忽略很少使用的页面。

下一个最重要的决定是缓存中的片段应该多大。存储完整页面可在页面命中之后快速显示页面,因为系统从缓存中检索页面,并立即将其发送到客户端,而不必执行任何其他操作。但是,如果页面的某些部分更改频繁而其他部分不是这样,则存储完整页面可能导致增加许多存储。存储较小的片段可提高页面命中的几率,但需要更多的存储开销和更多的 CPU 耗用。

页面索引

考虑系统如何在缓存中找到页面也是很重要的。系统查找页面的最简单方法是使用 URL。如果页面不依赖于任何其他因素,则只需通过将请求的 URL 与存储在缓存中的页面的 URL 进行比较,即可从缓存中检索到它。但是,很少发生这种情况。几乎所有动态页面都是根据参数建立的。因此,系统可能必须根据参数存储一个页面的多个实例。可以使用Vary-By-Parameter Caching 来实现此类型的缓存,其中页面内容依赖于参数。

缓存刷新

系统将项目在缓存中保留多长时间也是很重要的。按固定时间存储页面是最简单的方法。 但是,此方法可能未必一定是足够的。通过将缓存持续时间与外部事件关联,您可以解决这些问题。一些缓存策略尝试在低流量周期内预先生成页面。如果具有可预知的流量模式,并可以存储页面足够长的时间,以避免在高峰流量时间内进行刷新,则此方法可以是非常有效的。

Page Cache 具有以下优缺点:

优点

1、节省生成页面所需的 CPU 周期。对于大量并发用户,这导致响应时间更短,并提高了 Web 服务器的可伸缩性。

2、消除到数据库或其他外部数据源的不必要往返行程。此优点是特别重要的,因为这些外部源通常仅提供必须由资源池中所有并发页面请求共享的、有限数目的并发连接。对外部数据源的频繁访问会因资源争夺而很快导致 Web 服务器突然停止。

3、节省客户端连接。从客户端浏览器到 Web 服务器的每个并发连接都会耗用有限的资源。处理页面请求所用的时间越长,耗用连接资源的时间就越长。

4、支持许多页面请求的并发访问。因为页面缓存主要是一个只读资源,所以可以相当容易地对它进行多线程处理。因此,它防止了系统访问外部数据源时会发生的资源争夺。唯一必须同步的部分是缓存更新,因此围绕更新频率的注意事项对于获得良好性能是最关键的。

5、提高应用程序的可用性。如果系统需要访问外部数据源以生成页面,则它依赖于可用的数据源。甚至是在外部源变得不可用时,页面缓存页也允许系统将已缓存页面传递给客户端;数据可能不是最新的,但很可能比完全没有数据要好。

缺点

1、显示的信息不是最新的。如果缓存刷新机制配置得不正确,则 Web 站点可能显示无效数据,该数据可能使人误解或者甚至是有害的。

2、需要 CPU 和内存( RAM 或磁盘)资源。将不频繁查看的页面缓存起来,或设置太短的刷新间隔,可以导致开销增加,而且事实上会降低服务器性能。与所有性能度量一样,请使用实际的度量值和性能指示器进行全面的分析,以确定正确的设置。匆忙决定(如缓存每个页面)的坏处会大于好处。

3、增加了系统的复杂性并使其难于测试和调试。在大多数情况下,您应该在没有缓存的情况下开发和测试应用程序,然后在性能优化阶段启用缓存选项。

4、需要注意另外的安全事项。缓存所涉及的这一问题经常被忽略。当 Web 服务器处理多个用户发出的对机密信息的并发请求时,避免这些请求发生交叉是很重要的。因为页面缓存是全局实体,所以配置有误的页面缓存可能将原本为另一个 用户生成的页面传递给浏览器。

5、可以动态产生不一致的响应时间。虽然在 99% 的情况下快速传递页面肯定比每次都慢速传递页面要好,但是,如果缓存策略对缓存命中率的优化过高,并对缓存未命中率的优化过低,则会导致不定时发生的超时。

ASP.NET 中使用绝对过期实现 Page Cache

在 ASP.NET 中构建一个 Web 应用程序,并且希望对页面进行缓存以提高性能。已经评估了 Page Cache (页面缓存)中提出的备用选择方案。

实现策略

页面缓存通过对从动态网页生成的内容进行缓存来提高请求响应的吞吐量。默认情况下,在 ASP.NET 中支持页面缓存,但除非定义有效的过期策略,否则,不会对来自任何给定响应的输出进行缓存。要定义过期策略,可以使用低级 OutputCache API 或高级 @OutputCache 指令。

启用页面缓存后,对页面的第一个 GET 请求将创建一个页面缓存条目。该页面缓存条目用于响应随后的 GET 或 HEAD 请求,直到缓存的响应过期。

页面缓存遵循页面的过期策略。如果对过期策略为 60 秒的页面进行缓存,经过 60 秒之后,该页面将从输出缓存中删除。如果缓存在该时间之后收到另一个请求,它将执行网页代码并刷新缓存。这种过期策略称为"绝对过期",它意味着在某个时间之前页面一直是有效的。

下面的示例说明了使用@OutputCache指令对响应进行缓存的方式:

<%@ OutputCache Duration="60" VaryByParam="none" %>

该示例显示了生成响应的时间。要了解输出缓存的作用,请调用该网页,并注意生成响应的时间。然后刷新网页,您会注意到时间没有更改,这说明第二个响应是从缓存提供的。

下面的代码行激活对响应的页面缓存:

<%@ OutputCache Duration="60" VaryByParam="none" %> 

此指令只说明,页面应该缓存 60 秒,页面不会因任何 GET 或 POST 参数而改变。在第一个 60 秒内收到的请求是从缓存提供的。经过 60 秒之后,该页面从缓存中删除;下一个请求再次缓存该页面。

在 ASP.NET 中使用绝对过期模式来实现 Page Cache 具有以下优缺点:

优点

1、这是到目前为止 ASP.NET 中最简单的页面缓存方法。如果您要分析 Web 应用程序的使用模式来确定缓存哪些页面,那么,在许多情况下绝对过期可能已经足够了,而且无疑是一个很好的开端。另外,还要考虑到网页上动态内容的变动性。例如,天气网页的过期策略可以是 60 分钟,因为天气不会变得很快。不过,显示股票报价的网页可能根本不能缓存。为了确定正确的过期时间,必须知道哪些是最频繁查看的网页,并理解网页所包含的数据的变动性。

2、您可以为不同的网页设置不同的过期策略。通过这种做法,您可以只对频繁访问的网页进行缓存,而不会将缓存空间浪费在很少访问的网页上。您还可以刷新包含的数据比其他数据更经常变动的网页。

缺点

1、缓存的页面上的动态内容可能是无效的。这是因为页面过期基于时间而不是内容。在前面描述的示例中,时间在几秒钟之后才显示在网页上。因为该网页每 60 秒构建一次,秒字段在页面构建后的瞬间内是无效的。此示例中的无效数据部分是很小的。例如,如果您要显示对时间非常敏感的金融报价,并且需要很高的准确性,请考虑采用可确保您决不会显示无效数据的缓存策略。

2、此策略不支持将参数传递到网页。动态网页常常由参数来确定。例如,天气网页可能用邮政编码作为参数。除非您希望为数千个邮政编码(例如,美国有 42,000 个邮政编码)创建不同的网页和 URL,否则,您不能使用绝对过期对该网页进行缓存。Vary-By-Parameter Caching 解决了这个问题。

3、只有整个网页保持不变时才适用绝对过期。在许多应用程序中,网页的大部分很少发生更改(非常适用缓存),但与经常更改的其他部分(不能进行缓存)是耦合在一起的。因为绝对过期模式只缓存整个网页,它不能利用像这样的局部更改。在这些情况下,Page Fragment Caching 可能是一个更好的选择,因为它可以对网页的一部分进行缓存。HTML 框架为模拟页面分段提供了另一个选择。不过,框架在 Web 浏览器中有一些已知问题,例如,导航和打印问题。

4、无法刷新缓存的页面。 网页在过期或重新启动服务器之前,一直保留在缓存中。这就使测试成了问题。另外,在数据很少发生更改、可一旦发生更改决不能延迟的情况下,缓存也有很大的 问题。例如,每两小时更新天气预报在大多数时间可能已经足够了。不过,如果飓风正在逼近,您也许不希望等两小时后再更新天气预报。

5、必须修改每页的代码才能更改过期策略。因为过期策略只能在代码中更改,所以没有关闭整个应用程序的缓存的机制。

6、在缓存中存储网页需要服务器上的磁盘空间。在前面描述的示例中,较小的网页不会需要很多磁盘空间。不过,随着每个网页上的内容以及缓存中网页数目的增加,要求 Web 服务器提供更多的磁盘空间。

变体

下面的模式解释了 Page Cache 的其他实现方法:

Vary-By-Parameter Caching

Sliding Expiration Caching

相关模式

有关相关页面缓存设计和实现策略,请参阅以下模式:

Page Data Caching

Page Fragment Caching