【.NET Core框架】IMemoryCache、IDistributedCache

介绍

.Net Core框架中有两个缓存接口:

  • IMemoryCache:内存缓存接口,内存缓存可以存储任何对象
  • IDistributedCache:分布式缓存接口(Redis、Sqlserver、MongoDB、DB...)

微软缓存组件源码在:https://github.com/aspnet/Caching

内存缓存(MemoryCache)

如何使用

1、安装程序集:Microsoft.Extensions.Caching.Memory,如果是是Core MVC程序自带的Microsoft.AspNetCore.App包里已经涵盖了 Microsoft.Extensions.Caching.Memory,无需重复下载。
2、AddControllers()中已经注册了缓存服务services.AddMemoryCache();。如果是控制台项目使用内存缓存,需要手动注册缓存服务: services.AddMemoryCache();
3、构造函数注入 IMemoryCache

IMemoryCache

内存缓存接口只有三个方法

public interface IMemoryCache : IDisposable
    {
        ICacheEntry CreateEntry(object key);//添加一个缓存
        void Remove(object key);//删除一个缓存
        bool TryGetValue(object key, out object value);//获取一个缓存(并可得到具体的缓存是否存在)
    }

ICacheEntry代表一条缓存

public interface ICacheEntry : IDisposable
{
        DateTimeOffset? AbsoluteExpiration{get;set;}
        TimeSpan? AbsoluteExpirationRelativeToNow{get;set;}
        IList<IChangeToken> ExpirationTokens{get;}
        object Key{get;}
        IList<PostEvictionCallbackRegistration> PostEvictionCallbacks{get;}
        CacheItemPriority Priority{get;set;}
        long? Size{get;set;}
        TimeSpan? SlidingExpiration{get;set;}
        object Value{get;set;}
}
  • Key 缓存key
  • Value 缓存值
  • AbsoluteExpiration 绝对过期时间,为null则条件无效
  • AbsoluteExpirationRelativeToNow 相对当前时间的绝对过期时间(使用TimeSpan),为null条件无效
  • SlidingExpiration 滑动过期时间
  • ExpirationTokens 提供用来自定义缓存过期
  • PostEvictionCallbacks 缓存失效回调
  • Priority 缓存项优先级(在缓存满载的时候决定清除的顺序)
  • Size 代表缓存数据的大小,在内存缓存中一般为null

演示:

            IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); ;
            using (ICacheEntry cacheEntry = cache.CreateEntry("k1"))
            {
                cacheEntry.SetValue("v1");
            }
            string v1 = cache.Get<string>("k1");

MS提供了IMemoryCache扩展方法类CacheExtensions
CacheExtensions:

    public static class CacheExtensions
    {
        public static object Get(this IMemoryCache cache, object key){}
        public static Task<TItem> GetOrCreateAsync<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, Task<TItem>> factory){}
        public static TItem GetOrCreate<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, TItem> factory){}
        public static TItem Get<TItem>(this IMemoryCache cache, object key){}

        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value){}
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, MemoryCacheEntryOptions options){}
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, IChangeToken expirationToken){}
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, DateTimeOffset absoluteExpiration){}
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, TimeSpan absoluteExpirationRelativeToNow){}

        public static bool TryGetValue<TItem>(this IMemoryCache cache, object key, out TItem value){}
    }

缓存案例

绝对时间、滑动时间

       //绝对过期
        return memoryCache.GetOrCreate("c1", entry =>
        {
            entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddSeconds(2);
            return ++time;
        });
        //绝对过期
        return memoryCache.GetOrCreate("c2", entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(2);
            return ++time;
        });
        //滑动过期
        return memoryCache.GetOrCreate("c3", entry =>
        {
            entry.SlidingExpiration = TimeSpan.FromSeconds(2);
            return ++time;
        });
        //过期组合策略
        return memoryCache.GetOrCreate("c4", entry =>
        {
            entry.SlidingExpiration = TimeSpan.FromSeconds(2);
            entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddSeconds(2);
            return ++time;
        });
        //过期回调  
        return memoryCache.GetOrCreate("c1", entry =>
        {
            entry.SlidingExpiration = TimeSpan.FromSeconds(2);
            entry.RegisterPostEvictionCallback((key, value, reason, state) =>
            {
                Console.WriteLine($"key:{key}");
                Console.WriteLine($"value:{key}");
                Console.WriteLine($"reason:{key}");
                Console.WriteLine($"state:{key}");

            });
            return ++time;
        });

通过IChangeToken清除缓存

public class CacheProvider 
{
    private static CancellationTokenSource _resetCacheToken = new CancellationTokenSource();
    private readonly IMemoryCache _innerCache;


    public T Set<T>(object key, T value) 
    {
        var options = new MemoryCacheEntryOptions().SetPriority(CacheItemPriority.Normal).SetAbsoluteExpiration(typeExpiration);
        options.AddExpirationToken(new CancellationChangeToken(_resetCacheToken.Token));

        _innerCache.Set(CreateKey(type, key), value, options);

        return value;
    }

    public void Reset()
    {
        if (_resetCacheToken != null && !_resetCacheToken.IsCancellationRequested && _resetCacheToken.Token.CanBeCanceled)
        {
            _resetCacheToken.Cancel();
            _resetCacheToken.Dispose();
        }

        _resetCacheToken = new CancellationTokenSource();
    }
}

MemoryCacheOptions


Clock 顾名思义,是用来提供当前系统时间(默认UTC),
CompactOnMemoryPressure 已经被废弃,可以不用管
ExpirationScanFrequency 过期扫描频率(默认为1分钟,可以理解为每过多久移除一次过期的缓存项)
SizeLimit 缓存大小限制(这属于一个说明性属性,而且单位也不是缓存数目,而是缓存真正占用的空间大小)
CompactionPercentage 压缩率(默认0.05,百分比)

Clock

默认当前系统的当前UTC时间,可以重写 Clock 来实现自己自定义的获取当前时间的逻辑。
缓存里面用此时间来判断缓存是否过期。

ExpirationScanFrequency

缓存无非是一个字典表,当一些缓存项过期失效时候我们需要移除字典表里面的内容。
MemoryCache提供了一个属性来设置,间隔多久才进行一次过期缓存移除(默认1分钟)。
因为MemoryCache里面没有使用定时器来进行过期扫描,在添加、获取、删除缓存项时校验ExpirationScanFrequency属性,如果超过了间隔时间就移除缓存

SizeLimit

这个属性的作用是:当所有缓存大小超过这个值的时候进行一次缓存压缩。
这个属性并不是缓存项的数量,而是缓存真正占用的空间大小,如这个缓存项占用了多少内存。
在 MemoryCache 中关于缓存项的大小默认都是null或0,当然你可以通过手动设置缓存项的Size来启用相关功能。

CompactionPercentage

当内存大小超过 SizeLimit 时候进行压缩的比率,默认值是0.05,也就是百分之5。
缓存清理优先级:

压缩顺序:Low、Normal、High
NeverRemove 永远不会在超过 SizeLimt 时候进行清理。
CacheEntry 默认的 优先级为:Normal。

使用 SetSize、Size 和 SizeLimit 限制缓存大小

MemoryCache 实例可以选择指定并强制实施大小限制。 缓存大小限制没有定义的度量单位,因为缓存没有度量条目大小的机制。 如果设置了缓存大小限制,则所有条目都必须指定大小。 ASP.NET Core 运行时不会根据内存压力限制缓存大小。 由开发人员限制缓存大小。 指定的大小采用开发人员选择的单位。
SizeLimit 没有单位。 如果已设置缓存大小限制,则缓存条目必须以其认为最适合的任何单位指定大小。 缓存实例的所有用户都应使用相同的单位系统。 如果缓存的条目大小之和超出了 SizeLimit 指定的值,则不会缓存条目。 如果未设置缓存大小限制,则会忽略条目上设置的缓存大小。
例如:
如果 Web 应用主要缓存字符串,则每个缓存条目的大小可以是字符串长度。
应用可以将所有条目的大小指定为 1,大小限制是条目计数。
设置SizeLimit:

        new MemoryCacheOptions
        {
            SizeLimit = 1024
        };

使用 SetSize 扩展方法或 Size 属性设置缓存条目的大小:

    var cacheEntryOptions = new MemoryCacheEntryOptions()
        .SetSize(1);

    // cacheEntryOptions.Size = 1;

    _memoryCache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);

MemoryCache.Compact(清理缓存)

MemoryCache.Compact 尝试按以下顺序删除指定百分比的缓存:
所有到期项。
按优先级排列的项。 首先删除最低优先级的项。
最近最少使用的对象。
绝对到期时间最短的项。
可调到期时间最短的项。
永远不会删除优先级为 NeverRemove 的固定项。
调用 Compact 以删除 50% 的缓存条目:

((MemoryCache)memoryCache).Compact(0.5);

分布式缓存(IDistributedCache)

IDistributedCache:

public interface IDistributedCache
    {
     //获取
        byte[] Get(string key);
        Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken));
        //刷新并重置其过期超时的值(如果有)
        void Refresh(string key);
        Task RefreshAsync(string key, CancellationToken token = default(CancellationToken));
        //删除
        void Remove(string key);
        Task RemoveAsync(string key, CancellationToken token = default(CancellationToken));
        //添加
        void Set(string key, byte[] value, DistributedCacheEntryOptions options);
        Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken));
    }

Redis分布式缓存如何使用

安装 Microsoft.Extensions.Caching.Redis包
控制台中使用:

static void Main(string[] args)
        {
            //RedisCache实现了接口IDistributedCache
            RedisCache redisCache = new RedisCache(new RedisCacheOptions() {
                Configuration="192.168.254.134:6379",
                InstanceName="test"
            });
            redisCache.SetString("key","value");
            var val = redisCache.GetString("key");
        }

asp.net core中使用:

//将Redis分布式缓存服务添加到服务中
            services.AddDistributedRedisCache(options =>
            {
                //Redis实例名
                options.InstanceName = "RedisDistributedCache";
                options.ConfigurationOptions = new ConfigurationOptions()
                {
                    ConnectTimeout = 2000,
                    DefaultDatabase = 1,
                    //Password = "xxxxxx",
                    AllowAdmin = true,
                    AbortOnConnectFail = false,//当为true时,当没有可用的服务器时则不会创建一个连接
                    
                };                
                options.ConfigurationOptions.EndPoints.Add("xxxxxx:6379");
            });

需要缓存的服务中注入IDistributedCache即可:

public HomeController(IDistributedCache Cache){}  

MongoDB分布式缓存如何使用

安装 MarkCBB.Extensions.Caching.MongoDB 包
控制台中使用:

static void Main(string[] args)
        {
            MongoDBCache mongoDBCache = new MongoDBCache(new MongoDBCacheOptions()
            {
                ConnectionString = "mongodb://192.168.254.135:27017",
                DatabaseName = "sample",
                CollectionName = "sample"
            });

            mongoDBCache.Set("username", Encoding.UTF8.GetBytes("jack"), new DistributedCacheEntryOptions()
            {
                AbsoluteExpiration = DateTime.Now.AddDays(1)
            });

            var info = mongoDBCache.GetString("username");
        }

参考:
https://www.cnblogs.com/ants/p/8477223.html

posted @ 2020-01-05 12:00  .Neterr  阅读(4140)  评论(1编辑  收藏  举报