尽管缓存管理在Windows应用程序中已经不再是个问题,但在web环境下依然是个挑战。因为HTTP是一个无状态的协议并且web服务无法识别不同请求的用户。识别不同的请求究竟是哪个特定用户发出的,并且存储这些信息以便它在以后请求中能被重新使用,对我们来说非常重要。ASP.NET提供了很多特性用来在客户端和服务器端存储这些数据,但是有时我们会对“我们什么时候使用它们(哪个)”感到疑惑。在ASP.NET中,我们会遇到像Session,Application以及Cache这些对象,为了有效地在web应用中有效地使用它们,理解他们之间的不同对我们来说非常重要。

背景

在这篇文章中,我将谈到在ASP.NET中不同的缓存管理方法。在web应用中,有时需要在服务端存储数据以避免从数据库检索数据和数据格式化逻辑所需的开销来提高性能,同时在接下来的请求中我们可以跨用户、跨应用、跨机器地重用同样的数据。所以,为了实现这个目的我们需要在服务端缓存数据。

缓存帮我们在3个方面实现了提高服务质量

"codetitle">复制代码 代码如下:
string empNum = Request.QueryString["empnum"];
if (empNum != null)
{
    string details = null;

    if (Session["EMP_DETAILS"] == null)
    {
        //Get Employee Details for employee number passed
        details = GetEmployeeDetails(Convert.ToInt32(empNum));

        Session["EMP_DETAILS"] = details;
    }
    else
    {
        details = Session["EMP_DETAILS"];
    }

    //send it to the browser
    Response.Write(details);
}

ASP.NET application object

 ASP.NET提供了一个叫Application的对象用来存储所有用户都可以访问的数据。这个对象的生命周期与应用程序的生命周期一样,当应用程序启动时这个对象会被重新创建。与Session对象不同,Application对象可以被所有用户请求,因为这个对象是在应用程序域中创建和管理的,因而它也是不能在Web网站群中使用的。Application对象非常适合存储应用程序元数据(Config file data),这种数据可以被装载到Application对象中并且在整个应用程序周期中每个用户请求都可以访问其中的对象而不用重新装载。但是如果有这样的需求:在应用程序运行中无论什么时候对Config文件做了修改缓存数据必需失效,这时Application方式就不能提供这样的支持了。在这种情况下,就要考虑cache对象了,下面介绍cache对象的使用。

ASP.NET cache object

ASP.NET cache object是我最喜欢的缓存机制,这是为什么我在这里要多说一些的原因。ASP.NET提供了一个键-值对(key-value pair)对象--cache对象,它可以在system.web.caching名称空间中得到。它的范围是应用程序域,生命周期和应用程序生命周期一致。与Session对象不同,它是可以被不同用户来访问的。

尽管Application和Cache对象非常相似,主要区别在于Cache对象有拥有更多的特性,像过期策略、缓存依赖。它意味着数据存储在缓存对象可以根据预定义时间或它依赖的实体变化时过期或清楚,而这个特性Application对象是不支持的。

让我们来讨论下它支持的过期策略和缓存的依赖吧。

依赖

依赖意味着缓存的对象会被清除当依赖的实体发生变化时。所以可以定义一个依赖关系当依赖的对象发生变化时清除对应缓存对象。ASP.NET支持了两种依赖对象。

"codetitle">复制代码 代码如下:
object errorData;
 //Load errorData from errors.xml
 CacheDependency fileDependency =
     new CacheDependency(Server.MapPath("errors.xml"));
 Cache.Insert("ERROR_INFO", errorData, fileDependency);

"codetitle">复制代码 代码如下:
string[] relatedKeys = new string[1];
relatedKeys[0] = "EMP_NUM";
CacheDependency keyDependency = new CacheDependency(null, relatedKeys);
Cache["EMP_NUM"] = 5435;
Cache.Insert("EMP_NAME", "Shubhabrata", keyDependency);
Cache.Insert("EMP_ADDR", "Bhubaneswar", keyDependency);
Cache.Insert("EMP_SAL", "5555USD", keyDependency);

过期策略(Expiration Policy)

过期策略定义了如何以及何时让缓存的对象过期的。

"codetitle">复制代码 代码如下:
//Absolute Expiration
Cache.Insert("EMP_NAME", "Shubhabrata", null,
             DateTime.Now.AddDays(1), Cache.NoSlidingExpiration);

//Sliding Expiration
Cache.Insert("EMP_NAME", "Shubhabrata", null,
             Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(60));

怎样知道一个缓存对象被清除了?

上面的例子描述了如何清除缓存对象,但有时我们需要知道什么时候对象从缓存中清除。可以,我们通过使用回调来实现。在上面错误信息的例子中,无论任何时候error.xml发生变化时,缓存的对象就会被清除。假设我们想要更新缓存与最新的错误消息。何时从缓存中清除对象,我们可以使用回调(Callback)来做进一步处理(重新加载对象到缓存中)。

下面例子显示了如何在缓存过期时使用回调的场景。

复制代码 代码如下:
private void AddItemsToCache()
{   
    int empNum = 5435;
    CacheItemRemovedCallback onEmpDetailsRemove =
                    new CacheItemRemovedCallback(EmpDetailsRemoved);
    Cache.Insert("EMP_NUM", empNum, null,
                              Cache.NoAbsoluteExpiration,
                              Cache.NoSlidingExpiration,
                              CacheItemPriority.Default,
                              onEmpDetailsRemove);
}

private void EmpDetailsRemoved(string key, object val,
                              CacheItemRemovedReason reason)
{
    //When the item is expired
    if (reason == CacheItemRemovedReason.Expired)
    {
        //Again add it to the Cache
        AddItemsToCache();<BR>    }
}

在上面的例子中,你必须注意CacheItemPriority这个参数,它和Callback参数一起使用。CacheItemPriority用来设置增加到缓存中的对象的优先级。这个优先权告诉Cache当内存一旦很低时,这个优先级会指示对象的释放顺序。这个过程被称为清除(scavenging)。

.NET Remoting

你也许会想.NET remoting如何用于数据缓存?当我第一次听到这个问题时,这个问题就进到了我的脑海中。正如你所知道的.NET Remoting通过单例把对象共享给各个客户端,所以使用单例的对象可以用来缓存数据以共享数据给各个不同的客户端。因为.NET Remoting可以运行在进程和机器之外,当我们想要缓存对象并且跨服务、跨用户、尤其是用在网站群时,这个特性非常有用。这种方法我们可以将数据缓存到单例对象的数据成员里并且提供方法去读取和存储数据。当我们实现这种方法时,我们必须确保缓存的remoting对象不被垃圾回收器清除了。因而我们必须设置Remoting对象的缓存永不过期以至永远不会超时。我们可以重写InitializeLifetimeService和MarshalByRefObject方法使它们返回Null。但是这样做的主要问题是性能,通过分析使用这种方法比其它方法的性能都差。不管怎样,应该由设计师或开发者根据具体需求选择出最合适的方法。

内存映射文件(Memory-Mapping files)

大家都知道内存映射文件是什么,它基于映射到物理磁盘上的文件到应用程序存储空间的一个特定的地址范围。这种方式允许不同的进程使用相同的数据从而增加应用程序的性能。因为使用内存映射文件在ASP.NET应用中并不流行,我个人也不建议使用这种方法因为它增加了程序的复杂性,并且.NET Framework也不支持这样。但是如果有人喜欢使用这种方法的话,他必须为他们的需求开发出自定义的解决方案。

静态变量(Static variables)

我们可以使用静态变量来存储全局的数据或对象,以便在整个应用程序生命周期来访问它。同样地,我们也可以使用静态对象来缓存数据,并且可以提供方法来从缓存中检索和存储数据。因为静态对象存储在进程中,性能非常快。但是用静态变量实现过期策略和缓存依赖是非常复杂的,我还是比较喜欢使用Cache相比用静态变量。另一个问题是用户自定义缓存对象必须是线程安全的,所以实现它必须特别小心。

自定义静态缓存可以用下面方法实现:

复制代码 代码如下:
public class CustomCache
{
    //Synchronized to implement thread-safe
    static Hashtable _myCache =
             Hashtable.Synchronized(new Hashtable());

    public static object GetData(object key)
    {
        return _myCache[key];
    }

    public static void SetData(object key, object val)
    {
        _myCache[key] = val;
    }
}

数据库

我们可以使用数据库来存储数据来实现跨用户、跨机器的数据共享。当我们想要缓存非常大的数据对象时,这是一种非常好的方式。使用这种方式来存储小的数据是得不偿失的(性能低),用于存储少量数据可以寻找其它进程内的缓存机制。存储到数据库中的缓存数据需要经过序列化成XML来方便存储和检索,在.NET Framework中我们也可以使用其它类型的序列化格式。

页面输出缓存(ASP.NET page output caching)

有时,我们的web应用程序在一定的时间范围内对于某些页面来说是不会变化的,例如HR站点中,员工工资信息不会频繁地变动,它们在一个月一般只变动一次。一般来说都是在一个月的第一天发生变化。所以,对特定的员工来说,一个月中这个员工的页面内容是不会变化的。所以,把这些页面在服务器上缓存起来以避免每次请求重新计算的过程,这真是个不错的主意。为了达到这个目的,.NET为我们提供了在服务端指定特定时间缓存输出页面的特性;它也提供了缓存页面片段的特性。在这儿我不再详细去描述这种缓存方法了,因为网络上有很多关于这方面的详细介绍。这是一个非常长的部分如果我们现在讨论它,我计划在其它章节去讨论它。

复制代码 代码如下:
<!-- VaryByParm - different versions of same page will be
cached based on the parameter sent through HTTP Get/Post
Location - where the page is cached -->
<%@OutputCache Duration="60" VaryByParam="empNum"
                                       Location="Server"%>

我们来对比一下我们所讨论的这些缓存:

方法  是否支持网站群? 备注

ASP.NET Session State
- InProc
- StateSerer
- SQLServer


No
Yes
Yes

Unlike other option, it stores only user session specific data ASP.NET Application Object No   ASP.NET Cache Object No   .NET Remoting Yes   Memory-Mapped files No   Static Variables No   Database Yes   ASP.NET Page Output Caching No  

客户端缓存管理

在上面章节中我们讨论了在服务端的不通缓存方式,但有时我们希望能在客户端缓存数据和页面以提高性能。使用客户端缓存可以降低服务端的负载压力,但这种缓存机制却存在安全问题因为数据是存储在客户端。在客户端缓存也有不同的方式,我将简单地谈到几种。

Cookies

Cookie对web开发人员中是非常熟悉的概念,Cookie存储在客户端,当客户端每次发送请求时都会将它发送到服务端,服务端响应时也会把它发回到客户端。因为它限制了字节数(4096个字节),所以它只能缓存比较小的数据。它可以使用过期策略使它在一段特定的时间之后失效。下面的例子显示了在ASP.NET中如何使用Cookie。

复制代码 代码如下:
if (this.Request.Cookies["MY_NAME"] == null)
{
    this.Response.Cookies.Add(new HttpCookie("MY_NAME",
                                       "Shubhabrata Mohanty"));
}
else
{
    this.Response.Write(this.Request.Cookies["MY_NAME"].Value);
}

ViewState

.NET ViewState是一个新的概念。和页面相关的数据和控件都是存储在ViewState,这些保留值可以跨多个请求道服务器。如果你还记得,在VB-ASP应用开发中跨多个请求存储数据是通过Hidden控件的。事实上ViewState在ASP.NET是隐藏控件的内部实现,但对比隐藏控件它做了散列化以增加安全性。去看ViewState是如何实现的,你可以打开页面查看源代码。ViewState也不能存储大量数据因为它每个请求都会发送到服务端。

复制代码 代码如下:
protected void Page_Load(object sender, EventArgs e)
{
    if (this.ViewState["MY_NAME"] == null)
    {
        this.ViewState["MY_NAME"] = "Shubhabrata Mohanty";
    }

    //txtName is a TextBox control
    this.txtName.Text = this.ViewState["MY_NAME"].ToString();
}

Hidden fields

Hidden field在VB-ASP Web开发中非常流行。Hidden fields和其它控件的使用非常相似,但它在输出页面上是看不到的。和ViewState一样它也不能存储大量数据。注:隐藏框架(Hidden frames)可以在客户端缓存数据,但不是所有浏览器都支持隐藏框架。

复制代码 代码如下:
<!--In ASP.NET-->
<asp:HiddenField ID="myHiddenField" Value="Shubhabrata"
                                             runat="server" />
<!--In HTML-->
<input id="myHiddenField" type="hidden" value="Shubhabrata" />

微软IE浏览器缓存

因为我们是在谈论微软的ASP.NET,为什么不讨论一下微软的另外一种缓存能力呢?微软的IE浏览器提供了另一种机制在客户端缓存页面,这可以使用EXPIRES设置指令添加到HTML页面或在IIS中手动设置。到IIS中的HTTP标签属性窗口,然后选择使内容过期复选框。我们可以使用这个设置在客户端缓存静态网页和图片。

 

标签:
缓存

免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
狼山资源网 Copyright www.pvsay.com

评论“ASP.NET缓存管理的几种方法”

暂无“ASP.NET缓存管理的几种方法”评论...