代码改变世界

ASP.NET自定义输出缓存提供程序

2012-04-09 23:02  马伟  阅读(615)  评论(0编辑  收藏  举报

      我们知道,自从ASP.NET 发布以来,页输出缓存使开发人员能够把由网页、控件及HTTP响应等生成的输出内容存储到内存中。这样一来,在后面的Web请求时,系统能够从内存检索这些生成的输出内容而不是从头开始重新生成输出,从而使ASP.NET可以更迅速地提供内容,在性能上得到了很大的提高。但是,这种方法确有一个限制:即生成的内容一定要存储在内存中。这样一来,服务器将承受巨大流量带来的压力,输出缓存消耗的内存与来自Web应用程序的其他部分的内存需求之间导致严重冲突。

      针对上述情况,ASP.NET 4针对输出缓存增加了一个扩展点,它能够使你可以配置一个或多个自定义输出缓存提供程序。输出缓存提供程序可以使用任何的存储机制来存储HTML内容。这使得开发者有可能针对不同的持久性机制来创建自定义的输出缓存提供程序,其中可以包括本地或远程磁盘、数据库、云存储和分布式缓存引擎(如velocity、memcached)等等。

      要实现自定义输出缓存提供程序,你可以通过从System.Web.Caching.OutputCacheProvider类中派生一个类来创建自定义输出缓存提供程序。例如,下面的MyOutputCacheProvider类就是派生自OutputCacheProvider类,并创建了一个自定义输出缓存提供程序,如代码清单19-5所示:

      代码清单19-5:MyOutputCacheProvider.cs

using System;

using System.Collections.Generic;

using System.IO;

using System.Linq;

using System.Runtime.Serialization.Formatters.Binary;

using System.Timers;

using System.Web.Caching;

using System.Collections.Concurrent;

namespace _19_1

{

    public class MyOutputCacheProvider : OutputCacheProvider

    {

        private Timer timer;

        private string cachePath = "C:\\Cache\\";

        private ConcurrentDictionary<string, DateTime> 

cacheExpireList;

        private const string KEY_PREFIX = "__outputCache_";

        public MyOutputCacheProvider()

        {

            cacheExpireList = 

new ConcurrentDictionary<string, DateTime>();

            timer = new Timer(3000);

            timer.Elapsed += (o, e) =>

            {

                var discardedList = from cacheItem in cacheExpireList

                             where cacheItem.Value < DateTime.Now

                             select cacheItem;

                foreach (var discarded in discardedList)

                {

                    Remove(discarded.Key);

                    DateTime discardedDate;

                    cacheExpireList.TryRemove(discarded.Key, 

out discardedDate);

                }

            };

            timer.Start();

        }

        /// <summary>

        /// 添加缓存

        /// </summary>

        /// <param name="key">缓存的键</param>

        /// <param name="entry">缓存的对象</param>

        /// <param name="utcExpiry">过期时间</param>

        /// <returns>返回缓存值</returns>

        public override object Add(string key, object entry,

 DateTime utcExpiry)

        {

            FileStream fs = new FileStream(

String.Format("{0}{1}.binary", cachePath, key),

 FileMode.Create, FileAccess.Write);

            BinaryFormatter formatter = new BinaryFormatter();

            formatter.Serialize(fs, entry);

            fs.Close();

            cacheExpireList.TryAdd(key, utcExpiry.ToLocalTime());

            return entry;

        }

        /// <summary>

        /// 获得缓存值 

        /// </summary>

        /// <param name="key">缓存键</param>

        /// <returns>缓存值</returns>

        public override object Get(string key)

        {

            string path = 

String.Format("{0}{1}.binary", cachePath, key);

            if (File.Exists(path))

            {

                FileStream fs = new FileStream(

path, FileMode.Open, FileAccess.Read);

                BinaryFormatter formatter = new BinaryFormatter();

                object result = formatter.Deserialize(fs);

                fs.Close();

                return result;

            }

            else

            {

                return null;

            }

        }

        /// <summary>

        /// 根据键移除缓存

        /// </summary>

        /// <param name="key">缓存键</param>

        public override void Remove(string key)

        {

            string path = 

String.Format("{0}{1}.binary", cachePath, key);

            if (File.Exists(path))

            {

                File.Delete(path);

            }

        }

        /// <summary>

        /// 设置缓存

        /// </summary>

        /// <param name="key">缓存的键</param>

        /// <param name="entry">缓存的对象</param>

        /// <param name="utcExpiry">过期时间</param>

        public override void Set(string key, object entry, 

DateTime utcExpiry)

        {

            string path = 

String.Format("{0}{1}.binary", cachePath, key);

            FileStream fs = new FileStream(

path, FileMode.Create, FileAccess.Write);

            BinaryFormatter formatter = new BinaryFormatter();

            formatter.Serialize(fs, entry);

            fs.Close();

            cacheExpireList.TryAdd(key, utcExpiry.ToLocalTime());

        }

    }

}

     在MyOutputCacheProvider类中,我们重写了OutputCacheProvider类的Add、Get、Remove与Set方法,并将页输出缓存到“C:\\Cache\\”文件夹里。

     定义好MyOutputCacheProvider类之后,你可以通过使用OutputCache元素的新的providers节在Web.config文件中配置提供程序,如下面的示例所示:

<caching>

  <outputCache defaultProvider="AspNetInternalProvider">

    <providers>

      <add name="MyOutputCacheProvider" 

type="_19_1.MyOutputCacheProvider,19-1"/>

    </providers>

  </outputCache>

</caching>

      默认情况下,在ASP.NET 4中所有的HTTP响应、生成的网页以及控件都使用内存输出缓存,其中defaultProvider属性被默认设置为AspNetInternalProvider。当然,你可以更改Web应用程序中所使用的默认的输出缓存提供程序,这是通过为defaultProvider指定一个不同的提供程序名称实现的。如下面的代码所示:

<caching>

  <outputCache defaultProvider="MyOutputCacheProvider">

    <providers>

      <add name="MyOutputCacheProvider" 

type="_19_1.MyOutputCacheProvider,19-1"/>

    </providers>

  </outputCache>

</caching>

      此外,还可以针对每个控件和每个请求选择不同的输出缓存提供程序。为不同的Web用户控件选择不同的输出缓存提供程序的最简单的方法就是在用户控件的指令中以声明方式使用新的ProviderName属性。

     下面,我们将在MyOutputCacheProviderUserControl用户控件里定义该输出缓存提供程序,如下面的代码所示:

<%@ Control Language="C#" AutoEventWireup="true"
 CodeBehind="MyOutputCacheProviderUserControl.ascx.cs"
 Inherits="_19_1.MyOutputCacheProviderUserControl" %>
<%@ OutputCache Duration="30" VaryByParam="none" ProviderName="MyOutputCacheProvider" %>
<asp:Label ID="Label1" runat="server" />

      MyOutputCacheProviderUserControl.ascx.cs代码如下所示:

     

public partial class MyOutputCacheProviderUserControl :System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        this.Label1.Text=DateTime.Now.ToLongTimeString();
    }
}

      定义好MyOutputCacheProviderUserControl用户控件之后,下面我们继续来定义一个测试页面MyOutputCacheProviderWebForm.aspx,代码如下所示:

     

<%@ Page Language="C#" AutoEventWireup="true"

 CodeBehind="MyOutputCacheProviderWebForm.aspx.cs"
 Inherits="_19_1.MyOutputCacheProviderWebForm" %>
<%@ Register src="MyOutputCacheProviderUserControl.ascx"
 tagname="MyOutputCacheProviderUserControl" tagprefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Button ID="Button1" runat="server" Text="刷新页面" />
        <br />
        MyOutputCacheProviderUserControl:
        <uc1:MyOutputCacheProviderUserControl ID="MyOutputCacheProviderUserControl1"  runat="server" />
            <br />
         MyOutputCacheProviderWebForm
        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
    </div>
    </form>
</body>
</html>

    MyOutputCacheProviderWebForm.aspx.cs代码如下所示:

public partial class MyOutputCacheProviderWebForm :

 System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        this.Label1.Text=DateTime.Now.ToLongTimeString();

    }

}

    运行MyOutputCacheProviderWebForm.aspx页面,结果如图19-5所示:

 

图19-5:示例运行结果

    打开“C:\\Cache\\”文件夹,你就能够看见所生成的缓存文件,如图19-6与19-7所示:

 

图19-6:在本地磁盘生成的缓存文件

 

图19-7:缓存文件内容

       这里需要注意的是,你只能在用户控件中指定@ OutputCache指令的ProviderName属性,但在ASPX页面是不允许你指定@ OutputCache指令的ProviderName属性的。因为在页面中,默认情况下会使用web.config中配置的defaultProvider。

       虽然你不能够以声明的方式指定@ OutputCache指令的ProviderName属性,但你可以在Global.asax文件中通过重载GetOutputCacheProviderName(HttpContext context)方法以编程方式指定针对一个具体的请求使用哪一个提供程序。如下面的代码所示:

public override string GetOutputCacheProviderName(HttpContext context)
{
    if (context.Request.Path.EndsWith("MyOutputCacheProviderWebForm.aspx"))
    {
        return "MyOutputCacheProvider";
    }
    else
    {
        return base.GetOutputCacheProviderName(context);
    }
}


      利用ASP.NET 4中增加的输出缓存提供程序扩展,你现在可以为你的Web站点采取更积极和更智能化的输出缓存策略。例如,你可以把一个网站中的用户访问量占“前十名”的那些网页缓存到内存中,而把那些较低访问量的网页缓存到磁盘上。