眉山北京网站建设,申请域名是什么意思,php旅游网站cms,wordpress tax1. 引言
创建任务时我们需要指定分配给谁#xff0c;Demo中我们使用一个下拉列表用来显示当前系统的所有用户#xff0c;以供用户选择。我们每创建一个任务时都要去数据库取一次用户列表#xff0c;然后绑定到用户下拉列表显示。如果就单单对一个demo来说#xff0c;这样实…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 ListResultDtoUserListDto;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).AsTypedstring, ListResultDtoUserListDto().Get(AllUsers, () _userAppService.GetUsers());//1.5 泛型缓存版本//var userList _cacheManager.GetCachestring, ListResultDtoUserListDto(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 ObjectsDTO用来在应用层和展现层之间传输数据。
DTO的必要性
领域层的抽象数据隐藏序列化和延迟加载问题
那这个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:IEntityCacheTaskCacheItem{}
}实现ITaskCache缓存接口 namespace LearningMpaAbp.Tasks
{public class TaskCache : EntityCacheTask, TaskCacheItem, ITaskCache, ISingletonDependency{public TaskCache(ICacheManager cacheManager, IRepositoryTask, 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(IRepositoryTask taskRepository, IRepositoryUser, 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添加连接字符串如下 connectionStringsadd nameDefault connectionStringServer.\sqlexpress; DatabaseLearningMpaAbp; Trusted_ConnectionTrue; providerNameSystem.Data.SqlClient /add nameAbp.Redis.Cache connectionStringlocalhost//connectionStrings启动Redis Server后F5运行web项目断点调试发现已经成功应用Redis缓存。 若未启动Redis Server会报ErrorIt 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 来源简书 著作权归作者所有。商业转载请联系作者获得授权非商业转载请注明出处。