技巧 5 — 每请求缓存
在本文前面部分,我提到了对经常遍历代码路径的一些小改善可以获得较大的整体性能收益。对于这些小改善,其中有一个绝对是我的最爱,我将其称之为“每请求缓存”。
缓存 API 的设计目的是为了将数据缓存较长的一段时间,或者缓存至满足某些条件时,但每请求缓存则意味着只将数据缓存为该请求的持续时间。对于每个请求,要经常访问某个特定的代码路径,但是数据却只需提取、应用、修改或更新一次。这听起来有些理论化,那么我们来举一个具体的示例。
在社区服务器的论坛应用程序中,页面上使用的每个服务器控件都需要个性化的数据来确定使用什么外观、使用什么样式表,以及其他个性化数据。这些数据中有些可以长期缓存,但是有些数据却只针对每个请求提取一次,然后在执行该请求期间对其重用多次,如要用于控件的外观。
为了达到每请求缓存,请使用 ASP.NET HttpContext。对于每个请求,都会创建一个 HttpContext 实例,在该请求期间从 HttpContext.Current 属性的任何位置都可访问该实例。该 HttpContext 类具有一个特殊的 Items 集合属性;添加到此 Items 集合的对象和数据只在该请求持续期间内进行缓存。正如您可以使用缓存来存储经常访问的数据一样,您也可以使用 HttpContext.Items 来存储只基于每个请求使用的数据。它背后的逻辑非常简单:数据在它不存在的时候添加到 HttpContext.Items 集合,在后来的查找中,只是返回 HttpContext.Items 中的数据。
技巧 6 — 后台处理
通往代码的路径应该尽可能快速,是吗?可能有时您会发现您正在执行的针对每个请求执行的或者每 n 个请求执行一次的任务所需资源非常多。发送电子邮件或者分析和验证传入数据就是这样的一些例子。
剖析 ASP.NET Forums 1.0 并重新构建组成社区服务器的内容时,我们发现发布新帖子的代码路径非常慢。每次发布新帖子的时候,应用程序首先需要确保没有重复的帖子,然后必须使用“坏词”筛选器分析该帖子,分析帖子的字符图释,对帖子添加标记并进行索引,请求时将帖子添加到合适的队列,验证附件,最终在帖子发布之后,立即向所有订阅者发出电子邮件通知。很清楚,这涉及很多操作。
经研究发现,大多数时间都花在了索引逻辑和发送电子邮件上。对帖子进行索引是一个非常耗时的操作,人们发现内置的 System.Web.Mail 功能要连接 SMTP 服务器,然后连续发送电子邮件。当某个特定帖子或主题领域的订阅者数量增加时,执行 AddPost 功能所需的时间也越来越长。
并不需要针对每个请求都进行电子邮件索引。理想情况下,我们想要将此操作进行批处理,一次索引 25 个帖子或者每五分钟发送一次所有电子邮件。我们决定使用我曾经用于对数据缓存失效进行原型设计的代码,这个失效是最终被包含进了Visual Studio 2005之中。
System.Threading 命名空间中的 Timer 类非常有用,但是在 .NET Framework 中不是很有名,至少对于 Web 开发人员来说是这样。创建之后,这个 Timer 类将以一个可配置的间隔针对 ThreadPool 中的某个线程调用指定的回调。这就表示,您可以对代码进行设置,使其能够在没有对 ASP.NET 应用程序进行传入请求的情况下得以执行,这是后台处理的理想情况。您还可以在此后台进程中执行如索引或发送电子邮件之类的操作。
但是,这一技术有几个问题。如果应用程序域卸载,该计时器实例将停止激发事件。另外,因为 CLR 对于每个进程的线程数量具有一个硬性标准,所以在负载很重的服务器可能会出现这样的情形:其中的计时器可能不能保证线程继续完成操作,并且在某种程度上可能会造成延迟。ASP.NET 通过在进程中保留一定数量的可用线程,并且仅使用总线程的一部分用于请求处理,试图将上述情况发生的机会降到最低。但是,如果您具有很多异步操作时,这可能就是一个问题了。
这里没有足够的空间来放置该代码,但是您可以下载一个容易理解的示例,网址是www.rob-howard.net。请了解一下 Blackbelt TechEd 2004 演示中的幻灯片和演示。
技巧 7 — 页输出缓存和代理服务器
ASP.NET 是您的表示层(或者说应该是您的表示层);它由页、用户控件、服务器控件(HttpHandlers 和 HttpModules)以及它们生成的内容组成。如果您具有一个 ASP.NET 页,它会生成输出(HTML、XML、图像或任何其他数据),并且您针对每个请求运行此代码时,它都会生成相同的输出,那么您就拥有一个可用于页输出缓存的绝佳备选内容。
通过将下面这行内容添加页的最上端:
<%@ Page OutputCache VaryByParams="none" Duration="60" %>
您就可以高效地为此页生成一次输出,然后对它进行多次重用,时间最长为 60 秒,此时该页将重新执行,输出也将再一次添加到 ASP.NET 缓存。通过使用一些低级别可编程API 也可以完成此行为。对于输出缓存有几个可配置的设置,如刚刚讲到的 VaryByParams 属性。VaryByParams 刚好被请求到,但还允许您指定 HTTP GET 或 HTTP POST 参数来更改缓存项。例如,只需设置 VaryByParam="Report" 即可对 default.aspx?Report=1 或 default.aspx?Report=2 进行输出缓存。通过指定一个以分号分隔的列表,还可以指定其他参数。
很多人还没有意识到当使用了输出缓存之后,ASP.NET 页也会生成一些向下流到缓存服务器的 HTTP 标题头,如 Microsoft Internet Security 和 Acceleration Server 或 Akamai 使用的标题头。设置了 HTTP 缓存表题头之后,可以在这些网络资源上对文档进行缓存,客户端请求也可在不必返回原始服务器的情况下得以满足。
因此,使用页输出缓存不会使得您的应用程序效率更高,但是它可能会减少服务器上的负载,因为下行流缓存技术会缓存文档。当然,这只能是匿名内容;一旦它成为下行流之后,您就再也不会看到这些请求,并且再也无法执行身份验证以阻止对它的访问了。
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




