ABP module-zero +AdminLTE+Bootstrap Table+jQuery權(quán)限管理系統(tǒng)第十五節(jié)--緩存小結(jié)與ABP框架項目中 Redis Cache的實現(xiàn)

ABP+AdminLTE+Bootstrap Table權(quán)限管理系統(tǒng)一期
Github:https://github.com/Jimmey-Jiang/ABP-ASP.NET-Boilerplate-Project-CMS

為什么要用緩存

為什么要用緩存呢,說緩存之前先說使用緩存的優(yōu)點。

  • 減少寄宿服務(wù)器的往返調(diào)用(round-trips)。
  • 如果緩存在客戶端或是代理,將減少對服務(wù)器的請求,減少帶寬。
  • 減少對數(shù)據(jù)庫服務(wù)器的往返調(diào)用(round-trips)。
  • 當內(nèi)容緩存在web服務(wù)器,能夠減輕對數(shù)據(jù)庫的請求。
  • 減少網(wǎng)絡(luò)帶寬。
  • 避免了重新生成可重用內(nèi)容的時耗。
  • 提高性能
  • 因為緩存減少了round-trips, network traffic(網(wǎng)絡(luò)帶寬),并避免- 了生成可重用內(nèi)容的時耗,所以對性能有巨大的提高。

傳統(tǒng)的緩存方式

傳統(tǒng)的緩存方式如下面這張圖


image.png

之前我們處理方式處理起來也很簡單

  1. 頁面輸出緩存,直接在 ASP.NET中頁面緩存的使用OutputCache 在aspx頁的頂部加這樣一句即可:
    <%@ OutputCache Duration="60" VaryByParam="none" %>
    Duration 表示緩存的時間秒,必選,否則報錯。
  2. 第二種方式
    if (this.Cache["Keys"] == null) { this.Cache.Insert("Keys", List, null, DateTime.Now.AddHours(2), TimeSpan.Zero); }

這里是檢查緩存中Keys是否存在,如果不存在,則寫入一個新的值List.還有其他的一些使用方法。


image.png

上面兩種方式顯然不在現(xiàn)在使用范疇,也不在我想說的范疇之內(nèi)。,年代貌似有點久遠,不用webform基本用不到?,F(xiàn)在我們更多是的使用MVC。
我們想說的是MVC輸出緩存。

MVC緩存

輸出緩存Outputcache ,分為Action輸出緩存和Controller輸出緩存。使用的場景包括某個頁面的數(shù)據(jù)更新不是很頻繁,不需要每次都從數(shù)據(jù)庫區(qū)查詢。緩存起來從內(nèi)存中讀取。

數(shù)據(jù)緩存:是相對于全局的。任何地方需要調(diào)用的時候都可以去調(diào)用。使用的場景包括權(quán)限管理這種模塊的。每個角色對于菜單的訪問都是固定的,所以有必要將角色,權(quán)限,菜單這種數(shù)據(jù)做一個全局的數(shù)據(jù)緩存。修改時再做緩存的更新。

輸出緩存和數(shù)據(jù)緩存區(qū)別:打個比方輸出緩存就像是“局部變量”,數(shù)據(jù)緩存就像是全局變量(只是個比喻)。

Controller輸出緩存和 Action緩存使用方式是一樣的,就是Controller 或Action上打[OutPutCache]特性標簽。但是他們之間又是有區(qū)別的。

一、控制器緩存

Control緩存的作用域是整個控制器,所以在這個控制器下的所有Action都會被緩存起來。Control緩存的粒度比較粗,應(yīng)用也比較少些。
[OutputCache(Duration = 10)] public class HomeController : Controller { public ActionResult Index() { ViewBag.CurrentTime = DateTime.Now; return View(); } }
二、Action緩存
將[OutPutCache]特性標簽打在Action上,這樣,只有加緩存的Action才會有緩存,其他的Action是沒有的。

Outputcache特性常用的屬性參數(shù)

名稱 描述
AllowMultiple 獲取或設(shè)置一個值,該值指示是否可指定篩選器特性的多個實例。
CacheProfile 獲取或設(shè)置緩存配置文件名稱。
ChildActionCache 獲取或設(shè)置子操作緩存。
Duration 獲取或設(shè)置緩存持續(xù)時間(以秒為單位)。
Location 獲取或設(shè)置位置。
NoStore 獲取或設(shè)置一個值,該值指示是否存儲緩存。
Order 獲取或者設(shè)置執(zhí)行操作篩選器的順序。
SqlDependency 獲取或設(shè)置 SQL 依賴項。
TypeId (從Attribute繼承。)
VaryByContentEncoding 獲取或設(shè)置基于內(nèi)容變化的編碼。
VaryByCustom 獲取或設(shè)置基于自定義項變化的值。
VaryByHeader 獲取或設(shè)置基于標頭變化的值。
VaryByParam 獲取或設(shè)置基于參數(shù)變化的值。

輸出緩存CacheProfile使用配置文件設(shè)置緩存

舉例其中的CacheProfile,這種方式便于統(tǒng)一配置,當然也可以設(shè)置參數(shù)duration、location 、varybyparam等。我們需要在system.web 節(jié)點下加入這些

<caching> <outputCacheSettings> <outputCacheProfiles> <add name="TestConfigCache" duration="20" location="Any" enabled="true"/> </outputCacheProfiles> </outputCacheSettings> </caching>
其實作用和效果還是一樣,無非就是方便點,統(tǒng)一的配置參數(shù)都直接寫webconfig文件里面。其實也可以Controller中寫。
配置好了之后我們直接在控制器調(diào)用相應(yīng)的名字的OutputCache特性標簽即可。
[OutputCache(CacheProfile= "TestConfigCache")] public ActionResult Index() { ViewBag.CurrentTime = DateTime.Now; return View(); }
更多的方式,需要下去再研究下。

ABP中使用ICacheManager進行緩存管理

ABP中有兩種cache的實現(xiàn)方式:MemroyCacheRedisCache,兩者都繼承至ICache接口(準確說是CacheBase抽象類)。ABP核心模塊封裝了MemroyCache來實現(xiàn)ABP中的默認緩存功能。 Abp.RedisCache這個模塊封裝RedisCache來實現(xiàn)緩存(通過StackExchange.Redis這個類庫訪問redis)。

ABP給出了一個抽象緩存基類。并在內(nèi)部使用了該抽象基類。使用 MemoryCache 來實現(xiàn)了該抽象基類。它能夠被任何其它的緩存類來擴展。Abp.RedisCache 包就擴展了該緩存基類。
ABP對外提供了一個緩存接口ICacheMananger。我們通過構(gòu)造函數(shù)注入這個接口來獲取緩存。示例如下:

image.png

在這個示例中,我們注入了 ICacheManager接口,s并且獲取了一個名稱為ControllerCache的緩存。首先我們先對ControllerCache進行清除,然后存入緩存,緩存的名字是大小寫敏感的,那就是"ControllerCache"和"CONTROLLERCACHE"取得的緩存內(nèi)容是不同的。

注意:GetCache方法 千萬不要在你的構(gòu)造函數(shù)中使用GetCache方法。如果類不是一個單例對象那么該緩存可能會被dispose掉。

ICache

ICacheManager.GetCache方法返回了一個ICache對象。每一個緩存都是基于名稱單例存在的。只有首次訪問時才會被創(chuàng)建,以后你每次用相同的名稱去獲取的緩存都是相同的。所以我們可以在不同的類中使用相同的名稱來共享相同的緩存。

在示例代碼中,我們簡單的使用了ICache.Get方法,它有兩個參數(shù):

  • key : 要獲取的緩存項的唯一標識符
  • factory:如果根據(jù)給定的key獲取到的緩存項為空,那么factory將會創(chuàng)建一個標識符為key的緩存,并且返回該緩存

ICache接口還有其它方法,如前面Clear(),Get(),GetOrDefault,SetRemoveClear。當然也有這些方法的異步(async)版本。如下圖,我就懶得寫了。

image.png

ITypedCache

ICache 接口用key(字符串類型)來獲取緩存value(object類型)。ITypedCacheICahe提供了一個 類型安全 的包裝;為了使類型安全轉(zhuǎn)換(ICacheITypedCache),我們可以用擴展方法 AsTyped,而不需要寫其它強制類型轉(zhuǎn)換的代碼,如下所示:
ITypedCache<int, Item> myCache = _cacheManager.GetCache("MyCache").AsTyped<int, Item>();

Configuration

緩存的過期時間默認是60分鐘。它是變化的。如果你在60分鐘內(nèi)沒有使用該緩存,該緩存會被自動的移除。如果你想改變所有的緩存或者指定的緩存來的默認過期時間,你可以這樣做,實現(xiàn)如下:
//對所有緩存的配置 Configuration.Caching.ConfigureAll(cache => { cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2); });
//對指定緩存的配置 Configuration.Caching.Configure("MyCache", cache => { cache.DefaultSlidingExpireTime = TimeSpan.FromHours(8); });

這段代碼你應(yīng)該放在模塊(module)的 PreInitialize 方法中。如上所示:MyCache將會在8小時后過期,而其他的緩存將在2小時后過期。

這些配置將會在首次創(chuàng)建緩存的時候生效。配置不僅僅局限于DefaultSlidingExpireTime,你可以利用ICache接口中的屬性獲取方法來自由的配置并且初始化它們。

Entity Caching

ABP的緩存系統(tǒng)是以通用為目的,它有一個 EntityCache 基類,如果你需要的話,這個基類可以幫助你緩存實體。使用這個基類,我們可以通過ID取得實體,并且我們通過ID來緩存實體,這樣以后就不需要頻繁的查詢數(shù)據(jù)庫去取得實體。假設(shè)我們有個Person實體,像下面一樣:
public class Person : Entity { public string Name { get; set; } public int Age { get; set; } }

并且,假設(shè)我們通過該實體的Id,需要頻繁調(diào)用取得Person實體的Name。首先,我們應(yīng)該創(chuàng)建一個類來存儲 cache items:
[AutoMapFrom(typeof(Person))] public class PersonCacheItem { public string Name { get; set; } }

我們 不應(yīng)該直接存儲實體到緩存中 因為緩存的時候需要序列化緩存對象而實體可能不能被序列化(尤其是實體的導航屬性)。這就是為什么我們定義了一個簡單的像DTO的類來存儲數(shù)據(jù)到緩存中。我們添加了 AutoMapFrom 特性,這是因為我們想使用 AutoMapper 來自動的轉(zhuǎn)換 Person 實體為 PersonCacheItem 對象。如果我們不使用 AutoMapper,那么我們應(yīng)該重寫 EntityCache 類的 MapToCacheItem 方法手動轉(zhuǎn)換/映射它。

然而這不是必須的,我們可能想定義一個接口為緩存類:
public interface IPersonCache : IEntityCache<PersonCacheItem> { }
最后,我們可以創(chuàng)建緩存類來緩存Person實體:
public class PersonCache : EntityCache<Person, PersonCacheItem>, IPersonCache, ITransientDependency { public PersonCache(ICacheManager cacheManager, IRepository<Person> repository) : base(cacheManager, repository) { } }

這樣就OK了,我們的person緩存已經(jīng)準備好可以使用了。緩存類可以使瞬時(如同這個例子)或者是單例。這不是說緩存數(shù)據(jù)是瞬態(tài)的。在你的應(yīng)用程序中它一直是全局緩存并且是線程安全的。

現(xiàn)在,無論在什么地方我們需要取得Person的Name,我們可以通過Person的Id從緩存中取得它。如下所示:
public class MyPersonService : ITransientDependency { private readonly IPersonCache _personCache; public MyPersonService(IPersonCache personCache) { _personCache = personCache; } public string GetPersonNameById(int id) { return _personCache[id].Name; //alternative: _personCache.Get(id).Name; } }
我們很容易的注入 IPersonCache 接口,通過該接口取得緩存項和Name屬性。

那么EntityCache是怎么工作的?

  • 在首次調(diào)用的時候我們通過倉儲從數(shù)據(jù)庫中取得實體。那么隨后的調(diào)用都是從緩存中取得。
  • 如果實體被更新或者刪除,它會自動的無效實體。因此,它會在下次調(diào)用的時候重新從數(shù)據(jù)庫中檢索數(shù)據(jù)。
  • 使用 IObjectMapper 接口來映射實體到緩存項。IObjectMapper 接口在 AutoMapper 中被實現(xiàn)。所以,如果你使用了自動映射,那么就需要 AutoMapper模塊。你可以重寫 MapToCacheItem 方法手動映射它到緩存項。
  • 使用緩存類的FullName作為緩存的Name,你可以通過傳入的緩存名到基類的構(gòu)造函數(shù)來改變它。
  • 它是線程安全的。
    如果你有更復雜的緩存需求,那么你需要擴展 EntityCache 類或者創(chuàng)建你自己的解決方案。

Redis Cache 集成

Redis是什么,Redis是一個開源的使用ANSI C語言編寫、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫,并提供多種語言的API。它可以用作數(shù)據(jù)庫、緩存和消息中間件。它支持多種類型的數(shù)據(jù)結(jié)構(gòu),如字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)與范圍查詢、bitmaps、hyperloglogs和地理空間(geospatial)索引半徑查詢。
Redis 是完全開源免費的,遵守BSD協(xié)議,是一個高性能的key-value數(shù)據(jù)庫。
Redis 與其他 key - value 緩存產(chǎn)品有以下三個特點

  • Redis支持數(shù)據(jù)的持久化,可以將內(nèi)存中的數(shù)據(jù)保存在磁盤中,重啟的時候可以再次加載進行使用。
  • Redis不僅僅支持簡單的key-value類型的數(shù)據(jù),同時還提供list,set,zset,hash等數(shù)據(jù)結(jié)構(gòu)的存儲。
  • Redis支持數(shù)據(jù)的備份,即master-slave模式的數(shù)據(jù)備份。
    Redis 優(yōu)勢
  • 性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
  • 豐富的數(shù)據(jù)類型 – Redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數(shù)據(jù)類型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功執(zhí)行要么失敗完全不執(zhí)行。單個操作是原子性的。多個操作也支持事務(wù),即原子性,通過MULTI和EXEC指令包起來。
  • 豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性。
  • 多實用工具 - Redis是一個多實用工具,可用于多種用例,如:緩存,消息隊列(Redis本地支持發(fā)布/訂閱),應(yīng)用程序中的任何短期數(shù)據(jù),例如,web應(yīng)用程序中的會話,網(wǎng)頁命中計數(shù)等。

(1)首先,我們前往https://github.com/dmajkic/redis/downloads下載安裝包,直接下一步下一步就可以了。
然后打開安裝的地址就可以看到如下的文件:

image.png

(2)然后啟動Redis服務(wù),我們cmd到安裝目錄下,然后輸入命令redis-server.exe redis.windows.conf
就會看到下面的畫面證明我們啟動服務(wù)成功。
image.png

abp默認Cache Mananger是使用in-memory來緩存。所以,這可能會成為一個問題,如果有多個并發(fā)的Web服務(wù)運行在同一個應(yīng)用中。在這種情況下,你可能想要一個分布式/中央緩存服務(wù)器。那么,你可以使用Redis來作為你的緩存服務(wù)。
首先,你需要安裝
Abp.RedisCachenuget package 到你的項目中(你可以安裝它到你的Web項目)。這里我遇到一個錯誤。
image.png

開始的時候我搞了半天不知道為什么會出現(xiàn)這個莫名其妙的錯誤,后來才發(fā)現(xiàn),原來我引入Abp.RedisCache版本和abp版本不一致。才導致的這個錯誤,比如你abp是3.1.1,那么你的Abp.RedisCache最好也是對應(yīng)的版本,最好的話把abp和Abp.RedisCache都升級到最新版本,就不會有錯誤了。
然后我們看看Abp.Runtime.Caching.Redis;依賴項以及之間的關(guān)系。
image.png

然后在ABPCMSWebModule配置一下。
image.png

ABPCMSApplicationModule中引入。
image.png

Web.config中配置
image.png

你也可以添加配置到appSettings來設(shè)置Redis數(shù)據(jù)庫的Id。如:
<add key="Abp.Redis.Cache.DatabaseId" value="2"/>
在同一個服務(wù)器上使用不同的數(shù)據(jù)庫Id是非常有用的這可以創(chuàng)建不同的Key Spaces(隔離緩存)。
UseRedis有一個重載方法,你可以通過這個方法來傳入配置參數(shù),這可以覆蓋掉配置文件中的配置。關(guān)于Redis的其他配置可以查看Redis文檔。

在下面UserList打下斷點調(diào)試進去。

image.png

看到效果如下圖,證明我們AbpRedisCache引入成功。
image.png

當然為了更好的進行可視化操作,我建議使用跨平臺開源Redis DB管理工具(Redis Desktop Manager)地址:https://redisdesktop.com/download
下載下來直接下一步下一步安裝即可。
image.png

然后運行項目,然后我們在看下Redis Desktop Manager工具,效果如下圖:
image.png

使用可視化工具很方便

  1. 新建連接,輸入redis主機host,端口號port,再起個生動形象,簡明達意的別名。
  2. 該工具支持根據(jù)篩選條件查詢key,add new key,reload等。
  3. 支持常用redis操作,針對目標key執(zhí)行rename,delete,addrow,reload value操作。
  4. 命令控制臺操作 !大家感興趣可以自己玩一下。
    另外關(guān)于實體修改后自動更新緩存的實現(xiàn)遠離可以參考
    http://www.cnblogs.com/loyldg/p/using-redis-in-abp-2.html
    這個文章。

Github項目地址:https://github.com/Jimmey-Jiang/ABP-ASP.NET-Boilerplate-Project-CMS

ABP+AdminLTE+Bootstrap Table權(quán)限管理系統(tǒng)一期
[Github:https://github.com/Jimmey-Jiang/ABP-ASP.NET-Boilerplate-Project-CMS](https://github.com/Jimmey-Jiang/ABP-

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容