技巧 5 —— 预请求缓冲(Per-Request Caching)
在本文前面,我曾提到对频繁执行的代码块所做的小小改动可能产生很大的,整体性能的提升。我把其中一个我特别中意的叫做预请求缓冲(per-request caching)。
由于 Cache API 被设计用来缓冲长期数据或直到某个条件被满足,预请求缓冲意旨用于请求期间的缓冲该数据。特定的代码流程被每次请求频繁访问但是数据只需要被拾取,应用,修改或更新一次,这样说太理论化,还是让我们看一个具体的例子吧。
在 Community Server 的 Forums (论坛)应用中,某个页面上使用的每个服务器控件需要个性化数据以确定使用那个皮肤和式样页,以及其它的个性化数据,其中有些数据可以被长时间缓冲,但有些数据,比如用于控件的皮肤在单个请求中只被拾取一次并在该请求执行期间被重用多次。
为了完成预请求缓冲,用 ASP.NET HttpContext。HttpContext 的实例是随每个请求创建的,并可以通过 HttpContext.Current 属性在那个请求执行期间的任何地方存取它。HttpContext 类具有一个特别的 Items 集合属性,被添加到该 Items 集合的对象和数据只是在该请求期间被缓存。就像你可以使用 Cache 来保存频繁使用的数据一样,你可以用 HttpContext.Items 来保存只在某个预请求中使用的数据。在此背景后的逻辑很简单:当数据不存在时被添加到 HttpContext.Items 集合,以及在随后的并发查找中简单地返回 HttpContext.Items 中发现的数据。
技巧 6——后台处理
你的代码流程应该尽可能快,对吧?你自己可能多次发现要完成每个请求或每n个请求的任务代价很高。发出 e-mail 或解析并检查输入数据的有效性就是个例。
在重新生成 ASP.NET Forums 1.0 并把它整合到 Community Server 时,我们发现添加新贴的代码流程非常慢。每次添加帖子,应用程序首先要确保没有重复贴,然后必须用“badword”过滤器解析该贴的表情图像,记号并索引,如果必要还要将帖子添加到相应的队列中,对附件进行有效性检查,最终完成发贴后,给预订者发出 e-mail 通知。显然,这里做的工作太多。
我们发现大多数时间都花在了索引逻辑和发送e-mail上。索引帖子是一个很耗时的操作,此外,内建的 System.Web.Mail 功能要与 SMTP 服务器连接并顺序发送邮件。当特定帖子或主题预定者数量增加时,AddPost 函数的执行时间会越来越长。
并不是每个请求都需要索引邮件,我们想最好是批量集中处理,并且一次只索引25个帖子或每隔五分钟发送一次邮件。我们决定使用的代码与我曾在原型数据库缓冲失效中所使用的代码相同,最终它也被纳入 Visual Studio 2005。
名字空间 System.Threading 中的 Timer 类非常有用,但在.NET 框架中鲜为人知,至少对 Web 开发者来说是这样。一旦创建,Timer 将以可定制的间隔针对线程池中的某个线程调用指定的回调函数。这意味着你不用输入请求到 ASP.NET 应用程序便能让代码实行,这是一种最合适后台处理的情形。你也可以在这种后台处理模式中进行例如索引或发送电子邮件这样的工作。
尽管如此,这个技术存在几个问题,如果你的应用程序域关闭,该定时器实例将停止触发其事件。另外,由于 CLR 有一个硬坎,即每个进程的线程数是固定的,你便可能陷入严重的服务器负荷当中,此时可能就没有线程来处理定时器,从而造成延时。为了让发生这种情况的几率最小化,ASP.NET 通过在进程中预留一定数量的空闲线程,并只使用部分线程来处理请求。然而,如果你有许多异步处理,这样做会有问题。
由于篇幅所限,在此无法列出代码,但你可以从 www.rob-howard.net 下载可消化的例子。其中有 Blackbelt TechEd 2004 展示的幻灯和 Demo。
|