細說ASP.NET Cache 及其進階用法--服務端緩衝__.net

來源:互聯網
上載者:User

閱讀目錄 開始 Cache的基本用途 Cache的定義 Cache常見用法 Cache類的特點 快取項目的到期時間 快取項目的依賴關係 - 依賴其它快取項目 快取項目的依賴關係 - 檔案依賴 快取項目的移除優先順序 快取項目的移除通知 巧用快取項目的移除通知 實現【延遲操作】 巧用快取項目的移除通知 實現【自動載入設定檔】 檔案監視技術的選擇 各種緩衝方案的共存

許多做過程式效能最佳化的人,或者關注過程程式效能的人,應該都使用過各類緩衝技術。 而我今天所說的Cache是專指ASP.NET的Cache,我們可以使用HttpRuntime.Cache訪問到的那個Cache,而不是其它的緩衝技術。

以前我在【我心目中的Asp.net核心對象】 這篇部落格中簡單地提過它,今天我打算為它寫篇專題部落格,專門來談談它,因為它實在是太重要了。在這篇部落格中, 我不僅要介紹它的一些常見用法,還將介紹它的一些進階用法。 在上篇部落格【在.net中讀寫config檔案的各種方法】 的結尾處,我給大家留了一個問題,今天,我將在這篇部落格中給出一個我認為較為完美的答案。

本文提到的【延遲操作】方法(如:延遲合并寫入資料庫)屬於我的經驗總結,希望大家能喜歡這個思路。 回到頂部 Cache的基本用途

提到Cache,不得不說說它的主要功能:改善程式效能。
ASP.NET是一種動態網頁面技術,用ASP.NET技術做出來的網頁幾乎都是動態,所謂動態是指:頁面的內容會隨著不同的使用者或者持續更新的資料, 而呈現出不同的顯示結果。既然是動態,那麼這些動態內容是從哪裡來的呢。我想絕大多數網站都有自己的資料來源, 程式通過訪問資料來源擷取頁面所需的資料,然後根據一些商務規則的計算處理,最後變成適合頁面展示的內容。

由於這種動態網頁面技術通常需要從資料來源擷取資料,並經過一些計算邏輯,最終變成一些HTML代碼發給用戶端顯示。而這些計算過程顯然也是有成本的。 這些處理成本最直接可表現為影響伺服器的響應速度,尤其是當資料的處理過程變得複雜以及訪問量變大時,會變得比較明顯。 另一方面,有些資料並非時刻在發生變化,如果我們可以將一些變化不頻繁的資料的最終計算結果(包括頁面輸出)緩衝起來, 就可以非常明顯地提升程式的效能,緩衝的最常見且最重要的用途就體現在這個方面。 這也是為什麼一說到效能最佳化時,一般都將緩衝擺在第一位的原因。 我今天要說到的ASP.NET Cache也是可以實現這種緩衝的一種技術。 不過,它還有其它的一些功能,有些是其它緩衝技術所沒有的。

回到頂部 Cache的定義

在介紹Cache的用法前,我們先來看一下Cache的定義:(說明:我忽略了一些意義不大的成員) 

ASP.NET為了方便我們訪問Cache,在HttpRuntime類中加了一個靜態屬性Cache,這樣,我們就可以在任意地方使用Cache的功能。 而且,ASP.NET還給它增加了二個“捷徑”:Page.Cache, HttpContext.Cache,我們通過這二個對象也可以訪問到HttpRuntime.Cache, 注意:這三者是在訪問同一個對象。Page.Cache訪問了HttpContext.Cache,而HttpContext.Cache又直接存取HttpRuntime.Cache 回到頂部 Cache常見用法

通常,我們使用Cache時,一般只有二個操作:讀,寫。
要從Cache中擷取一個快取項目,我們可以調用Cache.Get(key)方法,要將一個對象放入緩衝,我們可以調用Add, Insert方法。 然而,Add, Insert方法都有許多參數,有時我們或許只是想簡單地放入緩衝,一切接受預設值,那麼還可以調用它的預設索引器, 我們來看一下這個索引器是如何工作的:

public object this[string key]{    get    {        return this.Get(key);    }    set    {        this.Insert(key, value);    }}

可以看到:讀緩衝,其實是在調用Get方法,而寫緩衝則是在調用Insert方法的最簡單的那個重載版本。

注意了:Add方法也可以將一個對象放入緩衝,這個方法有7個參數,而Insert也有一個簽名類似的重載版本, 它們有著類似的功能:將指定項添加到 System.Web.Caching.Cache 對象,該對象具有依賴項、到期和優先順序策略以及一個委託(可用於在從 Cache 移除插入項時通知應用程式)。 然而,它們有一點小的區別:當要加入的快取項目已經在Cache中存在時,Insert將會覆蓋原有的快取項目,而Add則不會修改原有快取項目。

也就是說:如果您希望某個快取項目一旦放入緩衝後,就不要再被修改,那麼調用Add確實可以防止後來的修改操作。 而調用Insert方法,則永遠會覆蓋已存在項(哪怕以前是調用Add加入的)。

從另一個角度看,Add的效果更像是 static readonly 的行為,而Insert的效果則像 static 的行為。
注意:我只是說【像】,事實上它們比一般的static成員有著更靈活的用法。

由於快取項目可以讓我們隨時訪問,看起來確實有點static成員的味道,但它們有著更進階的特性,比如: 緩衝到期(絕對到期,滑動到期),緩衝依賴(依賴檔案,依賴其它快取項目),移除優先順序,緩衝移除前後的通知等等。 後面我將會分別介紹這四大類特性。 回到頂部 Cache類的特點

Cache類有一個很難得的優點,用MSDN上的說話就是:

此類型是安全執行緒的。

為什麼這是個難得的優點呢。因為在.net中,絕大多數類在實現時,都只是保證靜態類型的方法是安全執行緒, 而不考慮執行個體方法是安全執行緒。這也算是一條基本的.NET設計規範原則。
對於那些類型,MSDN通常會用這樣的話來描述:

此類型的公用靜態(在 Visual Basic 中為 Shared)成員是安全執行緒的。但不能保證任何執行個體成員是安全執行緒的。

所以,這就意味著我們可以在任何地方讀寫Cache都不用擔心Cache的資料在多線程環境下的資料同步問題。 多線程編程中,最複雜的問題就是資料的同步問題,而Cache已經為我們解決了這些問題。

不過我要提醒您:ASP.NET本身就是一個多線程的編程模型,所有的請求是由線程池的線程來處理的。 通常,我們在多線程環境中為瞭解決資料同步問題,一般是採用鎖來保證資料同步, 自然地,ASP.NET也不例外,它為瞭解決資料的同步問題,內部也是採用了鎖。

說到這裡,或許有些人會想:既然只一個Cache的靜態執行個體,那麼這種鎖會不會影響並發。
答案是肯定的,有鎖肯定會在一定程度上影響並發,這是沒有辦法的事情。
然而,ASP.NET在實現Cache時,會根據CPU的個數建立多個緩衝容器,盡量可能地減小衝突, 以下就是Cache建立的核心過程:

internal static CacheInternal Create(){    CacheInternal internal2;    int numSingleCaches = 0;    if( numSingleCaches == 0 ) {        uint numProcessCPUs = (uint)SystemInfo.GetNumProcessCPUs();        numSingleCaches = 1;        for( numProcessCPUs -= 1; numProcessCPUs > 0; numProcessCPUs = numProcessCPUs >> 1 ) {            numSingleCaches = numSingleCaches << 1;        }    }    CacheCommon cacheCommon = new CacheCommon();    if( numSingleCaches == 1 ) {        internal2 = new CacheSingle(cacheCommon, null, 0);    }    else {        internal2 = new CacheMultiple(cacheCommon, numSingleCaches);    }    cacheCommon.SetCacheInternal(internal2);    cacheCommon.ResetFromConfigSettings();    return internal2;}

說明:CacheInternal是個內部用的封裝類,Cache的許多操作都要由它來完成。

在上面的代碼中,numSingleCaches的計算過程很重要,如果上面代碼不容易理解,那麼請看我下面的範例程式碼: 

static void Main(){    for( uint i = 1; i <= 20; i++ )        ShowCount(i);            }static void ShowCount(uint numProcessCPUs){    int numSingleCaches = 1;    for( numProcessCPUs -= 1; numProcessCPUs > 0; numProcessCPUs = numProcessCPUs >> 1 ) {        numSingleCaches = numSingleCaches << 1;    }    Console.Write(numSingleCaches + ",");}

程式將會輸出:

1,2,4,4,8,8,8,8,16,16,16,16,16,16,16,16,32,32,32,32

CacheMultiple的建構函式如下:

internal CacheMultiple(CacheCommon cacheCommon, int numSingleCaches) : base(cacheCommon){    this._cacheIndexMask = numSingleCaches - 1;    this._caches = new CacheSingle[numSingleCaches];    for (int i = 0; i < numSingleCaches; i++)    {        this._caches[i] = new CacheSingle(cacheCommon, this, i);    }}

現在您應該明白了吧:CacheSingle其實是ASP.NET內部使用的緩衝容器,多個CPU時,它會建立多個緩衝容器。
在寫入時,它是如何定位這些容器的呢。請繼續看代碼:

internal CacheSingle GetCacheSingle(int hashCode){    hashCode = Math.Abs(hashCode);    int index = hashCode & this._cacheIndexMask;    return this._caches[index];}

說明:參數中的hashCode是直接調用我們傳的key.GetHashCode() ,GetHashCode是由Object類定義的。

所以,從這個角度看,雖然ASP.NET的Cache只有一個HttpRuntime.Cache靜態成員,但它的內部卻可能會包含多個緩衝容器, 這種設計可以在一定程度上減少並發的影響。

不管如何設計,在多線程環境下,共用一個容器,衝突是免不了的。如果您只是希望簡單的緩衝一些資料, 不需要Cache的許多進階特性,那麼,可以考慮不用Cache 。 比如:可以建立一個Dictionary或者Hashtable的靜態執行個體,它也可以完成一些基本的緩衝工作, 不過,我要提醒您:您要自己處理多線程訪問資料時的資料同步問題。
順便說一句:Hashtable.Synchronized(new Hashtable())也是一個安全執行緒的集合,如果想簡單點,可以考慮它。

接下來,我們來看一下Cache的進階特性,這些都是Dictionary或者Hashtable不能完成的。 回到頂部 快取項目的到期時間

ASP.NET支援二種快取項目的到期策略:絕對到期和滑動到期。
1. 絕對到期,這個容易理解:就是在緩衝放入Cache時,指定一個具體的時間。當時間到達指定的時間的時,快取項目自動從Cache中移除。
2. 滑動到期:某些快取項目,我們可能只希望在有使用者在訪問時,就盡量保留在緩衝中,只有當一段時間內使用者不再訪問該快取項目時,才移除它, 這樣可以最佳化記憶體的使用,因為這種策略可以保證緩衝的內容都是【很熱門】的。 作業系統的記憶體以及磁碟的緩衝不都是這樣設計的嗎。而這一非常有用的特性,Cache也為我們準備好了,只要在將快取項目放入緩衝時, 指定一個滑動到期時間就可以實現了。

以上二個選項分別對應Add, Insert方法中的DateTime absoluteExpiration, TimeSpan slidingExpiration這二個參數。
注意:這二個參數都是成對使用的,但不能同時指定它們為一個【有效】值,最多隻能一個參數值有效。 當不使用另一個參數項時,請用Cache類定義二個static readonly欄位賦值。

這二個參數比較簡單,我就不多說了,只說一句:如果都使用Noxxxxx這二個選項,那麼快取項目就一直儲存在緩衝中。(或許也會被移除)

回到頂部 快取項目的依賴關係 - 依賴其它快取項目

ASP.NET Cache有個很強大的功能,那就是緩衝依賴。一個快取項目可以依賴於另一個快取項目。 以下範例程式碼建立了二個快取項目,且它們間有依賴關係。首先請看頁面代碼: 

<body>    <p>Key1 的緩衝內容:<%= HttpRuntime.Cache["key1"] %></p>    <hr />            <form action="CacheDependencyDemo.aspx" method="post">        <input type="submit" name="SetKey1Cache" value="設定Key1的值" />        <input type="submit" name="SetKey2Cache" value="設定Key2的值" />    </form></body>

頁面後台代碼:

public partial class CacheDependencyDemo : System.Web.UI.Page{    [SubmitMethod(AutoRedirect=true)]    private void SetKey1Cache()    {        SetKey2Cache();        CacheDependency dep = new CacheDependency(null, new string

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.