<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      代碼級淺析企業庫緩存組件

             事情的初衷很簡單,就是想不用xml配置來使用其緩存組件,試了很多遍都無法成功.不得已安裝了其源碼大略分析一遍,才總算成功.后來又一想,既然分析就分析的徹底一點吧,順便看看國外的高手們是怎么架構組件,書寫代碼的,于是就有了這篇文章.企業庫為5.0版本.

            首先是類關系圖:

       

       

            緩存組件的整體結構為CacheManager -> Cache -> CacheItem,其中CacheItem為緩存項,其有Key有Value,還有本緩存項的過期策略及刪除時的回調函數.Cache為緩存,除管理CacheItem外,還負責管理緩存性能計算器及緩存持久化.CacheManager為Cache類的包裝類,用戶調用接口,也是最為我們熟悉的,其代理了Cache類的緩存操作方法,此外還有過期輪詢等.下面就來一步一步的分析.

            一.緩存創建


       

             常見的緩存創建方式為:

      ICacheManager manager = Microsoft.Practices.EnterpriseLibrary.Caching.CacheFactory.GetCacheManager();

            其實還有一種創建方式:

      CacheManagerFactory factory = new CacheManagerFactory();
      ICacheManager manager = factory.CreateDefault();

            這兩種方式創建緩存,本質上調用的都是這段代碼:

      EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>(cacheManagerName)

            EnterpriseLibraryContainer對象,又稱企業庫容器對象,說白了就是個依賴注入的容器,封裝了unity框架.更具體的說明,請參見我另寫的一篇文章:代碼級淺析企業庫對象創建

            這段代碼的意思,就是返回一個注冊了的實現了ICacheManager接口的類.這里實際返回的是CacheManager類.

       

            二.緩存輪詢

       

            緩存配置中有兩個參數用在了這里:numberToRemoveWhenScavenging和maximumElementsInCacheBeforeScavenging.參數的名字已經把他們的用途說的很明白了:緩存里存儲了多少項數據后啟動清理及每次移除多少項數據.

       

      1 public void Add(string key, object value, CacheItemPriority scavengingPriority, ICacheItemRefreshAction refreshAction, params ICacheItemExpiration[] expirations)
      2 {
      3     realCache.Add(key, value, scavengingPriority, refreshAction, expirations);
      4     
      5     backgroundScheduler.StartScavengingIfNeeded();
      6 }

           

            代碼第六行說的很清楚,每次新增緩存項時,就會檢查緩存項是否超過了配置值.如果超過了,就會通過多線程的方式在線程池中執行以下方法

       

      internal void Scavenge()
      {
          int pendingScavengings = Interlocked.Exchange(ref scavengePending, 0);
          int timesToScavenge = ((pendingScavengings - 1) / scavengerTask.NumberOfItemsToBeScavenged) + 1;
          while (timesToScavenge > 0)
          {
              scavengerTask.DoScavenging();
              --timesToScavenge;
          }
      }

            然后又調用了ScavengerTask類的DoScavenging方法

       1 public void DoScavenging()
       2 {
       3     if (NumberOfItemsToBeScavenged == 0return;
       4 
       5     if (IsScavengingNeeded())
       6     {
       7         Hashtable liveCacheRepresentation = cacheOperations.CurrentCacheState;
       8 
       9         ResetScavengingFlagInCacheItems(liveCacheRepresentation);
      10         SortedList scavengableItems = SortItemsForScavenging(liveCacheRepresentation);
      11         RemoveScavengableItems(scavengableItems);
      12     }
      13 }


            這是實際實現功能的方法.如果緩存項多于配置值時就會執行.第9行代碼將緩存項的eligibleForScavenging字段設為true,表示可以對其做掃描移除工作.其實與這個字段相對應的EligibleForScavenging屬性并不是簡單的返回這個字段,其還考慮了緩存項的優先級,只有eligibleForScavenging為true且優先級不為最高(NotRemovable),才返回true.第10行即對緩存項做排序工作,以優先級為排序字段將緩存排序,優先級最高的排在后面,表示最后才被刪除.第11行則是真正刪除方法.在其方法體內會遍例排序之后的緩存項,如果EligibleForScavenging屬性為true則刪除,還有個變量記錄了刪除的個數.如果其等于配置值,則停止刪除.

            可以看到緩存輪詢與緩存過期無關,緩存優先級與緩存過期也沒關系.那么經過掃描后的緩存,仍然可能存在已過期項.

       

            三.緩存過期

            

            在配置文件中有一個配置與此有關:expirationPollFrequencyInSeconds,則每隔多長時間對緩存項進行一次過期檢查.

      pollTimer.StartPolling(backgroundScheduler.ExpirationTimeoutExpired);

            在緩存容器CacheManger創建時就會開始計時

      pollTimer = new Timer(callbackMethod, null, expirationPollFrequencyInMilliSeconds, expirationPollFrequencyInMilliSeconds);

            其本質是一個Timer對象,定時回調指定的函數.這里的回調函數其實是BackgroundScheduler對象的Expire方法:

       

      internal void Expire()
      {
          expirationTask.DoExpirations();
      }

            其又調用了ExpirationTask對象的DoExpirations方法:

       

      1 public void DoExpirations()
      2 {
      3     Hashtable liveCacheRepresentation = cacheOperations.CurrentCacheState;
      4     MarkAsExpired(liveCacheRepresentation);
      5     PrepareForSweep();
      6     int expiredItemsCount = SweepExpiredItemsFromCache(liveCacheRepresentation);
      7     
      8     if(expiredItemsCount > 0) instrumentationProvider.FireCacheExpired(expiredItemsCount);
      9 }

            這里是過期的實際功能方法.代碼第四行遍例緩存,將已過期的緩存的WillBeExpired屬性標記為true,第6行則是將所有WillBeExpired屬性標記為true的緩存項進行刪除.下面來看如何判斷一個緩存項是否過期.

            其實新增緩存的方法有多個重載,其中一個就是

      public void Add(string key, object value, CacheItemPriority scavengingPriority, ICacheItemRefreshAction refreshAction, params ICacheItemExpiration[] expirations)


       

            常見的有絕對時間,相對時間,文件依賴等.可以看到,一個緩存項,是可以有多個緩存依賴的,或者叫緩存過期策略.如果其中任意一個過期,則緩存項過期.

      public bool HasExpired()
      {
          foreach (ICacheItemExpiration expiration in expirations)
          {
              if (expiration.HasExpired())
              {
                  return true;
              }
          }

          return false;
      }

            對緩存項過期的管理,除定時輪詢外,在取值的時候,也會判斷.

       

            四.緩存回調

       

            如果緩存項從緩存中移除,則會觸發回調:

      RefreshActionInvoker.InvokeRefreshAction(cacheItemBeforeLock, removalReason, instrumentationProvider);

            實際上是以多線程的方式在線程池中執行回調函數

       

      public void InvokeOnThreadPoolThread()
      {
          ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolRefreshActionInvoker));
      }

      private void ThreadPoolRefreshActionInvoker(object notUsed)
      {
          try
          {
              RefreshAction.Refresh(KeyToRefresh, RemovedData, RemovalReason);
          }
          catch (Exception e)
          {
              InstrumentationProvider.FireCacheCallbackFailed(KeyToRefresh, e);
          }
      }

       

            五.線程安全

            企業庫用了大量的代碼來實現了緩存增,刪,取值的線程安全.它用了兩個鎖來實現線程安全.新增操作最復雜,就分析它吧

       1 public void Add(string key, object value, CacheItemPriority scavengingPriority, ICacheItemRefreshAction refreshAction, params ICacheItemExpiration[] expirations)
       2 {
       3     ValidateKey(key);
       4 
       5     CacheItem cacheItemBeforeLock = null;
       6     bool lockWasSuccessful = false;
       7 
       8     do
       9     {
      10         lock (inMemoryCache.SyncRoot)
      11         {
      12             if (inMemoryCache.Contains(key) == false)
      13             {
      14                 cacheItemBeforeLock = new CacheItem(key, addInProgressFlag, CacheItemPriority.NotRemovable, null);
      15                 inMemoryCache[key] = cacheItemBeforeLock;
      16             }
      17             else
      18             {
      19                 cacheItemBeforeLock = (CacheItem)inMemoryCache[key];
      20             }
      21 
      22             lockWasSuccessful = Monitor.TryEnter(cacheItemBeforeLock);
      23         }
      24 
      25         if (lockWasSuccessful == false)
      26         {
      27             Thread.Sleep(0);
      28         }
      29     } while (lockWasSuccessful == false);
      30 
      31     try
      32     {
      33         cacheItemBeforeLock.TouchedByUserAction(true);
      34 
      35         CacheItem newCacheItem = new CacheItem(key, value, scavengingPriority, refreshAction, expirations);
      36         try
      37         {
      38             backingStore.Add(newCacheItem);
      39             cacheItemBeforeLock.Replace(value, refreshAction, scavengingPriority, expirations);
      40             inMemoryCache[key] = cacheItemBeforeLock;
      41         }
      42         catch
      43         {
      44             backingStore.Remove(key);
      45             inMemoryCache.Remove(key);
      46             throw;
      47         }
      48         instrumentationProvider.FireCacheUpdated(1, inMemoryCache.Count);
      49     }
      50     finally
      51     {
      52         Monitor.Exit(cacheItemBeforeLock);
      53     }  
      54 
      55 }

            代碼第10行首先鎖住整個緩存,然后新增一個緩存項并把他加入緩存,然后在第22行嘗試鎖住緩存項并釋放緩存鎖.如果沒有成功鎖上緩存項,則重復以上動作.在代碼14與15行可以看到,這時加入緩存的緩存項并沒有存儲實際的值.他相當于一個占位符,表示這個位置即將有值.如果成功鎖住了緩存項,代碼第39號則是以覆蓋的方式將真正的值寫入緩存.

            這里為什么要用兩個鎖呢?我覺得這是考慮到性能.用一個鎖鎖住整個緩存完成整個操作固然沒有問題,但是如果代碼第33行或第38號耗時過多的話,會影響整個系統的性能,特別是第38行,涉及IO操作,更是要避免!那為什么在第14行使用的是占位符而不是真正的存儲呢?我覺得這也是考慮到性能.這里的新增操作包括兩個含義,緩存中不存在則新增,存在則更新.這里考慮的是更新的問題.通常做法是讓緩存項指向新對象,這樣先前指向的對象就會成為垃圾對象.在高負載的應用程序里,這會產生大量的垃圾對象,影響了系統的性能.如果通過Replace的方式來操作,則可以必免這個問題,讓緩存項始終指向一個內存地址,只是更新他的內容而以.

            六.離線存儲(緩存持久化)

       

       

            通過這個功能,可以讓內存數據保存在硬盤上.在緩存初始化的時候會從硬盤上加載數據

      Hashtable initialItems = backingStore.Load();
      inMemoryCache = Hashtable.Synchronized(initialItems);

            在新增與刪除的時候,會在硬盤上做相應的操作

      backingStore.Add(newCacheItem);

       

      backingStore.Remove(key);

            在企業庫里,是通過.net的IsolatedStorageFile類來實現其功能的.每個緩存都對應一個目錄

       

      private void Initialize()
      {
          store = IsolatedStorageFile.GetUserStoreForDomain();
          if (store.GetDirectoryNames(storageAreaName).Length == 0)
          {
              // avoid creating if already exists - work around for partial trust
              store.CreateDirectory(storageAreaName);
          }
      }

            每個緩存項則是一個子目錄,緩存項里的每個對象則被序列化成單個文件

       

      return Path.Combine(storageAreaName, itemToLocate);

       

       1 public IsolatedStorageCacheItem(IsolatedStorageFile storage, string itemDirectoryRoot, IStorageEncryptionProvider encryptionProvider)
       2 {
       3     if (storage == nullthrow new ArgumentNullException("storage");
       4 
       5     int retriesLeft = MaxRetries;
       6     while (true)
       7     {
       8         // work around - attempt to write a file in the folder to determine whether delayed io
       9         // needs to be processed
      10         // since only a limited number of retries will be attempted, some extreme cases may 
      11         // still fail if file io is deferred long enough.
      12         // while it's still possible that the deferred IO is still pending when the item that failed
      13         // to be added is removed by the cleanup code, thus making the cleanup fail, 
      14         // the item should eventually be removed (by the original removal)
      15         try
      16         {
      17             storage.CreateDirectory(itemDirectoryRoot);
      18 
      19             // try to write a file
      20             // if there is a pending operation or the folder is gone, this should find the problem
      21             // before writing an actual field is attempted
      22             using (IsolatedStorageFileStream fileStream =
      23                 new IsolatedStorageFileStream(itemDirectoryRoot + @"\sanity-check.txt", FileMode.Create, FileAccess.Write, FileShare.None, storage))
      24             { }
      25             break;
      26         }
      27         catch (UnauthorizedAccessException)
      28         {
      29             // there are probably pending operations on the directory - retry if allowed
      30             if (retriesLeft-- > 0)
      31             {
      32                 Thread.Sleep(RetryDelayInMilliseconds);
      33                 continue;
      34             }
      35 
      36             throw;
      37         }
      38         catch (DirectoryNotFoundException)
      39         {
      40             // a pending deletion on the directory was processed before creating the file
      41             // but after attempting to create it - retry if allowed
      42             if (retriesLeft-- > 0)
      43             {
      44                 Thread.Sleep(RetryDelayInMilliseconds);
      45                 continue;
      46             }
      47 
      48             throw;
      49         }
      50     }
      51 
      52     keyField = new IsolatedStorageCacheItemField(storage, "Key", itemDirectoryRoot, encryptionProvider);
      53     valueField = new IsolatedStorageCacheItemField(storage, "Val", itemDirectoryRoot, encryptionProvider);
      54     scavengingPriorityField = new IsolatedStorageCacheItemField(storage, "ScPr", itemDirectoryRoot, encryptionProvider);
      55     refreshActionField = new IsolatedStorageCacheItemField(storage, "RA", itemDirectoryRoot, encryptionProvider);
      56     expirationsField = new IsolatedStorageCacheItemField(storage, "Exp", itemDirectoryRoot, encryptionProvider);
      57     lastAccessedField = new IsolatedStorageCacheItemField(storage, "LA", itemDirectoryRoot, encryptionProvider);
      58 }

       

            至于IsolatedStorageFile這個類.我查了一下,這個類在sl或wp中用的比較多.這個類更具體的信息,各位看官自行谷歌吧.

       

            七.性能記數器

       

            這個就沒什么說的了,就是將各種緩存的操作次數記錄下來,包括成功的次數與失敗的次數.CachingInstrumentationProvider類里包含了13個EnterpriseLibraryPerformanceCounter類型的計數器.這種計數器其實是系統計數器PerformanceCounter類型的封裝.這13個計數器分別為:命中/秒,總命中數,未命中/秒,總未命中數,命中比,緩存總記問數,過期數/秒,總過期數,輪詢清除/秒,總輪詢清除數,緩存項總數,更新緩存項/秒,更新緩存項總數.更加具體的信息,各位看官自行谷歌吧.

       

            至此,緩存組件的分析告一段落了.我感覺緩存組件比上一篇寫到的對象創建模塊要好的很多,代碼結構清晰,職責分明.里面涉及的眾多技術運用,如多線程,鎖,性能,面向接口編程等也較為合理,算的上是一個學習的樣本.

       

            文章的最后放上一段我最喜歡的一句話吧:

       

            “設計軟件有兩種策略,一是做的非常的簡單,以至于明顯沒有缺陷。二是做的非常的復雜,以至于沒有明顯的缺陷。” – C.A.R. Hoare
       

       

      posted @ 2011-12-19 00:50  永遠的阿哲  閱讀(2093)  評論(6)    收藏  舉報
      主站蜘蛛池模板: 中国大陆高清aⅴ毛片| 成人av一区二区三区| 国产网曝门亚洲综合在线| 中文字幕日韩有码av| 91久久精品美女高潮不断| 中文字幕有码无码AV| 22222se男人的天堂| 亚洲无线码在线一区观看| 亚洲www永久成人网站| 67194亚洲无码| 免费无码成人AV片在线| 久久日产一线二线三线| 国产真实younv在线| 精品国产一区av天美传媒| 亚洲a∨国产av综合av下载| 夜夜爽免费888视频| 成人无码特黄特黄AV片在线| 欧美人成精品网站播放| 熟妇无码熟妇毛片| 又黄又无遮挡AAAAA毛片| 日本高清在线观看WWW色| 亚洲午夜精品毛片成人播放| 正在播放国产真实哭都没用| 免费特黄夫妻生活片| 一区二区三区不卡国产| 灵山县| 国产精品久久久久无码网站| 中国女人内谢69xxxx| 99久久精品国产熟女拳交| 国产亚洲精品AA片在线播放天 | 国产午夜精品理论大片| 久久国产一区二区日韩av| 好紧好爽午夜视频| 午夜福利国产盗摄久久性| 五月天天天综合精品无码| 亚洲国产性夜夜综合| 大地资源免费视频观看| 定州市| 免费午夜无码片在线观看影院| V一区无码内射国产| 超碰人人模人人爽人人喊手机版|