Round缓存管理器RoundCacheManager--ESBasic 可复用的.NET类库(26)

简介: 1.缘起:     在增量自动获取器章节的缘起部分,我们曾提到增量缓存,本节我们将深入探讨它以及用于管理增量缓存的管理器。我们还是以增量自动获取器章节提到的例子作为基础,并做更进一步的讨论。       OK,现在让我们开始这有趣的旅程。

1.缘起:

    增量自动获取器章节的缘起部分,我们曾提到增量缓存,本节我们将深入探讨它以及用于管理增量缓存的管理器。我们还是以增量自动获取器章节提到的例子作为基础,并做更进一步的讨论。

      OK,现在让我们开始这有趣的旅程。

首先,基于前面例子给出的上下文,我们知道IIncreaseAutoRetriever获取的增量是用于累积当天的已成交订单报表的。“当天已成交报表”就是一个典型的增量缓存,每当有新的增量到来,都会累加到上面。

我们假设今天是2009.07.08,那么我们可以将当前的增量缓存的ID标记为20090708,每隔一分钟都会有新的增量累加到该增量缓存实例中去。直到时钟指向2009.07.0900:00:00时,表示当前这一天已经结束,此时,将创建一个新的ID20090709的增量缓存实例来容纳新的增量。而ID20090708的增量缓存实例将不会再有新的增量进来,所以,其内容也将不再发生变化。此时,我们可以将ID20090708的增量缓存实例序列化后进行存储。这样做的好处是,当以后任何时候我们需要查询2009.07.08这一天的报表时,可以直接从存储设备加载20090708的增量缓存到内存中并反序列化,然后返回结果,这样就不用再去重新统计这一天的所有已成交的订单了。

所以,当时间进入2009.07.09,那么ID20090708的增量缓存实例就变成了一个历史缓存,而标记为20090709的新缓存会处理新来的增量。如此,我们可以使用一个管理器将所有的历史缓存和当前正在运行的缓存管理起来。我设计的ESBasic.ObjectManagement.Increasing.Management.RoundCacheManager就是为的这个目的。

  Round缓存管理器的形象示意图如下:

  

 

2.适用场合:

       RoundCacheManager是依赖于IIncreaseAutoRetriever的,所以如果要使用RoundCacheManager,首先必须满足IIncreaseAutoRetriever所需的条件,另外还有RoundCacheManager需要的额外增强的条件。

(1)需要定时从数据源获取新的增量数据。

(2)数据源可能不只一个。

(3)需要支持“轮”(Round)的概念。在Round切换时,需要能够准确识别增量断点。

(4)增量数据有某个字段是递增的。

(5)需要将获取的增量数据放入到正确的增量缓存。

(6)Round发生切换时,需要产生新的增量缓存来接收新的增量。而历史的增量缓存也需要被管理起来。

(7)被管理的历史缓存在其过期的时候,会自动从管理器中移除。

 

3.设计思想与实现

在分析RoundCacheManager的源码之前,我们先将其会涉及到的一些重要概念说明一下。

    由于增量缓存是基于Round的,比如上面提到的一天为一个Round,所以,在ESBasic中称这样的缓存为Round Cache,如果是当前正在使用的增量缓存,则称为Round Increasing Cache

    Round的切换点到来时,当前的Round Increasing Cache就会演变成一个属于历史的Round Cache    

首先,ESBasic要求Round Cache必须实现IRoundCache泛型接口,IRoundCache接口定义如下:

    ///   <summary>
    
///  IRoundCache 某一Round的完整的数据缓存。用于被序列化存储。
    
///   </summary>
     public   interface   IRoundCache < TRoundID >
    {
        TRoundID RoundID { 
get ; }
    }

 

该接口的泛型参数TRoundID用于抽象Round ID的类型,比如我们前面例子中提到的20090708,其TRoundID就是一个整数类型。

IRoundCache接口只有一个RoundID属性,用于返回该Round CacheID

    一般来说,我在实现IRoundCache接口时,会将实现类标记为可序列化的,这是为我们前面提到的“可以将历史的Round Cache序列化后进行存储”做准备的。

    当前正在使用的Round Cache必须实现IRoundIncreasingCache泛型接口,其定义的源码如下:

    ///   <summary>
    
///  IRoundIncreasingCache 用于存储当前Round数据的增量缓存
    
///   </summary>
     public   interface   IRoundIncreasingCache < TRoundID, TRoundCache, TObject >   where  TRoundCache :  IRoundCache < TRoundID >
    {
        TRoundID RoundID { 
get ; }

        
void  AddIncreasement( IList < TObject >  list);

        
///   <summary>
        
///  CreateRoundCache 当需要序列化存储当前Cache时,先转化为轻量的TRoundCache对象,然后再进行存储。
        
///   </summary>         
        TRoundCache CreateRoundCache();
    }

 

    该接口有三个泛型参数:TRoundIDTRoundCache TObject。其中TRoundID TObject我们已经很熟悉了,而TRoundCache就是实现了IRoundCache接口的类型。

      IRoundIncreasingCache接口的RoundID属性与IRoundCache接口的同名属性是一样的含义,这表明当前正在使用的增量缓存也有自己的唯一ID

      AddIncreasement方法用于将获取到的增量数据累加到IRoundIncreasingCache中。

      CreateRoundCache方法通常在Round切换点出现并完成当前Round的最后一次增量累积时,用于将IRoundIncreasingCache转化为一个历史的Round Cache,这样便可将其序列化存储。 

    从存储设备加载历史的Round Cache以及将Round Cache序列化存储的工作是由IRoundCachePersister接口来完成的,其定义如下:

    ///   <summary>
    
///  IRoundCachePersister 用于持久化或加载RoundCache。
    
///   </summary>    
     public   interface   IRoundCachePersister < TRoundID, TRoundCache >   where  TRoundCache :  IRoundCache < TRoundID >
    {
        
///   <summary>
        
///  Persist 注意,该方法不得抛出异常。
        
///   </summary>        
         void  Persist(TRoundCache roundCache);

        
void  Delete(TRoundID roundID);

        
IDictionary < TRoundID, TRoundCache >  LoadCaches( int  maxHistoryCountInMemory);
    }

   该接口的Persist方法用于将一个Round Cache持久化到存储设备中;Delete方法用于从存储设备删除指定ID Round Cache记录;LoadCaches用于从存储设备加载离当前最近的NRound Cache 

 

接下来,我们将注意力转移到本节的主角RoundCacheManager上来,RoundCacheManager的工作职责包含以下几个方面:

(1)初始化时,RoundCacheManager会做以下几个动作:首先,从存储设备加载一定数量的历史Round Cache到内存中并管理起来。其次,创建一个属于当前的Round Increasing Cache来处理即将到来的增量数据。

(2)Round切换点到来时,将当前的Round Increasing Cache转化为一个历史的Round Cache,并将其序列化存储。然后创建一个新的Round Increasing Cache来处理后续的增量数据。

(3)如果管理器中的某些历史Round Cache不再被需要,则会从管理器中移除。

 

RoundCacheManager的类图如下所示:   

       

  我将RoundCacheManager的实现要点罗列如下:

(1)CurrentRoundCache只读属性用于暴露当前正在工作的增量缓存。

(2)LastRefreshTime属性记录了最后一次获取增量并完成增量累积的时间。

(3)MaxHistoryCountInMemory用于指示在内存中最多保存多少个历史的Round Cache。在Round切换点时,将会产生新的历史Round Cache,这时如果历史Round Cache的数量超过MaxHistoryCountInMemory,则管理器会从内存中移除最老的那个Round Cache

(4)Round切换点发生时,管理器将触发NewRoundStarted事件以通知相关预订者。

(5)AddRoundCache方法和RemoveRoundCache方法用于操作管理器中的历史Round Cache。这两个方法都有一个bool参数,用于指示是否要针对存储设备做相应的动作。

(6)GetHistoryRoundCache用于返回管理器中存在的某个历史Round Cache

   如果要获取当前正在工作的Round Cache,应该直接使用CurrentRoundCache属性。如果目标Round Cache在管理器中不存在,GetHistoryRoundCache将返回null。为避免返回null,可以先调用ContainsHistory以查明目标是否存在于管理器中。

 

       RoundCacheManager是一个abstract类,其有三个abstract方法留给派生类去实现。这三个方法的实现取决于具体应用的需求。

       CreateNewRoundIncreasingCache方法用于创建一个当前正使用的增量缓存。该方法之所以要设计为abstract,是因为在RoundCacheManager中,我们并不知道该如何创建一个TRoundIncreasingCache对象,它有哪些构造函数、需要些什么参数等等,我们对此一无所知。

       GetNextRoundID方法用于根据当前的Round ID创建下一个Round ID。这也是由具体应用来决定的。

       GetExpiredHistoryList方法会在每次管理器中的历史Round Cache的数量发生增加时被调用,RoundCacheManager会依据该方法返回的列表来移除那些过期的历史Round Cache

 

4. 使用时的注意事项

(1)     RoundCacheManager以基类被继承的方式提供复用。RoundCacheManager搭建起了一个关于增量数据的获取、增量缓存的累积、Round的自动切换、增量缓存的自动持久化这样一个流程的完整骨架。使用者只需要从其继承并override几个抽象方法便可复用这样的处理流程。

(2)     通常来说,RoundCacheManager的继承者除了override几个抽象方法外,还会提供一系列的与应用相关的查询方法给外部使用。比如,在我们缘起部分的例子中,我们可以在派生类中设计一个类似下面的方法来提供对某个用户某段时间内的报表数据进行查询:

   ReportData  GetUserReportData( string  userID,  int  startDate,  int  endDate);

(3)     本模型的驱动源在于IIncreaseAutoRetriever的循环引擎,如果其抛出异常而停止运行,那么在此同时,本RoundCacheManager也就丧失了驱动力,后续将再无任何增量进入,Round切换事件NewRoundStarted将永远也不会再触发。

(4)     外部在调用RoundCacheManagerAddRoundCacheRemoveRoundCache方法时要捕获异常,因为持久化过程中可能会出错。

(5)     同介绍IIncreaseAutoRetriever时同样的道理,如果IIncreaseAutoRetriever抛出异常导致引擎停止运行,那么最好的方法就是重启应用程序。当然,这种情况发生的可能性是非常非常微小的。

 

5.扩展

  Round缓存管理器RoundCacheManager暂时没有任何扩展。

 

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

 

目录
相关文章
|
缓存 算法 安全
精选10款C#/.NET开发必备类库(含使用教程),工作效率提升利器!
精选10款C#/.NET开发必备类库(含使用教程),工作效率提升利器!
492 12
|
机器学习/深度学习 人工智能 Cloud Native
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台。本文深入解析 .NET 的核心优势,探讨其在企业级应用、Web 开发及移动应用等领域的应用案例,并展望未来在人工智能、云原生等方面的发展趋势。
274 3
|
XML 开发框架 数据格式
.Net Core 开发框架,支持多版本的类库
.Net Core 开发框架,支持多版本的类库
353 0
|
8月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
3月前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
|
4月前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
240 1
Redis专题-实战篇二-商户查询缓存
|
3月前
|
缓存 运维 监控
Redis 7.0 高性能缓存架构设计与优化
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Redis 7.0高性能缓存架构,探索函数化编程、多层缓存、集群优化与分片消息系统,用代码在二进制星河中谱写极客诗篇。
|
8月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
1200 0
|
4月前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。
|
8月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
285 32

热门文章

最新文章