自從使用Asp.net Core2.0 以來,不停摸索,查閱資料,這方面的資料是真的少,因此,在前人的基礎上,摸索出了Asp.net Core2.0 緩存 MemoryCache 和 Redis的用法,並實現了簡單的封裝
那么,先給出幾個參考資料吧
關於兩種緩存:https://www.cnblogs.com/yuangang/p/5800113.html
關於redis持久化:https://blog.csdn.net/u010785685/article/details/52366977
兩個nuget包,不用多說
接下來,貼出代碼
首先在startup,我讀取了appstting.json的數據,作為redis的配置

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Application.Common; using Application.Common.CommonObject; using Application.CoreWork; using Application.DAL; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace Application.web { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { Connection.MySqlConnection = Configuration.GetConnectionString("MySqlConnection"); Connection.SqlConnection = Configuration.GetConnectionString("SqlConnection"); RedisConfig.Connection = Configuration.GetSection("RedisConfig")["Connection"]; RedisConfig.DefaultDatabase =Convert.ToInt32( Configuration.GetSection("RedisConfig")["DefaultDatabase"]); RedisConfig.InstanceName = Configuration.GetSection("RedisConfig")["InstanceName"]; CommonManager.CacheObj.GetMessage<RedisCacheHelper>(); services.AddMvc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseBrowserLink(); app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } }
再貼上配置
"RedisConfig": { "Connection": "127.0.0.1:6379", "DefaultDatabase": 0, "InstanceName": "Redis1" },
用來放配置信息的靜態類

using System; using System.Collections.Generic; using System.Text; namespace Application.Common { public static class RedisConfig { public static string configname { get; set; } public static string Connection { get; set; } public static int DefaultDatabase { get; set; } public static string InstanceName { get; set; } } }
然后ICacheHelper.cs,這個類是兩種緩存通用的接口,讓使用更方便

using System; using System.Collections.Generic; using System.Net; using System.Text; namespace Application.Common.CommonObject { public interface ICacheHelper { bool Exists(string key); T GetCache<T>(string key) where T : class; void SetCache(string key, object value); void SetCache(string key, object value, DateTimeOffset expiressAbsoulte);//設置絕對時間過期 void SetSlidingCache(string key, object value, TimeSpan t); //設置滑動過期, 因redis暫未找到自帶的滑動過期類的API,暫無需實現該接口 void RemoveCache(string key); void KeyMigrate(string key, EndPoint endPoint,int database,int timeountseconds); void Dispose(); void GetMssages(); void Publish(string msg); } }
然后,實現接口,首先是MemoryCacheHelper

using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Net; using System.Text; namespace Application.Common.CommonObject { public class MemoryCacheHelper : ICacheHelper { //public MemoryCacheHelper(/*MemoryCacheOptions options*/)//這里可以做成依賴注入,但沒打算做成通用類庫,所以直接把選項直接封在幫助類里邊 //{ // //this._cache = new MemoryCache(options); // //this._cache = new MemoryCache(new MemoryCacheOptions()); //} //public MemoryCacheHelper(MemoryCacheOptions options)//這里可以做成依賴注入,但沒打算做成通用類庫,所以直接把選項直接封在幫助類里邊 //{ // this._cache = new MemoryCache(options); //} public static IMemoryCache _cache=new MemoryCache(new MemoryCacheOptions()); /// <summary> /// 是否存在此緩存 /// </summary> /// <param name="key"></param> /// <returns></returns> public bool Exists(string key) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); object v = null; return _cache.TryGetValue<object>(key, out v); } /// <summary> /// 取得緩存數據 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public T GetCache<T>(string key) where T : class { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); T v = null; _cache.TryGetValue<T>(key, out v); return v; } /// <summary> /// 設置緩存 /// </summary> /// <param name="key"></param> /// <param name="value"></param> public void SetCache(string key, object value) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); if (value == null) throw new ArgumentNullException(nameof(value)); object v = null; if (_cache.TryGetValue(key, out v)) _cache.Remove(key); _cache.Set<object>(key, value); } /// <summary> /// 設置緩存,絕對過期 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expirationMinute">間隔分鍾</param> /// CommonManager.CacheObj.Save<RedisCacheHelper>("test", "RedisCache works!", 30); public void SetCache(string key, object value, double expirationMinute) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); if (value == null) throw new ArgumentNullException(nameof(value)); object v = null; if (_cache.TryGetValue(key, out v)) _cache.Remove(key); DateTime now = DateTime.Now; TimeSpan ts = now.AddMinutes(expirationMinute) - DateTime.Now; _cache.Set<object>(key, value, ts); } /// <summary> /// 設置緩存,絕對過期 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expirationTime">DateTimeOffset 結束時間</param> /// CommonManager.CacheObj.Save<RedisCacheHelper>("test", "RedisCache works!", DateTimeOffset.Now.AddSeconds(30)); public void SetCache(string key, object value, DateTimeOffset expirationTime) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); if (value == null) throw new ArgumentNullException(nameof(value)); object v = null; if (_cache.TryGetValue(key, out v)) _cache.Remove(key); _cache.Set<object>(key, value, expirationTime); } /// <summary> /// 設置緩存,相對過期時間 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="t"></param> /// CommonManager.CacheObj.SaveSlidingCache<MemoryCacheHelper>("test", "MemoryCache works!",TimeSpan.FromSeconds(30)); public void SetSlidingCache(string key,object value,TimeSpan t) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); if (value == null) throw new ArgumentNullException(nameof(value)); object v = null; if (_cache.TryGetValue(key, out v)) _cache.Remove(key); _cache.Set(key, value, new MemoryCacheEntryOptions() { SlidingExpiration=t }); } /// <summary> /// 移除緩存 /// </summary> /// <param name="key"></param> public void RemoveCache(string key) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); _cache.Remove(key); } /// <summary> /// 釋放 /// </summary> public void Dispose() { if (_cache != null) _cache.Dispose(); GC.SuppressFinalize(this); } public void KeyMigrate(string key, EndPoint endPoint, int database, int timeountseconds) { throw new NotImplementedException(); } public void GetMssages() { throw new NotImplementedException(); } public void Publish(string msg) { throw new NotImplementedException(); } } }
然后是RedisCacheHelper

using Microsoft.Extensions.Caching.Redis; using Newtonsoft.Json; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Net; using System.Text; namespace Application.Common.CommonObject { public class RedisCacheHelper : ICacheHelper { public RedisCacheHelper(/*RedisCacheOptions options, int database = 0*/)//這里可以做成依賴注入,但沒打算做成通用類庫,所以直接把連接信息直接寫在幫助類里 { options = new RedisCacheOptions(); options.Configuration = "127.0.0.1:6379";//RedisConfig.Connection; options.InstanceName = RedisConfig.InstanceName; int database = RedisConfig.DefaultDatabase; _connection = ConnectionMultiplexer.Connect(options.Configuration); _cache = _connection.GetDatabase(database); _instanceName = options.InstanceName; _sub = _connection.GetSubscriber(); } public static RedisCacheOptions options; public static IDatabase _cache; public static ConnectionMultiplexer _connection; public static string _instanceName; public static ISubscriber _sub; /// <summary> /// 取得redis的Key名稱 /// </summary> /// <param name="key"></param> /// <returns></returns> private string GetKeyForRedis(string key) { return _instanceName + key; } /// <summary> /// 判斷當前Key是否存在數據 /// </summary> /// <param name="key"></param> /// <returns></returns> public bool Exists(string key) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); return _cache.KeyExists(GetKeyForRedis(key)); } /// <summary> /// 取得緩存數據 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public T GetCache<T>(string key) where T : class { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); var value = _cache.StringGet(GetKeyForRedis(key)); if (!value.HasValue) return default(T); return JsonConvert.DeserializeObject<T>(value); } /// <summary> /// 設置緩存數據 /// </summary> /// <param name="key"></param> /// <param name="value"></param> public void SetCache(string key, object value) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); if (value == null) throw new ArgumentNullException(nameof(value)); if (Exists(GetKeyForRedis(key))) RemoveCache(GetKeyForRedis(key)); _cache.StringSet(GetKeyForRedis(key), JsonConvert.SerializeObject(value)); } /// <summary> /// 設置絕對過期時間 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiressAbsoulte"></param> public void SetCache(string key, object value, DateTimeOffset expiressAbsoulte) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); if (value == null) throw new ArgumentNullException(nameof(value)); if (Exists(GetKeyForRedis(key))) RemoveCache(GetKeyForRedis(key)); TimeSpan t = expiressAbsoulte - DateTimeOffset.Now; _cache.StringSet(GetKeyForRedis(key), JsonConvert.SerializeObject(value), t); } /// <summary> /// 設置相對過期時間 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expirationMinute"></param> public void SetCache(string key, object value, double expirationMinute) { if (Exists(GetKeyForRedis(key))) RemoveCache(GetKeyForRedis(key)); DateTime now = DateTime.Now; TimeSpan ts = now.AddMinutes(expirationMinute) - now; _cache.StringSet(GetKeyForRedis(key), JsonConvert.SerializeObject(value), ts); } /// <summary> /// /// </summary> /// <param name="key"></param> /// <param name="endPoint"></param> /// <param name="database"></param> /// <param name="timeountseconds"></param> public void KeyMigrate(string key, EndPoint endPoint, int database, int timeountseconds) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); _cache.KeyMigrate(GetKeyForRedis(key), endPoint, database, timeountseconds); } /// <summary> /// 移除redis /// </summary> /// <param name="key"></param> public void RemoveCache(string key) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); _cache.KeyDelete(GetKeyForRedis(key)); } /// <summary> /// 銷毀連接 /// </summary> public void Dispose() { if (_connection != null) _connection.Dispose(); GC.SuppressFinalize(this); } public void SetSlidingCache(string key, object value, TimeSpan t) { throw new NotImplementedException(); } public void GetMssages() { using (_connection = ConnectionMultiplexer.Connect(options.Configuration)) _sub.Subscribe("msg", (channel, message) => { string result = message; }); } public void Publish(string msg) { using (_connection=ConnectionMultiplexer.Connect(options.Configuration)) _sub.Publish("msg", msg); } } }
然后是Cache.cs,用來實例化,此處用單例模式,保證項目使用的是一個緩存實例,博主吃過虧,由於redis實例過多,構造函數運行太多次,導致client連接數過大,內存不夠跑,速度卡慢

using System; using System.Collections.Generic; using System.Text; namespace Application.Common.CommonObject { public sealed class Cache { internal Cache() { } public static ICacheHelper cache; /// <summary> /// 判斷緩存是否存在 /// </summary> /// <typeparam name="CacheType">緩存類型</typeparam> /// <param name="key">鍵名</param> /// <returns></returns> public bool Exists<CacheType>(string key) where CacheType : ICacheHelper, new() { if (cache!=null&& typeof(CacheType).Equals(cache.GetType())) return cache.Exists(key); else { cache = new CacheType(); return cache.Exists(key); } } /// <summary> /// 獲取緩存 /// </summary> /// <typeparam name="T">轉換的類</typeparam> /// <typeparam name="CacheType">緩存類型</typeparam> /// <param name="key">鍵名</param> /// <returns>轉換為T類型的值</returns> public T GetCache<T,CacheType>(string key) where T:class where CacheType : ICacheHelper, new() { if (cache != null && typeof(CacheType).Equals(cache.GetType())) return cache.GetCache<T>(key); else { cache = new CacheType(); return cache.GetCache<T>(key); } } /// <summary> /// 保存緩存 /// </summary> /// <typeparam name="CacheType">緩存類型</typeparam> /// <param name="key">鍵名</param> /// <param name="value">值</param> public void Save<CacheType>(string key,object value) where CacheType : ICacheHelper, new() { if (cache != null && typeof(CacheType).Equals(cache.GetType())) cache.SetCache(key, value); else { cache = new CacheType(); cache.SetCache(key, value); } } /// <summary> /// 保存緩存並設置絕對過期時間 /// </summary> /// <typeparam name="CacheType">緩存類型</typeparam> /// <param name="key">鍵名</param> /// <param name="value">值</param> /// <param name="expiressAbsoulte">絕對過期時間</param> public void Save<CacheType>(string key, object value, DateTimeOffset expiressAbsoulte) where CacheType : ICacheHelper, new() { if (cache != null && typeof(CacheType).Equals(cache.GetType())) cache.SetCache(key, value, expiressAbsoulte); else { cache = new CacheType(); cache.SetCache(key, value, expiressAbsoulte); } } /// <summary> /// 保存滑動緩存 /// </summary> /// <typeparam name="CacheType">只能用memorycache,redis暫不實現</typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <param name="t">間隔時間</param> public void SaveSlidingCache<CacheType>(string key, object value,TimeSpan t) where CacheType : MemoryCacheHelper, new() { if (cache != null && typeof(CacheType).Equals(cache.GetType())) cache.SetSlidingCache(key, value, t); else { cache = new CacheType(); cache.SetSlidingCache(key, value, t); } } /// <summary> /// 刪除一個緩存 /// </summary> /// <typeparam name="CacheType">緩存類型</typeparam> /// <param name="key">要刪除的key</param> public void Delete<CacheType>(string key)where CacheType : ICacheHelper, new() { if (cache != null && typeof(CacheType).Equals(cache.GetType())) cache.RemoveCache(key); else { cache = new CacheType(); cache.RemoveCache(key); } } /// <summary> /// 釋放 /// </summary> /// <typeparam name="CacheType"></typeparam> public void Dispose<CacheType>() where CacheType : ICacheHelper, new() { if (cache != null && typeof(CacheType).Equals(cache.GetType())) cache.Dispose(); else { cache = new CacheType(); cache.Dispose(); } } public void GetMessage<CacheType>() where CacheType:RedisCacheHelper,new() { if (cache != null && typeof(CacheType).Equals(cache.GetType())) cache.GetMssages(); else { cache = new CacheType(); cache.GetMssages(); } } public void Publish<CacheType>(string msg)where CacheType : RedisCacheHelper, new() { if (cache != null && typeof(CacheType).Equals(cache.GetType())) cache.Publish(msg); else { cache = new CacheType(); cache.Publish(msg); } } } }
接下來,CommonManager.cs,該類主要是為了作為一個靜態類調用緩存,由於Cache.cs並非靜態類,方便調用

using Application.Common.CommonObject; using System; using System.Collections.Generic; using System.Text; namespace Application.Common { public class CommonManager { private static readonly object lockobj = new object(); private static volatile Cache _cache = null; /// <summary> /// Cache /// </summary> public static Cache CacheObj { get { if (_cache == null) { lock (lockobj) { if (_cache == null) _cache = new Cache(); } } return _cache; } } } }
最后呢就是使用啦隨便建的一個控制器

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; using Application.Common; using Application.Common.CommonObject; using Application.CoreWork; using Microsoft.AspNetCore.Mvc; namespace Application.web.Controllers { public class DefaultController : BaseController { public IActionResult Index() { for (int i = 0; i < 100000; i++) { CommonManager.CacheObj.Save<RedisCacheHelper>("key" + i, "key" + i + " works!"); } return View(); } public IActionResult GetStrin(int index) { string res = "已經過期了"; if (CommonManager.CacheObj.Exists<RedisCacheHelper>("key" + index)) { res = CommonManager.CacheObj.GetCache<String, RedisCacheHelper>("key" + index); } return Json(new ExtJson { success = true, code = 1000, msg = "成功", jsonresult = res }); } public IActionResult Publish(string msg) { try { CommonManager.CacheObj.Publish<RedisCacheHelper>(msg); return Json(new ExtJson { success = true, code = 1000, msg = "成功", jsonresult = msg }); } catch { return Json(new ExtJson { success = true, code = 1000, msg = "失敗", jsonresult = msg }); } } } }
那么,有的朋友在json處可能會報錯,自己封裝下,或者用原來的json吧
如果有不懂的地方,可以在評論中提問,有更好的改進方式,也請聯系我,共同進步,感謝閱讀
2019-5-23更新
在實際運行過程中,發現大量用戶請求,並且兩種緩存混合使用時,redis和memory的單例會多次創建,導致redis連接數達到上限,下面貼出修改后的cache.cs
using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; namespace Application.Common.CommonObject { public sealed class Cache { internal Cache() { } public static ICacheHelper cache; public static RedisCacheHelper redis; public static MemoryCacheHelper memory; public ICacheHelper Getcachehelper<CacheType>() { if (typeof(CacheType).Equals(typeof(RedisCacheHelper))) { if (redis == null) { redis = new RedisCacheHelper(); Process pc = Process.GetCurrentProcess(); CommonManager.TxtObj.Log4netError("進程ID:"+pc.Id+";redis為null,創建一個新的實例",typeof(Cache),null); } return redis; } else if (typeof(CacheType).Equals(typeof(MemoryCacheHelper))) { if (memory == null) memory = new MemoryCacheHelper(); return memory; } else return null; } /// <summary> /// 判斷緩存是否存在 /// </summary> /// <typeparam name="CacheType">緩存類型</typeparam> /// <param name="key">鍵名</param> /// <returns></returns> public bool Exists<CacheType>(string key) where CacheType : ICacheHelper, new() { cache = Getcachehelper<CacheType>(); return cache.Exists(key); } /// <summary> /// 獲取緩存 /// </summary> /// <typeparam name="T">轉換的類</typeparam> /// <typeparam name="CacheType">緩存類型</typeparam> /// <param name="key">鍵名</param> /// <returns>轉換為T類型的值</returns> public T GetCache<T, CacheType>(string key) where T : class where CacheType : ICacheHelper, new() { cache = Getcachehelper<CacheType>(); return cache.GetCache<T>(key); } /// <summary> /// 保存緩存 /// </summary> /// <typeparam name="CacheType">緩存類型</typeparam> /// <param name="key">鍵名</param> /// <param name="value">值</param> public void Save<CacheType>(string key, object value) where CacheType : ICacheHelper, new() { cache = Getcachehelper<CacheType>(); cache.SetCache(key, value); } /// <summary> /// 保存緩存並設置絕對過期時間 /// </summary> /// <typeparam name="CacheType">緩存類型</typeparam> /// <param name="key">鍵名</param> /// <param name="value">值</param> /// <param name="expiressAbsoulte">絕對過期時間</param> public void Save<CacheType>(string key, object value, DateTimeOffset expiressAbsoulte) where CacheType : ICacheHelper, new() { cache = Getcachehelper<CacheType>(); cache.SetCache(key, value, expiressAbsoulte); } /// <summary> /// 保存滑動緩存 /// </summary> /// <typeparam name="CacheType">只能用memorycache,redis暫不實現</typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <param name="t">間隔時間</param> public void SaveSlidingCache<CacheType>(string key, object value, TimeSpan t) where CacheType : MemoryCacheHelper, new() { cache = Getcachehelper<CacheType>(); cache.SetSlidingCache(key, value, t); } /// <summary> /// 刪除一個緩存 /// </summary> /// <typeparam name="CacheType">緩存類型</typeparam> /// <param name="key">要刪除的key</param> public void Delete<CacheType>(string key) where CacheType : ICacheHelper, new() { cache = Getcachehelper<CacheType>(); cache.RemoveCache(key); } /// <summary> /// 釋放 /// </summary> /// <typeparam name="CacheType"></typeparam> public void Dispose<CacheType>() where CacheType : ICacheHelper, new() { cache = Getcachehelper<CacheType>(); cache.Dispose(); } public List<string> GetCacheKeys<CacheType>() where CacheType : ICacheHelper, new() { cache = Getcachehelper<CacheType>(); return cache.GetCacheKeys(); } public void GetMessage<CacheType>() where CacheType : RedisCacheHelper, new() { cache = Getcachehelper<CacheType>(); cache.GetMssages(); } public void Publish<CacheType>(string msg) where CacheType : RedisCacheHelper, new() { cache = Getcachehelper<CacheType>(); cache.Publish(msg); } } }