ABP 源碼解析 六. 緩存


介紹

此系列文章主要是對ABP源碼進行解讀甫窟,初探作者在創(chuàng)造ABP的框架思路籽前,和使用到的設計模式進行。
通過解讀ABP源碼付燥,可以提升ABP使用方式宣谈,可以提升編碼意識,提高面向對象編程思想键科。

本篇文章主要介紹ABP框架的緩存實現(xiàn)機制闻丑。


UML

UML
  • ICache:定義一個可以存儲并通過鍵獲取項目的緩存
  • CacheBase:基類緩存,簡單實現(xiàn)ICache
  • AbpMemoryCache:使用MemoryCache 實現(xiàn) ICache
  • ICacheManager:緩存管理器應該,單例對象勋颖,跟蹤和管理緩存對象
  • CacheManagerBase:緩存管理器的基類
  • AbpMemoryCacheManager:使用MemoryCache實現(xiàn)ICacheManager
  • ICacheConfigurator:緩存配置器
  • CacheConfigurator:實現(xiàn)ICacheConfigurator
  • ICachingConfiguration:緩存系統(tǒng)配置
  • CachingConfiguration:實現(xiàn)ICachingConfiguration

源碼解析

ICache

ICache主要是定義一個緩存項目接口嗦嗡,并且提供超時時間、獲取值牙言、設置緩存等內容酸钦。
ABP系統(tǒng)默認系統(tǒng)了四個項目:應用程式設置緩存、租戶設置緩存咱枉、用戶設置緩存卑硫、多語言腳本緩存

/// <summary>
    /// Defines a cache that can be store and get items by keys.
    /// 定義一個可以存儲并通過鍵獲取項目的緩存
    /// </summary>
    public interface ICache : IDisposable
    {
        /// <summary>
        /// Unique name of the cache.
        /// 緩存唯一的名稱
        /// </summary>
        string Name { get; }

        /// <summary>
        /// Default sliding expire time of cache items.
        /// Default value: 60 minutes (1 hour).
        /// Can be changed by configuration.
        /// 默認緩存過期時間
        /// 默認值是1小時
        /// 可以通過配置改變
        /// </summary>
        TimeSpan DefaultSlidingExpireTime { get; set; }

        /// <summary>
        /// Default absolute expire time of cache items.
        /// Default value: null (not used).
        /// 默認的緩存項的絕對過期時間
        /// 默認值:null(沒有使用)
        /// </summary>
        TimeSpan? DefaultAbsoluteExpireTime { get; set; }

        /// <summary>
        /// Gets an item from the cache.
        /// This method hides cache provider failures (and logs them),
        /// uses the factory method to get the object if cache provider fails.
        /// 從緩存中獲取一個項目
        /// 這個方法隱藏了緩存提供者失敗信息(并記錄它們),如果緩存提供者失敗蚕断,則使用工廠方法來獲取對象
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="factory">Factory method to create cache item if not exists
        /// 如果不存在的話欢伏,創(chuàng)建緩存項的工廠方法
        /// </param>
        /// <returns>Cached item</returns>
        object Get(string key, Func<string, object> factory);

        /// <summary>
        /// Gets an item from the cache.
        /// This method hides cache provider failures (and logs them),
        /// uses the factory method to get the object if cache provider fails.
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="factory">Factory method to create cache item if not exists</param>
        /// <returns>Cached item</returns>
        Task<object> GetAsync(string key, Func<string, Task<object>> factory);

        /// <summary>
        /// Gets an item from the cache or null if not found.
        /// 從緩存中獲取一個項目,如果沒有找到亿乳,則可以獲得null
        /// </summary>
        /// <param name="key">Key</param>
        /// <returns>Cached item or null if not found</returns>
        object GetOrDefault(string key);

        /// <summary>
        /// Gets an item from the cache or null if not found.
        /// </summary>
        /// <param name="key">Key</param>
        /// <returns>Cached item or null if not found</returns>
        Task<object> GetOrDefaultAsync(string key);

        /// <summary>
        /// Saves/Overrides an item in the cache by a key.
        /// Use one of the expire times at most (<paramref name="slidingExpireTime"/> or <paramref name="absoluteExpireTime"/>).
        /// If none of them is specified, then
        /// <see cref="DefaultAbsoluteExpireTime"/> will be used if it's not null. Othewise, <see cref="DefaultSlidingExpireTime"/>
        /// will be used.
        /// 在緩存中通過一個鍵來覆蓋一個條目
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="value">Value</param>
        /// <param name="slidingExpireTime">Sliding expire time</param>
        /// <param name="absoluteExpireTime">Absolute expire time</param>
        void Set(string key, object value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);

        /// <summary>
        /// Saves/Overrides an item in the cache by a key.
        /// Use one of the expire times at most (<paramref name="slidingExpireTime"/> or <paramref name="absoluteExpireTime"/>).
        /// If none of them is specified, then
        /// <see cref="DefaultAbsoluteExpireTime"/> will be used if it's not null. Othewise, <see cref="DefaultSlidingExpireTime"/>
        /// will be used.
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="value">Value</param>
        /// <param name="slidingExpireTime">Sliding expire time</param>
        /// <param name="absoluteExpireTime">Absolute expire time</param>
        Task SetAsync(string key, object value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);

        /// <summary>
        /// Removes a cache item by it's key.
        /// 通過它的鍵刪除緩存項
        /// </summary>
        /// <param name="key">Key</param>
        void Remove(string key);

        /// <summary>
        /// Removes a cache item by it's key (does nothing if given key does not exists in the cache).
        /// </summary>
        /// <param name="key">Key</param>
        Task RemoveAsync(string key);

        /// <summary>
        /// Clears all items in this cache.
        /// 清除緩存中的所有項
        /// </summary>
        void Clear();

        /// <summary>
        /// Clears all items in this cache.
        /// </summary>
        Task ClearAsync();
    }

CacheBase

緩存項目基類只要實現(xiàn)了基本的存取操作硝拧,但是具體的實現(xiàn),下沉到子類實現(xiàn)

/// <summary>
    /// Base class for caches.
    /// It's used to simplify implementing <see cref="ICache"/>.
    /// 基類緩存
    /// 簡單實現(xiàn)<see cref="ICache"/>
    /// </summary>
    public abstract class CacheBase : ICache
    {
        public ILogger Logger { get; set; }

        public string Name { get; }

        public TimeSpan DefaultSlidingExpireTime { get; set; }

        public TimeSpan? DefaultAbsoluteExpireTime { get; set; }

        protected readonly object SyncObj = new object();

        private readonly AsyncLock _asyncLock = new AsyncLock();

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="name"></param>
        protected CacheBase(string name)
        {
            Name = name;
            DefaultSlidingExpireTime = TimeSpan.FromHours(1);

            Logger = NullLogger.Instance;
        }

        public virtual object Get(string key, Func<string, object> factory)
        {
            object item = null;

            try
            {
                item = GetOrDefault(key);
            }
            catch (Exception ex)
            {
                Logger.Error(ex.ToString(), ex);
            }

            if (item == null)
            {
                lock (SyncObj)
                {
                    try
                    {
                        item = GetOrDefault(key);
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex.ToString(), ex);
                    }

                    if (item == null)
                    {
                        item = factory(key);

                        if (item == null)
                        {
                            return null;
                        }

                        try
                        {
                            Set(key, item);
                        }
                        catch (Exception ex)
                        {
                            Logger.Error(ex.ToString(), ex);
                        }
                    }
                }
            }

            return item;
        }

        public virtual async Task<object> GetAsync(string key, Func<string, Task<object>> factory)
        {
            object item = null;

            try
            {
                item = await GetOrDefaultAsync(key);
            }
            catch (Exception ex)
            {
                Logger.Error(ex.ToString(), ex);
            }

            if (item == null)
            {
                using (await _asyncLock.LockAsync())
                {
                    try
                    {
                        item = await GetOrDefaultAsync(key);
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex.ToString(), ex);
                    }

                    if (item == null)
                    {
                        item = await factory(key);

                        if (item == null)
                        {
                            return null;
                        }

                        try
                        {
                            await SetAsync(key, item);
                        }
                        catch (Exception ex)
                        {
                            Logger.Error(ex.ToString(), ex);
                        }
                    }
                }
            }

            return item;
        }

        public abstract object GetOrDefault(string key);

        public virtual Task<object> GetOrDefaultAsync(string key)
        {
            return Task.FromResult(GetOrDefault(key));
        }

        public abstract void Set(string key, object value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);

        public virtual Task SetAsync(string key, object value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
        {
            Set(key, value, slidingExpireTime);
            return Task.FromResult(0);
        }

        public abstract void Remove(string key);

        public virtual Task RemoveAsync(string key)
        {
            Remove(key);
            return Task.FromResult(0);
        }

        public abstract void Clear();

        public virtual Task ClearAsync()
        {
            Clear();
            return Task.FromResult(0);
        }

        public virtual void Dispose()
        {

        }
    }

AbpMemoryCache

AbpMemoryCache是基于MemoryCache 實現(xiàn) ICache葛假,使用具體的緩存技術實現(xiàn)緩存項目

/// <summary>
    /// Implements <see cref="ICache"/> to work with <see cref="MemoryCache"/>.
    /// 使用<see cref="MemoryCache"/> 實現(xiàn) <see cref="ICache"/>
    /// </summary>
    public class AbpMemoryCache : CacheBase
    {
        private MemoryCache _memoryCache;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="name">Unique name of the cache</param>
        public AbpMemoryCache(string name)
            : base(name)
        {
            _memoryCache = new MemoryCache(new OptionsWrapper<MemoryCacheOptions>(new MemoryCacheOptions()));
        }

        public override object GetOrDefault(string key)
        {
            return _memoryCache.Get(key);
        }

        public override void Set(string key, object value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
        {
            if (value == null)
            {
                throw new AbpException("Can not insert null values to the cache!");
            }

            if (absoluteExpireTime != null)
            {
                _memoryCache.Set(key, value, DateTimeOffset.Now.Add(absoluteExpireTime.Value));
            }
            else if (slidingExpireTime != null)
            {
                _memoryCache.Set(key, value, slidingExpireTime.Value);
            }
            else if (DefaultAbsoluteExpireTime != null)
            {
                _memoryCache.Set(key, value, DateTimeOffset.Now.Add(DefaultAbsoluteExpireTime.Value));
            }
            else
            {
                _memoryCache.Set(key, value, DefaultSlidingExpireTime);
            }
        }

        public override void Remove(string key)
        {
            _memoryCache.Remove(key);
        }

        public override void Clear()
        {
            _memoryCache.Dispose();
            _memoryCache = new MemoryCache(new OptionsWrapper<MemoryCacheOptions>(new MemoryCacheOptions()));
        }

        public override void Dispose()
        {
            _memoryCache.Dispose();
            base.Dispose();
        }
    }

ICacheManager

緩存管理器應該障陶,單例對象,跟蹤和管理緩存對象

/// <summary>
    /// An upper level container for <see cref="ICache"/> objects. 
    /// A cache manager should work as Singleton and track and manage <see cref="ICache"/> objects.
    /// 緩存管理器應該聊训,單例對象抱究,跟蹤和管理緩存對象
    /// </summary>
    public interface ICacheManager : IDisposable
    {
        /// <summary>
        /// Gets all caches.
        /// 獲取所有緩存
        /// </summary>
        /// <returns>List of caches</returns>
        IReadOnlyList<ICache> GetAllCaches();

        /// <summary>
        /// Gets a <see cref="ICache"/> instance.
        /// It may create the cache if it does not already exists.
        /// 獲取緩存實例
        /// 如果緩存不存在,則創(chuàng)建緩存
        /// </summary>
        /// <param name="name">
        /// Unique and case sensitive name of the cache.
        /// 緩存的唯一的名稱带斑,大小寫敏感
        /// </param>
        /// <returns>The cache reference</returns>
        [NotNull] ICache GetCache([NotNull] string name);
    }

CacheManagerBase

緩存管理器的基類鼓寺,實現(xiàn)基本的操作勋拟,將具體的算法下層到子類實現(xiàn)

/// <summary>
    /// Base class for cache managers.
    /// 緩存管理器的基類
    /// </summary>
    public abstract class CacheManagerBase : ICacheManager, ISingletonDependency
    {
        protected readonly IIocManager IocManager;

        protected readonly ICachingConfiguration Configuration;

        protected readonly ConcurrentDictionary<string, ICache> Caches;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="iocManager"></param>
        /// <param name="configuration"></param>
        protected CacheManagerBase(IIocManager iocManager, ICachingConfiguration configuration)
        {
            IocManager = iocManager;
            Configuration = configuration;
            Caches = new ConcurrentDictionary<string, ICache>();
        }

        public IReadOnlyList<ICache> GetAllCaches()
        {
            return Caches.Values.ToImmutableList();
        }
        
        public virtual ICache GetCache(string name)
        {
            Check.NotNull(name, nameof(name));

            return Caches.GetOrAdd(name, (cacheName) =>
            {
                var cache = CreateCacheImplementation(cacheName);

                var configurators = Configuration.Configurators.Where(c => c.CacheName == null || c.CacheName == cacheName);

                foreach (var configurator in configurators)
                {
                    configurator.InitAction?.Invoke(cache);
                }

                return cache;
            });
        }

        public virtual void Dispose()
        {
            DisposeCaches();
            Caches.Clear();
        }

        protected virtual void DisposeCaches()
        {
            foreach (var cache in Caches)
            {
                IocManager.Release(cache.Value);
            }
        }

        /// <summary>
        /// Used to create actual cache implementation.
        /// 用于創(chuàng)建實際的緩存實現(xiàn)
        /// </summary>
        /// <param name="name">Name of the cache</param>
        /// <returns>Cache object</returns>
        protected abstract ICache CreateCacheImplementation(string name);
    }

AbpMemoryCacheManager

使用MemoryCache實現(xiàn)的ICacheManager,使用具體的緩存組件實現(xiàn)ICacheManager妈候,實現(xiàn)父類CacheManagerBase的方法

 /// <summary>
    /// Implements <see cref="ICacheManager"/> to work with MemoryCache.
    /// 使用MemoryCache實現(xiàn)<see cref="ICacheManager"/>
    /// </summary>
    public class AbpMemoryCacheManager : CacheManagerBase
    {
        public ILogger Logger { get; set; }

        /// <summary>
        /// Constructor.
        /// </summary>
        public AbpMemoryCacheManager(IIocManager iocManager, ICachingConfiguration configuration)
            : base(iocManager, configuration)
        {
            Logger = NullLogger.Instance;
        }

        protected override ICache CreateCacheImplementation(string name)
        {
            return new AbpMemoryCache(name)
            {
                Logger = Logger
            };
        }

        protected override void DisposeCaches()
        {
            foreach (var cache in Caches.Values)
            {
                cache.Dispose();
            }
        }
    }

ICacheConfigurator

緩存配置器敢靡,配置單個緩存項目,主要是配置創(chuàng)建之后的調用方法

/// <summary>
    /// A registered cache configurator.
    /// 緩存配置器
    /// </summary>
    public interface ICacheConfigurator
    {
        /// <summary>
        /// Name of the cache.
        /// It will be null if this configurator configures all caches.
        /// 緩存名稱苦银,如果這個配置器配置了所有的緩存啸胧,它就會是null
        /// </summary>
        string CacheName { get; }

        /// <summary>
        /// Configuration action. Called just after the cache is created.
        /// 配置操作。在緩存創(chuàng)建之后調用
        /// </summary>
        Action<ICache> InitAction { get; }
    }

ICachingConfiguration

緩存系統(tǒng)配置幔虏,配置多個ICache信息

/// <summary>
    /// Used to configure caching system.
    /// 緩存系統(tǒng)配置
    /// </summary>
    public interface ICachingConfiguration
    {
        /// <summary>
        /// Gets the ABP configuration object.
        /// ABP配置值系統(tǒng)
        /// </summary>
        IAbpStartupConfiguration AbpConfiguration { get; }

        /// <summary>
        /// List of all registered configurators.
        /// 所有已注冊的配置器列表
        /// </summary>
        IReadOnlyList<ICacheConfigurator> Configurators { get; }

        /// <summary>
        /// Used to configure all caches.
        /// 用于配置所有緩存
        /// </summary>
        /// <param name="initAction">
        /// An action to configure caches
        /// This action is called for each cache just after created.
        /// 配置緩存的操作
        /// 這個動作在創(chuàng)建后的每個緩存中都被調用
        /// </param>
        void ConfigureAll(Action<ICache> initAction);

        /// <summary>
        /// Used to configure a specific cache.
        /// 用于配置特定的緩存
        /// </summary>
        /// <param name="cacheName">Cache name</param>
        /// <param name="initAction">
        /// An action to configure the cache.
        /// This action is called just after the cache is created.
        /// </param>
        void Configure(string cacheName, Action<ICache> initAction);
    }

設計模式

  • 模板方法模式:ICacheManager,CacheManagerBase,AbpMemoryCacheManager吓揪,父類實現(xiàn)主要算法,定義調用順序和過程所计,子類實現(xiàn)具體算法
  • 橋接模式:ICacheManager,ICache 獨立變化Manager和Cache,是的Manager和Cache可以獨自擴展

我的公眾號
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末团秽,一起剝皮案震驚了整個濱河市主胧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌习勤,老刑警劉巖踪栋,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異图毕,居然都是意外死亡夷都,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門予颤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來囤官,“玉大人,你說我怎么就攤上這事蛤虐〉骋” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵驳庭,是天一觀的道長刑顺。 經(jīng)常有香客問我,道長饲常,這世上最難降的妖魔是什么蹲堂? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮贝淤,結果婚禮上柒竞,老公的妹妹穿的比我還像新娘。我一直安慰自己霹娄,他們只是感情好能犯,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布鲫骗。 她就那樣靜靜地躺著,像睡著了一般踩晶。 火紅的嫁衣襯著肌膚如雪执泰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天渡蜻,我揣著相機與錄音术吝,去河邊找鬼。 笑死茸苇,一個胖子當著我的面吹牛排苍,可吹牛的內容都是我干的。 我是一名探鬼主播学密,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼淘衙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了腻暮?” 一聲冷哼從身側響起彤守,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哭靖,沒想到半個月后具垫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡试幽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年筝蚕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铺坞。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡起宽,死狀恐怖,靈堂內的尸體忽然破棺而出康震,到底是詐尸還是另有隱情燎含,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布腿短,位于F島的核電站屏箍,受9級特大地震影響,放射性物質發(fā)生泄漏橘忱。R本人自食惡果不足惜赴魁,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钝诚。 院中可真熱鬧颖御,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芦岂,卻和暖如春瘪弓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背禽最。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工腺怯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留渣触,地道東北人溉箕。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像暴区,于是被迫代替她去往敵國和親懦趋。 傳聞我的和親對象是個殘疾皇子晾虑,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內容