ABP+AdminLTE+Bootstrap Table許可權管理系統一期 Github:https://github.com/Jimmey-Jiang/ABP-ASP.NET-Boilerplate-Project-CMS
為什麼要用快取
為什麼要用快取呢,說快取之前先說使用快取的優點。
- 減少寄宿伺服器的往返呼叫(round-trips)。
- 如果快取在客戶端或是代理,將減少對伺服器的請求,減少頻寬。
- 減少對資料庫伺服器的往返呼叫(round-trips)。
- 當內容快取在web伺服器,能夠減輕對資料庫的請求。
- 減少網路頻寬。
- 避免了重新生成可重用內容的時耗。
- 提高效能
- 因為快取減少了round-trips, network traffic(網路頻寬),並避免- 了生成可重用內容的時耗,所以對效能有巨大的提高。
傳統的快取方式
傳統的快取方式如下面這張圖
之前我們處理方式處理起來也很簡單- 頁面輸出快取,直接在 ASP.NET中頁面快取的使用OutputCache 在aspx頁的頂部加這樣一句即可:
<%@ OutputCache Duration="60" VaryByParam="none" %>
Duration 表示快取的時間秒,必選,否則報錯。 - 第二種方式
if (this.Cache["Keys"] == null) { this.Cache.Insert("Keys", List, null, DateTime.Now.AddHours(2), TimeSpan.Zero); }
這裡是檢查快取中Keys是否存在,如果不存在,則寫入一個新的值List.還有其他的一些使用方法。
上面兩種方式顯然不在現在使用範疇,也不在我想說的範疇之內。,年代貌似有點久遠,不用webform基本用不到。現在我們更多是的使用MVC。 我們想說的是MVC輸出快取。
MVC快取
輸出快取:Outputcache
,分為Action輸出快取和Controller輸出快取。使用的場景包括某個頁面的資料更新不是很頻繁,不需要每次都從資料庫區查詢。快取起來從記憶體中讀取。
資料快取:是相對於全域性的。任何地方需要呼叫的時候都可以去呼叫。使用的場景包括許可權管理這種模組的。每個角色對於選單的訪問都是固定的,所以有必要將角色,許可權,選單這種資料做一個全域性的資料快取。修改時再做快取的更新。
輸出快取和資料快取區別:打個比方輸出快取就像是“區域性變數”,資料快取就像是全域性變數(只是個比喻)。
Controller輸出快取和 Action快取使用方式是一樣的,就是Controller 或Action上打[OutPutCache]特性標籤。但是他們之間又是有區別的。
一、控制器快取
Control快取的作用域是整個控制器,所以在這個控制器下的所有Action都會被快取起來。Control快取的粒度比較粗,應用也比較少些。
[OutputCache(Duration = 10)] public class HomeController : Controller { public ActionResult Index() { ViewBag.CurrentTime = DateTime.Now; return View(); } }
二、Action快取
將[OutPutCache]特性標籤打在Action上,這樣,只有加快取的Action才會有快取,其他的Action是沒有的。
Outputcache特性常用的屬性引數
名稱 | 描述 |
---|---|
AllowMultiple | 獲取或設定一個值,該值指示是否可指定篩選器特性的多個例項。 |
CacheProfile | 獲取或設定快取配置檔名稱。 |
ChildActionCache | 獲取或設定子操作快取。 |
Duration | 獲取或設定快取持續時間(以秒為單位)。 |
Location | 獲取或設定位置。 |
NoStore | 獲取或設定一個值,該值指示是否儲存快取。 |
Order | 獲取或者設定執行操作篩選器的順序。 |
SqlDependency | 獲取或設定 SQL 依賴項。 |
TypeId | (從Attribute繼承。) |
VaryByContentEncoding | 獲取或設定基於內容變化的編碼。 |
VaryByCustom | 獲取或設定基於自定義項變化的值。 |
VaryByHeader | 獲取或設定基於標頭變化的值。 |
VaryByParam | 獲取或設定基於引數變化的值。 |
輸出快取CacheProfile使用配置檔案設定快取
舉例其中的CacheProfile,這種方式便於統一配置,當然也可以設定引數duration、location 、varybyparam等。我們需要在system.web 節點下加入這些
`
其實作用和效果還是一樣,無非就是方便點,統一的配置引數都直接寫webconfig檔案裡面。其實也可以Controller中寫。 配置好了之後我們直接在控制器呼叫相應的名字的
OutputCache特性標籤即可。
[OutputCache(CacheProfile= "TestConfigCache")]
public ActionResult Index()
{
ViewBag.CurrentTime = DateTime.Now;
return View();
}
`
更多的方式,需要下去再研究下。
ABP中使用ICacheManager進行快取管理
ABP中有兩種cache的實現方式:MemroyCache
和RedisCache
,兩者都繼承至ICache介面(準確說是CacheBase
抽象類)。ABP核心模組封裝了MemroyCache
來實現ABP中的預設快取功能。 Abp.RedisCache
這個模組封裝RedisCache
來實現快取(通過StackExchange.Redis
這個類庫訪問redis)。
ABP給出了一個抽象快取基類。並在內部使用了該抽象基類。使用 MemoryCache 來實現了該抽象基類。它能夠被任何其它的快取類來擴充套件。Abp.RedisCache 包就擴充套件了該快取基類。 ABP對外提供了一個快取介面ICacheMananger。我們通過建構函式注入這個介面來獲取快取。示例如下:
在這個示例中,我們注入了 ICacheManager介面,s並且獲取了一個名稱為ControllerCache的快取。首先我們先對ControllerCache進行清除,然後存入快取,快取的名字是大小寫敏感的,那就是"ControllerCache"和"CONTROLLERCACHE"取得的快取內容是不同的。
注意:GetCache方法 千萬不要在你的建構函式中使用GetCache方法。如果類不是一個單例物件那麼該快取可能會被dispose掉。
ICache
ICacheManager.GetCache
方法返回了一個ICache
物件。每一個快取都是基於名稱單例存在的。只有首次訪問時才會被建立,以後你每次用相同的名稱去獲取的快取都是相同的。所以我們可以在不同的類中使用相同的名稱來共享相同的快取。
在示例程式碼中,我們簡單的使用了ICache.Get
方法,它有兩個引數:
- key : 要獲取的快取項的唯一識別符號
- factory:如果根據給定的key獲取到的快取項為空,那麼factory將會建立一個識別符號為key的快取,並且返回該快取
ICache介面還有其它方法,如前面Clear()
,Get()
,GetOrDefault
,Set
,Remove
和Clear
。當然也有這些方法的非同步(async)版本。如下圖,我就懶得寫了。
ITypedCache
ICache
介面用key(字串型別)來獲取快取value(object型別)。ITypedCache
為ICahe
提供了一個 型別安全 的包裝;為了使型別安全轉換(ICache
到ITypedCache
),我們可以用擴充套件方法 AsTyped
,而不需要寫其它強制型別轉換的程式碼,如下所示:
ITypedCache<int, Item> myCache = _cacheManager.GetCache("MyCache").AsTyped<int, Item>();
Configuration
快取的過期時間預設是60分鐘。它是變化的。如果你在60分鐘內沒有使用該快取,該快取會被自動的移除。如果你想改變所有的快取或者指定的快取來的預設過期時間,你可以這樣做,實現如下:
//對所有快取的配置 Configuration.Caching.ConfigureAll(cache => { cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2); });
//對指定快取的配置 Configuration.Caching.Configure("MyCache", cache => { cache.DefaultSlidingExpireTime = TimeSpan.FromHours(8); });
這段程式碼你應該放在模組(module)的 PreInitialize
方法中。如上所示:MyCache
將會在8小時後過期,而其他的快取將在2小時後過期。
這些配置將會在首次建立快取的時候生效。配置不僅僅侷限於DefaultSlidingExpireTime
,你可以利用ICache
介面中的屬性獲取方法來自由的配置並且初始化它們。
Entity Caching
ABP的快取系統是以通用為目的,它有一個 EntityCache
基類,如果你需要的話,這個基類可以幫助你快取實體。使用這個基類,我們可以通過ID取得實體,並且我們通過ID來快取實體,這樣以後就不需要頻繁的查詢資料庫去取得實體。假設我們有個Person
實體,像下面一樣:
public class Person : Entity { public string Name { get; set; } public int Age { get; set; } }
並且,假設我們通過該實體的Id,需要頻繁呼叫取得Person實體的Name。首先,我們應該建立一個類來儲存 cache items:
[AutoMapFrom(typeof(Person))] public class PersonCacheItem { public string Name { get; set; } }
我們 不應該直接儲存實體到快取中 因為快取的時候需要序列化快取物件而實體可能不能被序列化(尤其是實體的導航屬性)。這就是為什麼我們定義了一個簡單的像DTO的類來儲存資料到快取中。我們新增了 AutoMapFrom 特性,這是因為我們想使用 AutoMapper 來自動的轉換 Person 實體為 PersonCacheItem 物件。如果我們不使用 AutoMapper,那麼我們應該重寫 EntityCache 類的 MapToCacheItem 方法手動轉換/對映它。
然而這不是必須的,我們可能想定義一個介面為快取類:
public interface IPersonCache : IEntityCache<PersonCacheItem> { }
最後,我們可以建立快取類來快取Person實體:
public class PersonCache : EntityCache<Person, PersonCacheItem>, IPersonCache, ITransientDependency { public PersonCache(ICacheManager cacheManager, IRepository<Person> repository) : base(cacheManager, repository) { } }
這樣就OK了,我們的person快取已經準備好可以使用了。快取類可以使瞬時(如同這個例子)或者是單例。這不是說快取資料是瞬態的。在你的應用程式中它一直是全域性快取並且是執行緒安全的。
現在,無論在什麼地方我們需要取得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是怎麼工作的?
- 在首次呼叫的時候我們通過倉儲從資料庫中取得實體。那麼隨後的呼叫都是從快取中取得。
- 如果實體被更新或者刪除,它會自動的無效實體。因此,它會在下次呼叫的時候重新從資料庫中檢索資料。
- 使用 IObjectMapper 介面來對映實體到快取項。IObjectMapper 介面在 AutoMapper 中被實現。所以,如果你使用了自動對映,那麼就需要 AutoMapper模組。你可以重寫 MapToCacheItem 方法手動對映它到快取項。
- 使用快取類的FullName作為快取的Name,你可以通過傳入的快取名到基類的建構函式來改變它。
- 它是執行緒安全的。 如果你有更復雜的快取需求,那麼你需要擴充套件 EntityCache 類或者建立你自己的解決方案。
Redis Cache 整合
Redis是什麼,Redis是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。它可以用作資料庫、快取和訊息中介軟體。它支援多種型別的資料結構,如字串(strings)、雜湊(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)與範圍查詢、bitmaps、hyperloglogs和地理空間(geospatial)索引半徑查詢。 Redis 是完全開源免費的,遵守BSD協議,是一個高效能的key-value資料庫。 Redis 與其他 key - value 快取產品有以下三個特點:
- Redis支援資料的持久化,可以將記憶體中的資料儲存在磁碟中,重啟的時候可以再次載入進行使用。
- Redis不僅僅支援簡單的key-value型別的資料,同時還提供list,set,zset,hash等資料結構的儲存。
- Redis支援資料的備份,即master-slave模式的資料備份。 Redis 優勢
- 效能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
- 豐富的資料型別 – Redis支援二進位制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 資料型別操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要麼成功執行要麼失敗完全不執行。單個操作是原子性的。多個操作也支援事務,即原子性,通過MULTI和EXEC指令包起來。
- 豐富的特性 – Redis還支援 publish/subscribe, 通知, key 過期等等特性。
- 多實用工具 - Redis是一個多實用工具,可用於多種用例,如:快取,訊息佇列(Redis本地支援釋出/訂閱),應用程式中的任何短期資料,例如,web應用程式中的會話,網頁命中計數等。
(1)首先,我們前往github.com/dmajkic/red…下載安裝包,直接下一步下一步就可以了。 然後開啟安裝的地址就可以看到如下的檔案:
(2)然後啟動Redis服務,我們cmd到安裝目錄下,然後輸入命令redis-server.exe redis.windows.conf
就會看到下面的畫面證明我們啟動服務成功。
abp預設Cache Mananger是使用in-memory來快取。所以,這可能會成為一個問題,如果有多個併發的Web服務執行在同一個應用中。在這種情況下,你可能想要一個分散式/中央快取伺服器。那麼,你可以使用Redis來作為你的快取服務。
首先,你需要安裝
Abp.RedisCachenuget package 到你的專案中(你可以安裝它到你的Web專案)。這裡我遇到一個錯誤。
開始的時候我搞了半天不知道為什麼會出現這個莫名其妙的錯誤,後來才發現,原來我引入Abp.RedisCache版本和abp版本不一致。才導致的這個錯誤,比如你abp是3.1.1,那麼你的Abp.RedisCache最好也是對應的版本,最好的話把abp和Abp.RedisCache都升級到最新版本,就不會有錯誤了。
然後我們看看Abp.Runtime.Caching.Redis;依賴項以及之間的關係。
然後在ABPCMSWebModule配置一下。
ABPCMSApplicationModule中引入。
Web.config中配置
你也可以新增配置到appSettings來設定Redis資料庫的Id。如:
<add key="Abp.Redis.Cache.DatabaseId" value="2"/>
在同一個伺服器上使用不同的資料庫Id是非常有用的這可以建立不同的Key Spaces(隔離快取)。
UseRedis有一個過載方法,你可以通過這個方法來傳入配置引數,這可以覆蓋掉配置檔案中的配置。關於Redis的其他配置可以檢視Redis文件。
在下面UserList打下斷點除錯進去。
看到效果如下圖,證明我們AbpRedisCache引入成功。 當然為了更好的進行視覺化操作,我建議使用跨平臺開源Redis DB管理工具(Redis Desktop Manager)地址:redisdesktop.com/download 下載下來直接下一步下一步安裝即可。 然後執行專案,然後我們在看下Redis Desktop Manager工具,效果如下圖: 使用視覺化工具很方便- 新建連線,輸入redis主機host,埠號port,再起個生動形象,簡明達意的別名。
- 該工具支援根據篩選條件查詢key,add new key,reload等。
- 支援常用redis操作,針對目標key執行rename,delete,addrow,reload value操作。
- 命令控制檯操作 !大家感興趣可以自己玩一下。 另外關於實體修改後自動更新快取的實現遠離可以參考 www.cnblogs.com/loyldg/p/us… 這個文章。
Github專案地址:github.com/Jimmey-Jian…
ABP+AdminLTE+Bootstrap Table許可權管理系統一期 [Github:https://github.com/Jimmey-Jiang/ABP-ASP.NET-Boilerplate-Project-CMS](https://github.com/Jimmey-Jiang/ABP-