接着上一章节继续唠唠
本章主要说一下Redis
- Redis操作优化
一.基础类的配置工作
1.我想信许多人(许多neter人)操作redis依然用的是StackExchange.Redis,这个neget包,并没有用国内现在一些大佬们推出了包
RedisOptions主要是redis连接的一个配置类
实现代码如下:
public class RedisOptions ???{ ???????/// <summary> ???????/// 数据库地址 ???????/// </summary> ???????public string RedisHost { get; set; } ???????/// <summary> ???????/// 数据库用户名 ???????/// </summary> ???????public string RedisName { get; set; } ???????/// <summary> ???????/// 数据库密码 ???????/// </summary> ???????public string RedisPass { get; set; } ???????/// <summary> ???????/// 库 ???????/// </summary> ???????public int RedisIndex { get; set; } ???}
RedisServiceExtensions,算是redis操作的核心类,主要封装了redis的crud以及sub,pub
public static class RedisServiceExtensions ???{ ???????#region 初始化参数 ???????private static readonly int _DefulatTime = 600; //默认有效期 ???????private static RedisOptions config; ???????private static ConnectionMultiplexer connection; ???????private static IDatabase _db; ???????private static ISubscriber _sub; ???????#endregion ???????public static IServiceCollection AddRedisCacheService( ???????????this IServiceCollection serviceCollection, ???????????Func<RedisOptions, RedisOptions> redisOptions = null ???????????) ???????{ ???????????var _redisOptions = new RedisOptions(); ???????????_redisOptions = redisOptions?.Invoke(_redisOptions) ?? _redisOptions; ???????????config = _redisOptions; ???????????connection = ConnectionMultiplexer.Connect(GetSystemOptions()); ???????????_db = connection.GetDatabase(config.RedisIndex); ???????????_sub = connection.GetSubscriber(); ???????????return serviceCollection; ???????} ???????????????#region 系统配置 ???????/// <summary> ???????/// 获取系统配置 ???????/// </summary> ???????/// <returns></returns> ???????private static ConfigurationOptions GetSystemOptions() ???????{ ???????????var options = new ConfigurationOptions ???????????{ ???????????????AbortOnConnectFail = false, ???????????????AllowAdmin = true, ???????????????ConnectRetry = 10, ???????????????ConnectTimeout = 5000, ???????????????KeepAlive = 30, ???????????????SyncTimeout = 5000, ???????????????EndPoints = { config.RedisHost }, ???????????????ServiceName = config.RedisName, ???????????}; ???????????if (!string.IsNullOrWhiteSpace(config.RedisPass)) ???????????{ ???????????????options.Password = config.RedisPass; ???????????} ???????????return options; ???????} ???????#endregion ???????//============ ???????#region 获取缓存 ???????/// <summary> ???????/// 读取缓存 ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <returns>数据类型/NULL</returns> ???????public static object Get(string key) ???????{ ???????????return Get<object>(key); ???????} ???????/// <summary> ???????/// 读取缓存 ???????/// </summary> ???????/// <typeparam name="T">泛型</typeparam> ???????/// <param name="key">键</param> ???????/// <returns>数据类型/NULL</returns> ???????public static T Get<T>(string key) ???????{ ???????????var value = _db.StringGet(key); ???????????return (value.IsNull ? default(T) : JsonTo<T>(value).Value); ???????} ???????#endregion ???????#region 异步获取缓存 ???????/// <summary> ???????/// 异步读取缓存 ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <returns>object/NULL</returns> ???????public static async Task<object> GetAsync(string key) ???????{ ???????????return await GetAsync<object>(key); ???????} ???????/// <summary> ???????/// 异步读取缓存 ???????/// </summary> ???????/// <typeparam name="T">泛型</typeparam> ???????/// <param name="key">键</param> ???????/// <returns>数据类型/NULL</returns> ???????public static async Task<T> GetAsync<T>(string key) ???????{ ???????????var value = await _db.StringGetAsync(key); ???????????return (value.IsNull ? default(T) : JsonTo<T>(value).Value); ???????} ???????#endregion ???????#region 同步转异步添加[I/O密集] ???????/// <summary> ???????/// 添加缓存 ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="never">是否永久保存[true:是,false:保存10分钟]</param> ???????public static bool Insert(string key, object data, bool never = false) ???????{ ???????????return InsertAsync(key, data, never).Result; ???????} ???????/// <summary> ???????/// 添加缓存 ???????/// </summary> ???????/// <typeparam name="T">泛型</typeparam> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="never">是否永久保存[true:是,false:保存10分钟]</param> ???????/// <returns>添加结果</returns> ???????public static bool Insert<T>(string key, T data, bool never = false) ???????{ ???????????return InsertAsync<T>(key, data, never).Result; ???????} ???????/// <summary> ???????/// 添加缓存 ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="time">保存时间[单位:秒]</param> ???????/// <returns>添加结果</returns> ???????public static bool Insert(string key, object data, int time) ???????{ ???????????return InsertAsync(key, data, time).Result; ???????} ???????/// <summary> ???????/// 添加缓存 ???????/// </summary> ???????/// <typeparam name="T">泛型</typeparam> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="time">保存时间[单位:秒]</param> ???????/// <returns>添加结果</returns> ???????public static bool Insert<T>(string key, T data, int time) ???????{ ???????????return InsertAsync<T>(key, data, time).Result; ???????} ???????/// <summary> ???????/// 添加缓存 ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="cachetime">缓存时间</param> ???????/// <returns>添加结果</returns> ???????public static bool Insert(string key, object data, DateTime cachetime) ???????{ ???????????return InsertAsync(key, data, cachetime).Result; ???????} ???????/// <summary> ???????/// 添加缓存 ???????/// </summary> ???????/// <typeparam name="T">泛型</typeparam> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="cachetime">缓存时间</param> ???????/// <returns>添加结果</returns> ???????public static bool Insert<T>(string key, T data, DateTime cachetime) ???????{ ???????????return InsertAsync<T>(key, data, cachetime).Result; ???????} ???????#endregion ???????#region 异步添加 ???????/// <summary> ???????/// 添加缓存[异步] ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="never">是否永久保存[true:是,false:保存10分钟]</param> ???????/// <returns>添加结果</returns> ???????public static async Task<bool> InsertAsync(string key, object data, bool never = false) ???????{ ???????????return await _db.StringSetAsync(key, ToJson(data), (never ? null : new TimeSpan?(TimeSpan.FromSeconds(_DefulatTime)))); ???????} ???????/// <summary> ???????/// 添加缓存[异步] ???????/// </summary> ???????/// <typeparam name="T">泛型</typeparam> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="never">是否永久保存[true:是,false:保存10分钟]</param> ???????/// <returns>添加结果</returns> ???????public static async Task<bool> InsertAsync<T>(string key, T data, bool never = false) ???????{ ???????????return await _db.StringSetAsync(key, ToJson<T>(data), (never ? null : new TimeSpan?(TimeSpan.FromSeconds(_DefulatTime)))); ???????} ???????/// <summary> ???????/// 添加缓存[异步] ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="time">保存时间[单位:秒]</param> ???????/// <returns>添加结果</returns> ???????public static async Task<bool> InsertAsync(string key, object data, int time) ???????{ ???????????return await _db.StringSetAsync(key, ToJson(data), new TimeSpan?(TimeSpan.FromSeconds(time))); ???????} ???????/// <summary> ???????/// 添加缓存[异步] ???????/// </summary> ???????/// <typeparam name="T">泛型</typeparam> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="time">保存时间[单位:秒]</param> ???????/// <returns>添加结果</returns> ???????public static async Task<bool> InsertAsync<T>(string key, T data, int time) ???????{ ???????????return await _db.StringSetAsync(key, ToJson<T>(data), new TimeSpan?(TimeSpan.FromSeconds(time))); ???????} ???????/// <summary> ???????/// 添加缓存[异步] ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="cachetime">缓存时间</param> ???????/// <returns>添加结果</returns> ???????public static async Task<bool> InsertAsync(string key, object data, DateTime cachetime) ???????{ ???????????return await _db.StringSetAsync(key, ToJson(data), new TimeSpan?(cachetime - DateTime.Now)); ???????} ???????/// <summary> ???????/// 添加缓存[异步] ???????/// </summary> ???????/// <typeparam name="T">泛型</typeparam> ???????/// <param name="key">键</param> ???????/// <param name="data">数据</param> ???????/// <param name="cachetime">缓存时间</param> ???????/// <returns>添加结果</returns> ???????public static async Task<bool> InsertAsync<T>(string key, T data, DateTime cachetime) ???????{ ???????????return await _db.StringSetAsync(key, ToJson<T>(data), new TimeSpan?(cachetime - DateTime.Now)); ???????} ???????#endregion ???????#region 验证缓存 ???????/// <summary> ???????/// 验证缓存是否存在 ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <returns>验证结果</returns> ???????public static bool Exists(string key) ???????{ ???????????return _db.KeyExists(key); ???????} ???????#endregion ???????#region 异步验证缓存 ???????/// <summary> ???????/// 验证缓存是否存在 ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <returns>验证结果</returns> ???????public static async Task<bool> ExistsAsync(string key) ???????{ ???????????return await _db.KeyExistsAsync(key); ???????} ???????#endregion ???????#region 移除缓存 ???????/// <summary> ???????/// 移除缓存 ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <returns>移除结果</returns> ???????public static bool Remove(string key) ???????{ ???????????return _db.KeyDelete(key); ???????} ???????#endregion ???????#region 异步移除缓存 ???????/// <summary> ???????/// 移除缓存 ???????/// </summary> ???????/// <param name="key">键</param> ???????/// <returns>移除结果</returns> ???????public static async Task<bool> RemoveAsync(string key) ???????{ ???????????return await _db.KeyDeleteAsync(key); ???????} ???????#endregion ???????#region 队列发布 ???????/// <summary> ???????/// 队列发布 ???????/// </summary> ???????/// <param name="Key">通道名</param> ???????/// <param name="data">数据</param> ???????/// <returns>是否有消费者接收</returns> ???????public static bool Publish(Models.RedisChannels Key, object data) ???????{ ???????????return _sub.Publish(Key.ToString(), ToJson(data)) > 0 ? true : false; ???????} ???????#endregion ???????#region 队列接收 ???????/// <summary> ???????/// 注册通道并执行对应方法 ???????/// </summary> ???????/// <typeparam name="T">数据类型</typeparam> ???????/// <param name="Key">通道名</param> ???????/// <param name="doSub">方法</param> ???????public static void Subscribe<T>(Models.RedisChannels Key, DoSub doSub) where T : class ???????{ ???????????var _subscribe = connection.GetSubscriber(); ???????????_subscribe.Subscribe(Key.ToString(), delegate (RedisChannel channel, RedisValue message) ???????????{ ???????????????T t = Recieve<T>(message); ???????????????doSub(t); ???????????}); ???????} ???????#endregion ???????#region 退订队列通道 ???????/// <summary> ???????/// 退订队列通道 ???????/// </summary> ???????/// <param name="Key">通道名</param> ???????public static void UnSubscribe(Models.RedisChannels Key) ???????{ ???????????_sub.Unsubscribe(Key.ToString()); ???????} ???????#endregion ???????#region 数据转换 ???????/// <summary> ???????/// JSON转换配置文件 ???????/// </summary> ???????private static JsonSerializerSettings _jsoncfg = new JsonSerializerSettings ???????{ ???????????ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ???????????NullValueHandling = NullValueHandling.Ignore, ???????????Formatting = Formatting.None ???????}; ???????/// <summary> ???????/// 封装模型转换为字符串进行存储 ???????/// </summary> ???????/// <param name="value">值</param> ???????/// <returns></returns> ???????private static string ToJson(object value) ???????{ ???????????return ToJson<object>(value); ???????} ???????/// <summary> ???????/// 封装模型转换为字符串进行存储 ???????/// </summary> ???????/// <typeparam name="T">泛型</typeparam> ???????/// <param name="value">值</param> ???????/// <returns></returns> ???????private static string ToJson<T>(T value) ???????{ ???????????return JsonConvert.SerializeObject(new Models.RedisData<T> ???????????{ ???????????????Value = value ???????????}, _jsoncfg); ???????} ???????/// <summary> ???????/// 缓存字符串转为封装模型 ???????/// </summary> ???????/// <typeparam name="T"></typeparam> ???????/// <param name="value"></param> ???????/// <returns></returns> ???????private static Models.RedisData<T> JsonTo<T>(string value) ???????{ ???????????return JsonConvert.DeserializeObject<Models.RedisData<T>>(value, _jsoncfg); ???????} ???????private static T Recieve<T>(string cachevalue) ???????{ ???????????T result = default(T); ???????????bool flag = !string.IsNullOrWhiteSpace(cachevalue); ???????????if (flag) ???????????{ ???????????????var cacheObject = JsonConvert.DeserializeObject<Models.RedisData<T>>(cachevalue, _jsoncfg); ???????????????result = cacheObject.Value; ???????????} ???????????return result; ???????} ???????#endregion ???????#region 方法委托 ???????/// <summary> ???????/// 委托执行方法 ???????/// </summary> ???????/// <param name="d"></param> ???????public delegate void DoSub(object d); ???????#endregion ???}
二.在starup里注入
AddRedisCacheService是我在RedisServiceExtensions里放的拓展方法,这里用来注入redis的配置,RedisOptionKey是我在预编译变量里放置的key,
对应appsetting.json里配置文件的key
如图:
这里我们通过上一章的ConfigLocator很轻松的就拿到了redisOptions强类型的值
到这里我们基本配置就完成,再说明一下,redisconfig的配置,redisName代表redis库名,redisHost代表链接库地址,redisPass代表密码
三.初级测试
测试代码如下:
public IActionResult Index() ???????{ ???????????var key = "Test_Redis_Key"; ???????????var result= RedisServiceExtensions.Insert(key,"good"); ???????????var value = RedisServiceExtensions.Get(key); ???????????return View(); ???????}
测试结果:
result=true表示写入成功
value=good恰好是我们刚才写的good
初级对reids的测试就是这么简单,客户端连接数据库我就不演示了
四.redis高级测试
高级测试我主要测试pub和sub并且要给大家演示出timeout那个问题,我争取将其复现了!
首先强调一点pub和sub是有通道的,通道大家不陌生吧!
如图:
程序启动,我们先订阅一个通道,本此测试我订阅两个通道,根据有说服力!
subscribe是一个泛型方法,泛型约束的是执行方法的参数类型,有两个入参,一个是通道名称,一个是要执行的委托方法
代码如下:注意我这里将其做成拓展方法,并且开另一个线程执行
/// <summary> ???????/// 注册通道并执行对应方法 ???????/// </summary> ???????/// <typeparam name="T">数据类型</typeparam> ???????/// <param name="serviceCollection"></param> ???????/// <param name="Key">通道名</param> ???????/// <param name="doSub">方法</param> ???????public static IServiceCollection Subscribe<T>(this IServiceCollection serviceCollection,Models.RedisChannels Key, DoSub doSub) where T : class ???????{ ???????????Task.Run(() => ???????????{ ???????????????var _subscribe = connection.GetSubscriber(); ???????????????_subscribe.Subscribe(Key.ToString(), delegate (RedisChannel channel, RedisValue message) ???????????????{ ???????????????????T t = Recieve<T>(message); ???????????????????doSub(t); ???????????????}); ???????????}); ???????????return serviceCollection; ???????}
RedisService.SubscribeDoSomething,RedisService.MemberChannel_SubscribeDoSomething是两个委托方法,我给第一个通道pub,他就执行一次SubscribeDoSomething
给第二个通道pub,他就执行一次MemberChannel_SubscribeDoSomething
这两个方法实现如下:
public class RedisService ???{ ???????public static void SubscribeDoSomething(object query) ???????{ ???????????int num = 0; ???????????Log4Net.Info($"TestPubSub_通道订阅_{num}"); ???????????num += 1; ???????} ???????public static void MemberChannel_SubscribeDoSomething(object query) ???????{ ???????????query= query as string; ???????????int num = 0; ???????????Log4Net.Info($"MemberChannel_SubscribeDoSomething_{query}_{num}"); ???????????num += 1; ???????} ???}
接下来我还是在控制器里调用,进行pub
public IActionResult Index() ???????{ ???????????//发布TestPubSub ???????????var result = RedisServiceExtensions.Publish( Infrastructrue.Caches.Redis.Models.RedisChannels.TestPubSub,null); ???????????//发布MemberRegister ???????????var result2 = RedisServiceExtensions.Publish(Infrastructrue.Caches.Redis.Models.RedisChannels.MemberRegister, "哥就是哥_不一样的烟火..."); ???????????return View(); ???????}
测试结果:
日志上成功记录下来,也就是说,pub出去的东西,sub到,然后执行写了日志!
接下来我让控制器循环执行100次pub,我们让第二个通道的pub100次,第一个通道就pub一次
代码如下:
public IActionResult Index() ???????{ ???????????//发布TestPubSub ???????????var result = RedisServiceExtensions.Publish(Infrastructrue.Caches.Redis.Models.RedisChannels.TestPubSub, null); ???????????for (int i = 0; i < 100; i++) ???????????{ ???????????????//发布MemberRegister ???????????????var result2 = RedisServiceExtensions.Publish(Infrastructrue.Caches.Redis.Models.RedisChannels.MemberRegister, "哥就是哥_不一样的烟火..."); ???????????} ???????????return View(); ???????}
哈哈,太happy了,一次把我要说的那个问题------------timeout的问题测出来了!
如图:
详细信息如下:遇到的肯定是这个问题想都不用想
Timeout performing PUBLISH MemberRegister (5000ms), inst: 24, qs: 0, in: 0, serverEndpoint: 39.107.202.142:6379, mgr: 9 of 10 available, clientName: GY, IOCP: (Busy=0,Free=1000,Min=4,Max=1000), WORKER: (Busy=5,Free=32762,Min=4,Max=32767), v: 2.0.513.63329 (Please take a look at this article for some common client-side issues that can cause timeouts: https://stackexchange.github.io/StackExchange.Redis/Timeouts)
这个问题的根本原因在于我们配置的redis默认等待时间
我现在用的是等待二十秒不行,如果改才600等待10分钟,你的timeout应该就不会出现了!(如有不对请斧正)
这一篇就这样吧,下一篇我会唠唠这个系统的权限管理模块的实现
- 下章管理系统模块实现
[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(三)
原文地址:https://www.cnblogs.com/gdsblog/p/10004615.html