定时刷新缓存管理器 IRefreshableCacheManager --ESBasic 可复用的.NET类库(16)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 1.缘起:     为了提升系统的性能或减轻数据库的压力等原因,我们经常在系统中使用缓存来把那些经常使用的数据保留在内存中。如果因为某些原因,缓存中这些经常使用的数据不能及时与数据源进行同步更新,那么采用定时刷新缓存中的数据有可能就是一种合适的选择。

1.缘起:

    为了提升系统的性能或减轻数据库的压力等原因,我们经常在系统中使用缓存来把那些经常使用的数据保留在内存中。如果因为某些原因,缓存中这些经常使用的数据不能及时与数据源进行同步更新,那么采用定时刷新缓存中的数据有可能就是一种合适的选择。

    如果你的缓存是定时刷新,那么你就需要自己为其维护一个定时器或循环引擎。如果你的系统中像这样定时刷新的缓存有多个,而且每个缓存定时刷新的时间间隔又要求不一样,那么,使这些缓存按照你预想的情况进行运转,你就需要花费一些气力。

    我设计了定时刷新缓存管理器IRefreshableCacheManager来帮助你管理你系统中所有需要进行定时刷新的缓存。它可以根据每个缓存的刷新时间间隔要求,在正确的时刻,刷新其所管理的缓存。你不用再自己手动去处理定时器或循环引擎,IRefreshableCacheManager自动为你完成这一切。

 

2.适用场合:

(1)系统中需要使用一个或多个需要进行定时刷新的缓存。

(2)每个缓存所要求的刷新时间间隔可能都不一样。

(3)刷新的时间间隔并不要求精确。

 

3.设计思想与实现

    本节所讲述的是定时刷新缓存管理器IRefreshableCacheManager,其重点在于“管理器”,而不是在于“缓存”,这点是必须清楚的。缓存只是被管理器管理的对象。

能够被IRefreshableCacheManager管理的缓存必须是可定时刷新的缓存,也就是说,这些缓存必须实现IRefreshableCache接口以表明自己可以接受管理器的管理。IRefreshableCache定义如下:

    public   interface   IRefreshableCache
    {
        
///   <summary>
        
///  RefreshSpanInSecs 定时刷新的时间间隔(秒)。如果设置为0,则表示与IRefreshableCacheManager的刷新时间统一。
        
///   </summary>
         int  RefreshSpanInSecs {  get ; }

        
///   <summary>
        
///  LastRefreshTime 最后一次刷新时间。
        
///   </summary>
         DateTime  LastRefreshTime {  get set ; }        
        
        
void  Refresh();       
    }

这个接口相当简单,它只要求缓存提供Refresh方法进行刷新,并通过RefreshSpanInSecs属性表示出自己希望进行定时刷新的时间间隔。如果这个属性设置为0(默认值),则表示该缓存接受管理器的统一调度。

LastRefreshTime属性用于记录最后一次刷新结束的时间,该属性的值由管理器进行设置。

我们从IRefreshableCache接口的定义看到,管理器不用关心与缓存中的数据相关的任何信息,这是由具体的应用在实现IRefreshableCache接口时才指定的。

接下来,我们看看IRefreshableCacheManager接口的定义:

    public   interface   IRefreshableCacheManager
    {
        
///   <summary>
        
///  RefreshSpanInSecs 定时刷新缓存的时间间隔。
        
///   </summary>
         int  RefreshSpanInSecs {  set ; }

        
IList < IRefreshableCache >  CacheList {  set ; }

        
void  Initialize();

        
///   <summary>
        
///  RefreshNow 手动刷新被管理的所有缓存。
        
///   </summary>
         void  RefreshNow();

        
///   <summary>
        
///  AddCache 动态添加缓存。
        
///   </summary>        
         void  AddCache( IRefreshableCache  cache);

        
///   <summary>
        
///  RemoveCache 动态移除缓存。
        
///   </summary>        
         void  RemoveCache( IRefreshableCache  cache);      

        
///   <summary>
        
///  CacheRefreshFailed 当某个缓存刷新抛出异常时,将触发该事件。
        
///   </summary>
         event   CbCacheException  CacheRefreshFailed;
    }

这个接口也有一个RefreshSpanInSecs属性,如果被管理的缓存的RefreshSpanInSecs属性设置的是0,那么管理器将用自身的这个属性对其进行调度。IRefreshableCacheManager接口的RefreshSpanInSecs属性是为了简化那种被管理的所有缓存都采用统一刷新时间间隔的情况而存在的。

      RefreshableCacheManager在实现时仍然是借助前面介绍的循环引擎AgileCycleEngine来进行定时控制的。

IRefreshableCacheManager提供了AddCacheRemoveCache方法用于在运行中动态的添加或移除缓存。

当某个缓存在刷新时,抛出异常,则IRefreshableCacheManager会触发CacheRefreshFailed事件,事件参数包含了出现异常的缓存和异常对象。

 

    关于RefreshableCacheManager的实现,要注意以下几点:

(1)管理器的实现是线程安全的,可以使用于多线程的环境中。我们对其内部的缓存列表进行了加锁控制。

(2)管理器在初始化(Initialize)时,启动了循环引擎(其DetectSpanInSecs必须设置为1秒),而且,将所有被管理的缓存的LastRefreshTime都设置为当前时间。

(3)在实现EngineAction方法时,必须在foreach块中使用try来捕捉引擎刷新时抛出的异常,而不是在try块中进行foreach。这个顺序是重要的,如果在try块中进行foreach,那么当一个缓存在刷新时抛出异常而导致foreach中断后,后续缓存的刷新方法都将不会被检测和调用。

(4)使用锁不仅仅是为了同步对内部缓存列表集合的修改,手动调用刷新方法RefreshNow也需要被同步,否则就可能出现两个线程同时进行检测和刷新缓存的情况(一个是循环引擎的线程,另一个是手动调用RefreshNow方法的线程)。

 

4. 使用时的注意事项

(1)由于管理器内部实现采用了循环引擎,所以定时刷新的时间间隔不可能很精确,而且,针对每个缓存的刷新方法的顺序调用也是导致这种不精确的另一个原因。

(2)也由于管理器内部实现采用了循环引擎,循环引擎能设置的最小检测时间间隔为1秒,所以缓存的刷新时间间隔也不可能小于1秒。

(3)如果某个缓存在刷新时抛出异常,那么其LastRefreshTime属性还是记录的上一次成功刷新的时间。

 

5.扩展

     定时刷新缓存管理器通过事件暴露缓存刷新失败的通知,当缓存刷新发生异常时,我们可能需要将异常记录到日志中。如果是这样,那就可以直接使用ESBasic提供的RefreshableCacheExceptionLogBridge来完成。

       RefreshableCacheExceptionLogBridge借助ESBasic.Logger.IAgileLogger组件来将异常的详细信息记录到目标日志中。日志可以是文本文件,也可以是数据库等其他存储。ESBasic提供了IAgileLogger接口的实现FileAgileLogger,用于将日志写入文本文件中。

 

注: ESBasic已经开源,点击这里下载源码。
    
ESBasic开源前言

 

相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
目录
相关文章
|
4月前
|
存储 缓存
.NET 6中Startup.cs文件注入本地缓存策略与服务生命周期管理实践:AddTransient, AddScoped, AddSingleton。
记住,选择正确的服务生命周期并妥善管理它们是至关重要的,因为它们直接影响你的应用程序的性能和行为。就像一个成功的建筑工地,工具箱如果整理得当,工具选择和使用得当,工地的整体效率将会大大提高。
161 0
|
缓存 JavaScript
vue使用keep-alive实现页面前进刷新,后退缓存,完美运行无bug
vue使用keep-alive实现页面前进刷新,后退缓存,完美运行无bug
1424 122
|
SQL 缓存 开发框架
分享一个 .NET EF6 应用二级缓存提高性能的方法
分享一个 .NET EF6 应用二级缓存提高性能的方法
140 0
|
10月前
|
缓存 算法 安全
精选10款C#/.NET开发必备类库(含使用教程),工作效率提升利器!
精选10款C#/.NET开发必备类库(含使用教程),工作效率提升利器!
278 12
|
11月前
|
机器学习/深度学习 人工智能 Cloud Native
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台。本文深入解析 .NET 的核心优势,探讨其在企业级应用、Web 开发及移动应用等领域的应用案例,并展望未来在人工智能、云原生等方面的发展趋势。
209 3
|
缓存 NoSQL Java
【Azure Redis 缓存 Azure Cache For Redis】Redis出现 java.net.SocketTimeoutException: Read timed out 异常
【Azure Redis 缓存 Azure Cache For Redis】Redis出现 java.net.SocketTimeoutException: Read timed out 异常
151 5
|
缓存 NoSQL 网络协议
【Azure Redis 缓存】Redisson 连接 Azure Redis出现间歇性 java.net.UnknownHostException 异常
【Azure Redis 缓存】Redisson 连接 Azure Redis出现间歇性 java.net.UnknownHostException 异常
339 1
|
存储 缓存 监控
DNS 定期刷新缓存
【8月更文挑战第18天】
772 2
|
缓存 程序员
封装一个给 .NET Framework 用的内存缓存帮助类
封装一个给 .NET Framework 用的内存缓存帮助类
136 1
|
缓存 开发框架 .NET
看看 Asp.net core Webapi 项目如何优雅地使用内存缓存
看看 Asp.net core Webapi 项目如何优雅地使用内存缓存
252 1