ABP入门系列(13)——Redis缓存用起来

1. 引言

创建任务时我们需要指定分配给谁,Demo中我们使用一个下拉列表用来显示当前系统的所有用户,以供用户选择。我们每创建一个任务时都要去数据库取一次用户列表,然后绑定到用户下拉列表显示。如果就单单对一个demo来说,这样实现也无可厚非,但是在正式项目中,显然是不合理的,浪费程序性能,有待优化。
说到优化,你肯定立马就想到了使用缓存。是的,缓存是提高程序性能的高效方式之一。
这一节我们就针对这一案例来看一看Abp中如何使用缓存来提高程序性能。

2. Abp的缓存机制

在直接使用缓存之前,我们还是来简单梳理下Abp的缓存机制。
Abp之所以能成为一个优秀的DDD框架,我想跟作者详细的文档有很大关系,
作者已经在ABP官方文档介绍了如何使用Caching,英文水平好的就直接看官方的吧。

Abp对缓存进行抽象定义了ICache接口,位于Abp.Runtime.Caching命名空间。
并对ICache提供了默认的实现AbpMemoryCacheAbpMemoryCache是基于MemoryCache的一种实现方式。MemoryCache是微软的一套缓存机制,定义在System.Runtime.Caching命名空间,顾名思义 ,在内存中进行高速缓存。我们通过类型依赖图来看下Abp对Cache的实现:

Abp.Runtime.Caching 类型依赖图

从图中可以看出主要包括四个部分:

  • ICache->CacheBase->AbpMemoryCache:对缓存的抽象以及实现;
  • ITypedCache:缓存的泛型实现;
  • ICacheManager->CacheManagerBase->AbpMemoryCacheManager:缓存管理类的抽象和实现,代码中可以通过注入ICacheManager来获取缓存;
  • ICachingConfiguration->CachingConfiguration:用来配置使用哪种缓存。

3. Abp缓存实操演练

3.1. 定位优化点

定位到我们的TasksController,其中有两种创建Task的Action,代码如下:

 

public PartialViewResult RemoteCreate() {var userList = _userAppService.GetUsers();ViewBag.AssignedPersonId = new SelectList(userList.Items, "Id", "Name");return PartialView("_CreateTaskPartial");
}[ChildActionOnly] 
public PartialViewResult Create() {var userList = _userAppService.GetUsers();ViewBag.AssignedPersonId = new SelectList(userList.Items, "Id", "Name");return PartialView("_CreateTask");
}

可以看到两个方法都需要调用_userAppService.GetUsers();来获取用户列表。
现在我们来使用缓存技术对其优化。首先我们应该想到了Asp.net mvc自带的一套缓存机制,OutputCache。

3.2. 使用[OutputCache]进行缓存

如果对OutputCache不了解,可以参考我的这篇文章Asp.net mvc 知多少(九)。

我们可以简单在Action上添加[OutputCache]特性即可。

 

[OutputCache(Duration = 1200, VaryByParam = "none")]
[ChildActionOnly] 
public PartialViewResult Create() {var userList = _userAppService.GetUsers();ViewBag.AssignedPersonId = new SelectList(userList.Items, "Id", "Name");return PartialView("_CreateTask");
}

[OutputCache(Duration = 1200, VaryByParam = "none")]这句代码的意思是该action只缓存1200s。1200s后,ASP.NET MVC会重新执行action并再次缓存。因为是在[ChildActionOnly]中使用[OutputCache],所以该缓存属于Donut Hole caching。
在该方法内部打个断点,测试只有第一次调用会进入方法内部,之后1200s内都不会再进入该方法,1200s后会再次进入,说明缓存成功!

3.3. 使用ICacheManager进行缓存

按照上面对Abp缓存机制的梳理,我们可以在需要使用缓存的地方注入ICacheManager来进行缓存管理。
现在我们就在TasksController中注入ICacheManager
申明私有变量,并在构造函数中注入,代码如下:

 

private readonly ITaskAppService _taskAppService;
private readonly IUserAppService _userAppService;
private readonly ICacheManager _cacheManager;public TasksController(ITaskAppService taskAppService, IUserAppService userAppService, ICacheManager _cacheManager) {_taskAppService = taskAppService;_userAppService = userAppService;_cacheManager = cacheManager;
}

下面修改RemoteCreateaction如下:

 

public PartialViewResult RemoteCreate()
{   var userList = _cacheManager.GetCache("ControllerCache").Get("AllUsers", () => _userAppService.GetUsers()) as ListResultDto<UserListDto>;ViewBag.AssignedPersonId = new SelectList(userList.Items, "Id", "Name");return PartialView("_CreateTaskPartial");
}

分析代码发现我们在通过上面代码中获取的缓存是需要进行类型转换的。原来_cacheManager.GetCache返回的是ICache类型,而ICache定义key-value对应的是string-object类型,所以自然从缓存获取完数据后要进行类型转换了(注:最新Abp版本为ICache提供了扩展方法,不再需要显示进行类型转换)。那有没有泛型版本?聪明如你,作者对ICache进行包装封装了个ITypedCache以实现类型安全。代码种进行了5种实现,可以一探究竟:

 

public PartialViewResult RemoteCreate()
{//1.1 注释该段代码,使用下面缓存的方式//var userList = _userAppService.GetUsers();//1.2 同步调用异步解决方案(最新Abp创建的模板项目已经去掉该同步方法,所以可以通过下面这种方式获取用户列表)//var userList = AsyncHelper.RunSync(() => _userAppService.GetUsersAsync());//1.3 缓存版本var userList = _cacheManager.GetCache("ControllerCache").Get("AllUsers", () => _userAppService.GetUsers());//1.4 转换为泛型版本//var userList = _cacheManager.GetCache("ControllerCache").AsTyped<string, ListResultDto<UserListDto>>().Get("AllUsers", () => _userAppService.GetUsers());//1.5 泛型缓存版本//var userList = _cacheManager.GetCache<string, ListResultDto<UserListDto>>("ControllerCache").Get("AllUsers", () => _userAppService.GetUsers());ViewBag.AssignedPersonId = new SelectList(userList.Items, "Id", "Name");return PartialView("_CreateTaskPartial");
}

经测试,用户列表正确缓存。

与[OutputCache]相比,我们很自然就会问Abp提供的缓存怎么没有配置缓存过期时间,你想到的框架肯定也想到了,Abp的默认缓存过期时间是60mins,我们可以通过在使用缓存项目的Module(模块)中自定义缓存时间。
因为我们是在Web项目中使用的Cache,所以定位到XxxWebModule.cs,在PreInitialize方法中进行缓存配置。

 

//配置所有Cache的默认过期时间为2小时
Configuration.Caching.ConfigureAll(cache =>
{cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2);
});//配置指定的Cache过期时间为10分钟
Configuration.Caching.Configure("ControllerCache", cache =>
{cache.DefaultSlidingExpireTime = TimeSpan.FromMinutes(10);
});

3.4. 使用IEntityCache对实体进行缓存

3.4.1. 缓存方式的思考

上面的两种缓存方式,我们一般用于存储自定义缓存,但有一个局限性,受到具体缓存过期时间的限制。
思考一下,我们缓存的用户列表,它是一个实时会变化的集合,而这个实时是不定时的,可能1mins之内就有新用户注册,也有可能几天没有用户注册(比如我们这个Demo),这个时候就不好设置缓存过期(刷新)时间。
但由于我们是Demo性质只是为了演示用法,所以我们设定缓存过期时间为10mins也无可厚非。

那有没有一种缓存机制,不需要设置缓存过期时间,当数据变化的时候就能自动重新缓存呢?
答案是肯定的,Abp为我们提供了IEntityCache,实体缓存机制。
当我们需要通过ID获取实体数据而又不想经常去数据库查询时,我们就可以使用IEntityCache
换句话说,IEntityCache支持按实体Id进行动态缓存。

3.4.2. IEntityCache缓存原理

在演示具体操作之前,我们先来讲解下IEntityCache的缓存原理:

  • 首先它第一次从数据库中获取实体,然后后续调用将会从缓存获取。
  • 当实体更新或删除时它自动将缓存的实体置为无效状态,因此它将会再下一次请求中从数据库中重新获取。
  • 它使用缓存的类的完整类名作为缓存名称,可以通过为构造函数传参来修改缓存名称。
  • 它是线程安全的。
  • 它使用IObjectMapper将实体映射到缓存项。 IObjectMapper由AutoMapper模块实现。所以,如果你使用它,你需要AutoMapper模块。您可以覆盖MapToCacheItem方法以手动将实体映射到缓存项。

3.4.3. IEntityCache上手实战

既然是缓存实体,基于我们这个demo,我们就拿Task实体玩一下吧。
在这里我们先要复习下什么是DTO,重申下DDD为什么引入DTO。
Data Transfer Objects(DTO)用来在应用层和展现层之间传输数据。

DTO的必要性:

  1. 领域层的抽象
  2. 数据隐藏
  3. 序列化和延迟加载问题

那这个DTO跟要讲的实体缓存有什么关系呢?
不绕弯子了,就是说实体缓存不应直接对Entity进行缓存,以避免缓存时序列化了不该序列化的对象和实体。
那具体怎么操作呢?我们就直接上Demo吧。
我们定义一个TaskCacheItem,用来缓存Title、Description、State。并定义映射规则[AutoMapFrom(typeof(Task))]

 

namespace LearningMpaAbp.Tasks.Dtos
{[AutoMapFrom(typeof(Task))]public class TaskCacheItem{public string Title { get; set; }public string Description { get; set; }public TaskState State { get; set; }}
}

下面我们定义一个针对TaskCacheItem的缓存接口。

 

namespace LearningMpaAbp.Tasks
{public interface ITaskCache:IEntityCache<TaskCacheItem>{}
}

实现ITaskCache缓存接口:

 

namespace LearningMpaAbp.Tasks
{public class TaskCache : EntityCache<Task, TaskCacheItem>, ITaskCache, ISingletonDependency{public TaskCache(ICacheManager cacheManager, IRepository<Task, int> repository, string cacheName = null) : base(cacheManager, repository, cacheName){}}
}

现在,当我们需要根据TaskId获取Title、Description、State,我们就可以通过在需要的类中注入注入ITaskCache,来从缓存中获取。
下面我们在ITaskAppService中添加一个接口TaskCacheItem GetTaskFromCacheById(int taskId);
然后在TaskAppService中实现它,申明变量并在构造函数注入ITaskCache,实现定义的接口:

 

private readonly ITaskCache _taskCache;/// <summary>
///     In constructor, we can get needed classes/interfaces.
///     They are sent here by dependency injection system automatically.
/// </summary>
public TaskAppService(IRepository<Task> taskRepository, IRepository<User, long> userRepository,ISmtpEmailSenderConfiguration smtpEmialSenderConfigtion, INotificationPublisher notificationPublisher, ITaskCache taskCache)
{_taskRepository = taskRepository;_userRepository = userRepository;_smtpEmialSenderConfig = smtpEmialSenderConfigtion;_notificationPublisher = notificationPublisher;_taskCache = taskCache;
}public TaskCacheItem GetTaskFromCacheById(int taskId)
{return _taskCache[taskId];
}

测试如下,直接在即时窗口调用方法,发现只有一条Sql查询生成,说明实体缓存成功。

即时窗口和跟踪Sql输出

可能读到这里,你可能会问,说好的『Redis缓存用起来』,你讲了半天,跟Redis没有半毛钱关系啊。

Redis这么厉害的技能,当然要压轴出场啊,下面Redis开讲。

4. Redis是什么玩意

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构,如字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)与范围查询、bitmaps、hyperloglogs和地理空间(geospatial)索引半径查询。

官方的解释就是这么拗口,对于初识Redis,我们可以简单把它理解为基于内存的速度非常快性能非常棒的Key-Value数据库。

有一点需要说明,Redis官方仅支持Linux系统不支持Windows系统。
但是呢,微软大法好啊,微软开源技术团队(Microsoft Open Tech group)开发和维护了一个Win64 的版本,我们可以在https://github.com/MSOpenTech/redis上下载Win64版本来玩一玩。

想了解更多,请参考中文官方文档或英文官方文档。

5. 动手试玩Redis

5.1. 安装Redis

打开微软开源技术团队维护的Redis Github链接,找到Releases目录,下载最新版本的msi安装即可。

下载后,一直下一步安装即可。

5.2. 简单试玩

找到安装目录,打开cmd并进入到安装目录,输入redis-server redis.windows.conf,即可启动Redis 服务。Redis服务默认启动在6379端口。

启动Redis Server

再启动一个cmd窗口,执行redis-cli.exe即可开一个Redis客户端。
执行set命令进行缓存设置;
执行get命令进行缓存读取;
执行subscribe命令进行频道监听;
执行publish命令向指定频道发布消息;
具体步骤详参下图:

简单试玩

6. ABP上试玩Redis缓存

跟着我的步伐,对Redis也算有了基本的认识,咱们下面就进入今天的压轴主题,介绍Abp下如何使用redis进行缓存。
首先我们要知道为什么要用Redis进行缓存。
默认的缓存管理是在内存中(in-memory)进行缓存。当你有不止一个并发web服务器需要运行同一个应用程序,默认的缓存管理就不满足你的需求。你可能需要一个分布式/中央缓存服务器来进行缓存管理,这时Redis就可以粉墨登场了。

6.1. Abp集成Redis

首先打开Web层,下载Abp.RedisCache Nuget包安装。
其中要说明的是Abp.RedisCache是依赖StackExchange.RedisNuget包的。

修改XxxWebModule.cs,在DependsOn特性上添加对AbpRedisCacheModule的依赖,并在模块的PreInitialize方法中调用UseRedis扩展方法,代码如下:

 

[DependsOn(typeof(LearningMpaAbpDataModule),typeof(LearningMpaAbpApplicationModule),typeof(LearningMpaAbpWebApiModule),typeof(AbpWebSignalRModule),//typeof(AbpHangfireModule), - ENABLE TO USE HANGFIRE INSTEAD OF DEFAULT JOB MANAGERtypeof(AbpWebMvcModule),typeof(AbpRedisCacheModule))]public class LearningMpaAbpWebModule : AbpModule{public override void PreInitialize(){//省略其他配置代码//配置使用Redis缓存Configuration.Caching.UseRedis();//配置所有Cache的默认过期时间为2小时Configuration.Caching.ConfigureAll(cache =>{cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2);});//配置指定的Cache过期时间为10分钟Configuration.Caching.Configure("ControllerCache", cache =>{cache.DefaultSlidingExpireTime = TimeSpan.FromMinutes(10);});            }....
}

最后一步在Web.Config文件的【connectionStrings】节点为Abp.Redis.Cache添加连接字符串,如下:

 

  <connectionStrings><add name="Default" connectionString="Server=.\sqlexpress; Database=LearningMpaAbp; Trusted_Connection=True;" providerName="System.Data.SqlClient" /><add name="Abp.Redis.Cache" connectionString="localhost"/></connectionStrings>

启动Redis Server后,F5运行web项目,断点调试,发现已经成功应用Redis缓存。
若未启动Redis Server,会报Error:It was not possible to connect to the redis server(s); to create a disconnected multiplexer, disable AbortOnConnectFail. SocketFailure on PING

这样我们就用Redis代替了默认的MemoryCache缓存方案,而不需要改动其它代码,Abp就是这么简单、灵活、松藕合!

7. 总结

这篇文章中主要梳理了Abp中如何进行缓存管理,并简要介绍了Abp中的缓存机制,并与Asp.net mvc自带的[Outputcache]缓存进行简要对比,并进行了缓存管理实战演练。最后对Redis进行了简要介绍,并介绍了如何切换Redis缓存。



作者:圣杰
链接:https://www.jianshu.com/p/241793caa328
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ABP入门系列(14)——应用BootstrapTable表格插件

1. 引言 之前的文章ABP入门系列&#xff08;7&#xff09;——分页实现讲解了如何进行分页展示&#xff0c;但其分页展示仅适用于前台web分页&#xff0c;在后台管理系统中并不适用。后台管理系统中的数据展示一般都是使用一些表格插件来完成的。这一节我们就使用BootstrapTab…

musictools怎么用不了_夏天少不了一只草编包,怎么搭配才不像“买菜用”?

要说有什么包包能跟夏天的气息一拍即合&#xff0c;那绝对非“草编包”莫属&#xff01;由藤条、柳条等编制而成的田园风“小篮子”&#xff0c;已经成了夏日街头小姐姐们的包包首选。各大品牌都争相推出草编包系列&#xff0c;在原有的浪漫度假风之外&#xff0c;添加了更多时…

ABP入门系列(15)——创建微信公众号模块

1. 引言 现在的互联网已不在仅仅局限于网页应用&#xff0c;IOS、Android、平板、智能家居等平台正如火如荼的迅速发展&#xff0c;移动应用的需求也空前旺盛。所有的互联网公司都不想错过这一次移动浪潮&#xff0c;布局移动市场分一份移动红利。 的确&#xff0c;智能手机作…

spark 算子例子_Spark性能调优方法

公众号后台回复关键词&#xff1a;pyspark&#xff0c;获取本项目github地址。Spark程序可以快如闪电⚡️&#xff0c;也可以慢如蜗牛?。它的性能取决于用户使用它的方式。一般来说&#xff0c;如果有可能&#xff0c;用户应当尽可能多地使用SparkSQL以取得更好的性能。主要原…

ABP入门系列(16)——通过webapi与系统进行交互

1. 引言 上一节我们讲解了如何创建微信公众号模块&#xff0c;这一节我们就继续跟进&#xff0c;来讲一讲公众号模块如何与系统进行交互。 微信公众号模块作为一个独立的web模块部署&#xff0c;要想与现有的【任务清单】进行交互&#xff0c;我们要想明白以下几个问题&#x…

python嵩天第二版第五章_如何避免从入门到放弃——python小组学习复盘

2019年春节python学习行动复盘2019-02-09为了主攻python&#xff0c;没有参加心理学晨读。对心理学也不敢兴趣&#xff0c;怕耽误学习python的时间。那么没学习心理学的情况下&#xff0c;python学的怎么样&#xff1f;是否达到自己的预期&#xff1f;一、预期目标&#xff1a;…

ABP入门系列(17)——使用ABP集成的邮件系统发送邮件

1.Abp集成的邮件模块是如何实现的 ABP中对邮件的封装主要集成在Abp.Net.Mail和Abp.Net.Mail.Smtp命名空间下&#xff0c;相应源码在此。 分析可以看出主要由以下几个核心类组成&#xff1a; EmailSettingNames&#xff1a;静态常量类&#xff0c;主要定义了发送邮件需要的相关…

cdn转发防攻击_高防CDN和高防服务器的区别?

越来越多的网络攻击需要处理&#xff0c;而高防CDN和高防服务器是很好的选择&#xff0c;那么如何选择呢&#xff1f;我们就来分析一下关于这两者之间的选择。首先从价格上看的话&#xff0c;高防御CDN的价格相对高一些&#xff0c;防御上看&#xff0c;高防御CDN的防御效果也更…

ABP入门系列(18)—— 使用领域服务

1.引言 自上次更新有一个多月了&#xff0c;发现越往下写&#xff0c;越不知如何去写。特别是当遇到DDD中一些概念术语的时候&#xff0c;尤其迷惑。如果只是简单的去介绍如何去使用ABP&#xff0c;我只需参照官方文档&#xff0c;实现到任务清单Demo中去就可以了&#xff0c;…

mysql文件类型_MyCat教程:实现MySql主从复制

原文&#xff1a;http://iii75.cn/mwQhBW 作者&#xff1a;波波烤鸭历史相关文章Mycat入门教程单个mysql数据库在处理业务的时候肯定是有限的&#xff0c;这时我们扩展数据库的第一种方式就是对数据库做读写分离(主从复制),本文我们就先来介绍下怎么来实现mysql的主从复制操作。…

截屏当前界面_电脑屏幕怎么截取,常见的几种电脑截屏方法

随着科技的快速发展电脑已经逐渐渗入到我们的工作和生活中&#xff0c;我们需要使用电脑的地方也越来越多&#xff0c;电脑已经成为了一种新式的办公工具。今天小编不是向大家介绍电脑的应用&#xff0c;而是想要和大家分享一下关于电脑截图的几种方法。1、Print Screen SysRqP…

ABP入门系列(19)——使用领域事件

1.引言 最近刚学习了下DDD中领域事件的理论知识&#xff0c;总的来说领域事件主要有两个作用&#xff0c;一是解耦&#xff0c;二是使用领域事件进行事务的拆分&#xff0c;通过引入事件存储&#xff0c;来实现数据的最终一致性。若想了解DDD中领域事件的概念&#xff0c;可参…

扩容是元素还是数组_Java中对数组的操作

数组对于每一门编程语言来说都是重要的数据结构之一&#xff0c;当然不同语言对于数组的实现及处理也不尽相同。Java语言中提供的数组是用来存储固定大小的同类型元素。如&#xff1a;声明一个数组变量&#xff0c;numbers[100]来代替直接声明100个独立变量number0,number1,...…

ABP入门系列(20)——使用后台作业和工作者

1.引言 说到后台作业&#xff0c;你可能条件反射的想到BackgroundWorker&#xff0c;但后台作业并非是后台任务&#xff0c;后台作业用一种队列且持久稳固的方式安排一些待执行后台任务。 为执行长时间运行的任务而用户无需等待&#xff0c;以提高用户体验。为创建可重试且持…

加载中_GIS地图在项目中的加载显示

下面我们就来说说如何在应用程序中加载显示GIS地图&#xff0c;首先我们在SuperMap iDesktop 9D(10i)中编辑好我们需要的地图&#xff0c;如下图所示&#xff1a;如上图所示&#xff0c;这是我编辑好的一幅天河区的地图&#xff0c;下面我就以这幅地图为例来说说如何把这样一幅…

ABP入门系列(21)——切换MySQL数据库

1. 引言 Abp支持MySql已经不是什么新鲜事了&#xff0c;但按照官方文档&#xff1a;Entity Framework - MySql Integration来&#xff0c;你未必能成功切换&#xff0c;本文就记录下切换MySql数据库遇到的一些坑&#xff0c;供后人乘凉&#xff01; 2. 环境准备 MySql数据库…

ABP开发框架前后端开发系列---(1)框架的总体介绍

ABP是ASP.NET Boilerplate的简称&#xff0c;ABP是一个开源且文档友好的应用程序框架。ABP不仅仅是一个框架&#xff0c;它还提供了一个最徍实践的基于领域驱动设计(DDD)的体系结构模型。学习使用ABP框架也有一段时间了,一直想全面了解下这个框架的整个来龙去脉&#xff0c;并把…

c++ pdflib输出表格_DescrTab2包,输出SCI级别的描述统计表

今天浏览R包&#xff0c;发现一个不错的包——DescrTab2包。看R包介绍&#xff0c;这个包可以绘制出版物质量级别的描述统计表。看起来很不错。下面来学习下。1. R包安装和加载install.packages("DescrTab2") # 安装包library(DescrTab2) # 加载包2. 加载演示数据集l…

服务器怎么控制忽略样式_使用JavaScript来编写你的CSS样式代码——JSS

介绍JSS是CSS的创作工具&#xff0c;它允许你使用JavaScript以声明&#xff0c;无冲突和可重用的方式描述样式。它可以在浏览器&#xff0c;服务器端或在构建时在Node中编译。JSS与框架无关。它由多个包组成&#xff1a;核心部分&#xff0c;插件以及框架集成等。Githubhttps:/…

Java设计模式、框架、架构、平台之间的关系

1、设计模式 为什么要先说设计模式?因为设计模式在这些概念中是最基本的&#xff0c;而且也比较简单。那么什么是设计模式呢?说的直白点&#xff0c;设计模式就是告诉你针对特定问题如何组织类、对象和接口之间的关系&#xff0c;是前人总结的经验。比如我要在代码中实现一个…