[VS2010] ASP.NET 4.0 新功能:自訂輸出快取 (Output Cache) 提供者

[VS2010] ASP.NET 4.0 新功能:自訂輸出快取 (Output Cache) 提供者

輸出快取是 ASP.NET 開發以來重要的功能之一,快取機制是由伺服器的記憶體提供,將網頁或資料的一部份或全部暫存在記憶體中,以提供更快速的回應時間 (response time),並減少伺服器處理與回傳資料的負載,讓應用程式可以服務更多來自用戶端的呼叫,而且輸出快取就單純是暫存快取資料,因此在 ASP.NET 不斷的升級之時,輸出快取沒有做太多的改變,然而這段期間快取的需求卻不斷變化,同時來自大型應用程式的 Load Balancing 架構以及 Web Farm 應用程式的架構下,不同的用戶端會導向到不同的 Server,而快取資料卻只能存在單一台 Server 的記憶體,這會造成快取資料形同虛設,它只能對一小部份的用戶端有作用,沒有連接到存有快取的 Server 的用戶端就無法存取到快取的資料。這促使了微軟必須要將快取機制做修改,以能夠提供除了記憶體以外的快取儲存區,像是檔案或資料庫等,以在不同情況下的快取能夠服務到最多的用戶端,這樣才能夠真正發揮快取的作用,尤其是在大型應用程式中。

ASP.NET 4.0 的輸出快取即具備這樣的能力,ASP.NET 4.0 的 Core Service 將 Output Cache 改寫成使用 Provider Model,讓開發人員可以利用設計自己的 Provider 的方式,讓 ASP.NET 可以輸出快取資料到不同的快取儲存體,而且開發人員也可以將快取與現有具高效率的快取提供者做連接,像是 memcache 這個受歡迎的 Open Source Cache Provider。開發人員可以實作在 System.Web.Caching 命名空間中的 OutputCacheProvider 抽象類別中的各個成員,並且在 Web.config 中加掛這個類別,即可讓 ASP.NET 使用這個類別來提供快取機制。下列程式碼即是一個 OutputCacheProvider 的範例實作:

public class FileCacheProvider : OutputCacheProvider
{
    private string _cachePath;

    private string CachePath
    {
        get
        {
            if (!string.IsNullOrEmpty(_cachePath))
                return _cachePath;

            _cachePath = ConfigurationManager.AppSettings["OutputCachePath"];
            var context = HttpContext.Current;

            if (context != null)
            {
                _cachePath = context.Server.MapPath(_cachePath);
                if (!_cachePath.EndsWith("\\"))
                    _cachePath += "\\";
            }

            return _cachePath;
        }
    }
    public override object Add(string key, object entry, DateTime utcExpiry)
    {
        Debug.WriteLine("Cache.Add(" + key + ", " + entry + ", " + utcExpiry +")");

        var path = GetPathFromKey(key);

        if (File.Exists(path))
            return entry;

        using (var file = File.OpenWrite(path))
        {
            var item = new CacheItem { Expires = utcExpiry, Item = entry };
            var formatter = new BinaryFormatter();
            formatter.Serialize(file, item);
        }

        return entry;
    }

    public override object Get(string key)
    {
        Debug.WriteLine("Cache.Get(" + key + ")");

        var path = GetPathFromKey(key);

        if (!File.Exists(path))
            return null;

        CacheItem item = null;

        using (var file = File.OpenRead(path))
        {
            var formatter = new BinaryFormatter();
            item = (CacheItem)formatter.Deserialize(file);              
        }

        if (item == null || item.Expires <= DateTime.Now.ToUniversalTime())
        {
            Debug.WriteLine("Expired: " + item.Expires + " <= " + DateTime.Now.ToUniversalTime());
            Remove(key);
            return null;
        }

        return item.Item;
    }

    public override void Remove(string key)
    {
        Debug.WriteLine("Cache.Remove(" + key + ")");

        var path = GetPathFromKey(key);
        if (File.Exists(path))
            File.Delete(path);
    }

    public override void Set(string key, object entry, DateTime utcExpiry)
    {
        Debug.WriteLine("Cache.Set(" + key + ", " + entry + ", " + utcExpiry + ")");

        var item = new CacheItem { Expires = utcExpiry, Item = entry };
        var path = GetPathFromKey(key);

        using (var file = File.OpenWrite(path))
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(file, item);
        }
    }
    private string GetPathFromKey(string key)
    {
        return CachePath + MD5(key) + ".txt";
    }

    private string MD5(string s)
    {
        MD5CryptoServiceProvider provider;
        provider = new MD5CryptoServiceProvider();
        byte[] bytes = Encoding.UTF8.GetBytes(s);
        StringBuilder builder = new StringBuilder();

        bytes = provider.ComputeHash(bytes);

        foreach (byte b in bytes)
            builder.Append(b.ToString("x2").ToLower());

        return builder.ToString();
    }
}

 

然後在 Web.config 中將這個 Provider 加掛上 ASP.NET:

<caching>
  <outputCache defaultProvider="FileCache">
    <providers>
      <add name="FileCache" type="MyCacheProvider.FileCacheProvider, MyCacheProvider"/>
    </providers>
  </outputCache>
</caching>

 

如此即可在 ASP.NET 中使用自訂的快取提供者給 ASP.NET Cache 使用:

List of files that contain cache entries

 

參考資料與圖片來源:

Extensible Output Caching with ASP.NET 4 (VS 2010 and .NET 4.0 Series)

ASP.NET 4.0: Writing custom output cache providers