[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(三)

接着上一章节继续唠唠

本章主要说一下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应该就不会出现了!(如有不对请斧正)

  这一篇就这样吧,下一篇我会唠唠这个系统的权限管理模块的实现

  • 下章管理系统模块实现

  

原文地址:https://www.cnblogs.com/gdsblog/p/10004615.html

时间: 2024-10-10 21:04:52

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(三)的相关文章

【ASP.NET Core快速入门】(二)部署到IIS

原文:[ASP.NET Core快速入门](二)部署到IIS 配置IIS模块 ASP.NET Core Module载地址:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/aspnet-core-module?tabs=aspnetcore2x 安装后再IIS管理器模块页面会出现aspnetcoremodule 新建网站 修改应用池 网站发布 控制台方式 dotnet publish发布到项目文件的bin/deb

ASP.NET Core Web Api之JWT刷新Token(三)

原文:ASP.NET Core Web Api之JWT刷新Token(三) 前言 如题,本节我们进入JWT最后一节内容,JWT本质上就是从身份认证服务器获取访问令牌,继而对于用户后续可访问受保护资源,但是关键问题是:访问令牌的生命周期到底设置成多久呢?见过一些使用JWT的童鞋会将JWT过期时间设置成很长,有的几个小时,有的一天,有的甚至一个月,这么做当然存在问题,如果被恶意获得访问令牌,那么可在整个生命周期中使用访问令牌,也就是说存在冒充用户身份,此时身份认证服务器当然也就是始终信任该冒牌访问令

ASP.NET Core快速入门(Jessetalk)(第2章:配置管理)

课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 任务9:配置介绍 命令行配置 Json文件配置 从配置文件文本到c#对象实例的映射 - Options 与 Bind 配置文件热更新 框架设计:Configuration 任务10:命令行配置 新建一个项目CommandLineSample--控制台应用(.NET Core) 依赖性右键--管理NuGet程序包--下载microsoft.aspnetcore.all 传入参数

ASP.NET Core快速入门(Jessetalk)(第三章:依赖注入)

课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 任务16:介绍 1.依赖注入概念详解 从UML和软件建模来理解 从单元测试来理解 2.ASP.NET Core 源码解析 任务17:从UML角度来理解依赖 1.什么是依赖 当一个类A完成某个任务需要另一个类B来帮助时,A就对B产生了依赖 例如CustomerController需要对customer进行新增或查找时用到EF,则对EF的Context产生了依赖 var cont

【ASP.NET Core快速入门】(一)环境安装

下载.NET Core SDK 下载地址:https://www.microsoft.com/net/download/windows https://www.microsoft.com/net/learn/get-started/windows 安装vs2017,安装的时候选择安装core跨平台 在控制台创建ASP.NET Core应用程序 在程序安装后,可以在控制台输入dotnet进行创建core应用程序 输入dotnet  --help查看命令帮助 .NET 命令行工具 (2.1.2) 使

ASP.NET Core 打造一个简单的图书馆管理系统(五)初始化书籍信息

前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?view=aspnetcore-2.1&tabs=windows <Pro ASP.NET MVC 5>.<锋利的 jQuery> 此系列皆使用 VS2017+C# 作为开发环境.如果有什么问题或者意见欢迎在留言区进行留言.

创建基于ASP.NET core 3.1 的RazorPagesMovie项目(三)-已搭建基架的Razor页面解释和更新

本节主要介绍在上一节中通过搭建基架而创建的Razor页面,并做一些UI改变. 一.创建.删除.详细信息和编辑页面 1.双击Pages/Movies/Index.cshtml.cs文件,这是一个Razor页面模型: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Microsoft.AspNetCore.Mvc; 6 us

asp.net core 拦击器制作的权限管理系统DEMO

效果图 没有登陆不会执行请求日期的方法,不管是否登陆都不允许访问请求时间方法 验证不通过是会进行转发到Home/error方法中, 唯一有点遗憾的是会进行两次请求,而不是一次. 代码附上: [Route("[controller]/[action]")] public class HomeController : BaseController { /// <summary> /// Ajax请求页面 /// </summary> /// <param na

ASP.NET Core 打造一个简单的图书馆管理系统(九) 学生信息增删(终章)

前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?view=aspnetcore-2.1&tabs=windows <Pro ASP.NET MVC 5>.<锋利的 jQuery> 当此系列文章写完后会在一周内推出修正版. 此系列皆使用 VS2017+C# 作为开发环境.如果