写自己的缓存框架,JAD-CACHE架构设计篇

在之前一篇《写一个自己的通用缓存框架,以同时支持ehcache、mecache以及springcache注解等等》博文中,列出了自己的通用缓存框架需要实现在的大致功能总结如下:

1、提供统一的缓存操作api;

2、支持同时使用多种缓存实现;

3、提供灵活的配置;

4、需要防止缓存穿透;

5、需要可以灵活指定缓存存活时间;

6、需要任意控制缓存的停用或启用。

目前这个框架的编码部分已完成,并取名为JAD-CACHE,取这个名字的原因是因为它是我的个人的JAD项目的一部分,JAD项目是本人用业余时间开发一个企业基础架构平台,因涉及的东西比较多,而且很多模块还没有完全完成。目前准备把其中做的比较完善的缓存模块单独从原项目中剥离出来作为一个独立的项目并准备开源给大家测试和使用,于是也就有了JAD-CACHE。

本文先展示这个框架的原理及架构设计,后续开放源代码后再发布一些使用手册方面的文章。

JAD-CACHE缓存框架是在spring
cache模块的基础上扩展而来,在上一篇博文《通用缓存框架,spring缓存模块原理分析篇》已经系统分析过srping
cache的原理,这里不再重复。spring cache模块重要的两个类就是org.springframework.cache.Cache和
org.springframework.cache.CacheManager。现在我跟据自己的需要对它们进行扩展。

我设计的Cahce相关的扩展类图如下:

图:Cahce扩展类类图1

上图中灰色部分是spring自已的类,其它的是扩展出来的。上图ManageredCache是一个接口,表示这个Cache可由本框架的CacheClient实例管理起来(关于CacheClient的概念稍后介绍)。ManageredCache接口在父接口Cache的基础上,增加了isAllowNullValues()等等方法,作用分别如下:

getCacheClient()

获得管理它的CacheClient实例

isAllowNullValues()

能否在此缓存中保存null值,防止缓存穿透

getActivityTime()

获得此缓存中对像存活时间,注意这个存活时间是一个业务存活时间,开发人员可通过配置指定一个默认的时间,也可以在调用put()方法缓存对像时通过参数另外指定一个存活时间。这样,在调用get()方法从缓存中取出对像后,会先通过个这个方法判断对像是否失效(即使它依旧存在于缓存中,但我们可以在业务的角度上以为它失效了),这个存活时间应该短于用户在ehcache.xml等配置文件配置的存活时间,这样就实现了个性化指定同一类型不同对像的存活时间。

put(Object, Object,int)

这个方法跟父类Cache接口的put(Object,Object)功能相同,就是把对像缓存起来,但它支持一个int类型的参数,用于指定对像存活时间的秒数。

size()

统计对像总数

keySet()

获得所有key

所有要缓存的数据都不是直接持久化到缓存容器中的,而是被装包成了一个个CacheValue类型的实例,在上图的类图中,可以看到,CacheValue类包含两个属性expiryTime和value,其中expiryTime是超时时间,value就是据体的数据对像。如果数据对像为null值,就转换成NullCacheData实例。在AbstractManageredCache这个抽象类的相关方法中,就实现了这些逻辑。

AbstractManageredCache是对ManageredCache接口的抽象实现,实现了在操作缓存之前,先通过管理它的CacheClient实例判断当前缓存客户端的状态是否已开启

(调用cacheClient的isStarted()方法),如果启用,就调用父接口Cache中声明的getNativeCache()获得具体的缓存实例操作缓存,如果没有启用,就什么也不做。同时,在它的put()方法实现过程中,会跟据它的activityTime属性指定存活时间,或跟据allowNullValues属性决定是否缓存null值,并将所有要缓存的数据包装成CacheValue实例持久化到缓存容器中。而在get()方法实现的罗辑,也会做相应的操作,跟据activityTime属性踢出超时的对像,并将缓存容器中的CacheValue实例转换成原始的数据类型。

   
在spring原来的缓存模型中,所有的Cache实例都被ManagerCache所管理。但本框架抽像出了一个叫CacheClient的概念,所有的缓存实例都被扩展成了ManageredCache对像,并被一个CacheClient实例管理起来(而此CacheClient实例又持有一个CacheManager的引用,这样一来Cache也就可以通过CacheClient间接的被ManagerCache管理)。这样做有一个好处就是:在Cache操作缓存之前,先通过它的CacheClient判断当前的缓存状态,跟据这个装态决定是否要进行操作。设计CacheClinet还有一个目地,就是让多个缓存实现能更好的共存于同一个应用之中,比如让EhCache实现的缓存交给一个CacheClinet管理,让MemCache实现的缓存交给另一个CacheClinet管理。在spring原来的缓存模块中,设计了一个叫CompositeCacheManager的类,可以同时配置多个CacheManager实例以达到这个目地。但我弃用它,改用CacheClinet的目地,就是想增加可以任意停用或启用某些Cache的功能。比如,当memcache服务器挂掉时,我们通过它对应的CacheClinet实例改变这个实例管理的所有Cache的状态,停用它,从而达到从应用层上禁用或启用缓存的目地。CacheClient相关的类图如下:

图:CacheClient相关类图

上图中的CacheClient类是一个接口,它声明的一些诸如start(),stop()等方法,用于修改本实例的状态,启用或停用,而isStarted()就是用来检查状态的,返回当前Client是否启用。每一个CacheClient实例管理一个CacheManager,通过该接口中的getCacheManager()可以获得它所控制的CacheManager实例引用,每个一个实例有一个唯一的名字,通过getClientName()可以获取它的名字。除此外,此接口还有一些诸如getAllowNullValues(),setDefActivityTime()之类的方法。这是为了方便开发人员对缓存的配置,开发人员在配置CacheClient实例时,可以在这里配置allowNullValues,

defActivityTime等属性,这样再在配置CacheManager或者Cache实例时就可以不指定了这些属性了,Cache会自动继承它的Client的属性值。这些配置在CacheClient接口的抽像实现类AbstractCacheClient中都有相应的实现。

在AbstractCacheClient中还有一个重要的属性,就是autoStart属性。这个属性如果配置为true,那在spring容器初始化的时候,这个Client实例一但生成,就会自动调用它的start()启动它,以使得它所控制的CacheManager可以正常操作缓存。否则,它不会自动启动,它所控制的所有Cache都处于禁用状态。

另外,为了方便配置,在AbstractCacheClient还提供了autoCreateCache属性,用于指定此客户端能否自动创建缓存实例。在Spring原来的缓存配置中,需要把用到的每个Cache都写到配置文件中,要么配到ehcache.xml中,要么在配置CacheManager时附加Cache实列相关的配置。否则,在操作没有配置的缓存时,会提示找不到某某名称的cache。在本框架中,如果指定了autoCreateCache属性为true,在调用CacheManager.getCache(String

name)获取不到Cache时,会自动创建一个默认的(通过覆盖CacheManager.getMissgeCache()实现)。当然,如果指定autoCreateCache为false时,就不会自动创建,这要求用户在ehcache.xml中自己配置。当然,为了方便,本框架,还可以直接在CacheClient中配置,上面的类图AbstractCacheClient中两个属性cacheNames和cacheBeans就是用于这个配置的。用户可通过cacheNames只配置名称,或者通过cacheBeans属性配置一个类型为的JadCache实例bean。这个JadCache类似于spring

在实现ehcache时提供的EhCacheFactoryBean。只不过这个更加简洁通用。如果只通过cacheNames配置一个Cache的名称,那么此Cache实例的相关属性都采用默认值这要求用户在ehcache.xml配置文件中配置一个defaultCache,或需要在memcache.xml中配置一个默认的cacheclient。

每一个CacheClient唯一管理一个CacheManager实例(在上面类图中可以看到,AbstractCacheClient类中有一个JadCacheManager类型的属性:cacheManager。关于本框架的CacheManager扩展稍后介绍)。CacheClient在被spring初始化时,会自动调用registryCacheManager()方法,跟据不同的缓存实现厂商生成一个对应的CacheManager实例,并注册到spring

context中,同时调用initCache()方法通过配置中的cacheNames或cacheBeans自动初始化所有的Cache实例。初始化完成后,再调用registryToMasterCacheManager()方法,这个方法稍后介绍。

一个应用系统中,可以有一个或多个CacheClient实例,每个CacheClient实例控制一个CacheManger。开发人员通过配置不同的CacheClient实例可以实现同时支持多个不同的缓存实现,比如,把EhCache相关的缓存配置到一个CacheClient中而把MemCache配置到另一个CacheClient中。

为了统一管理所有CacheClient实例,本框架设计了一个叫CacheClientManager来管理所CacheClient实例,它有一个抽像实现AbstractCacheClientManager,这个抽像类中,也有allowNullValues,

defActivityTime等CacheClient中具的相同的属性,只不过这里的属性作为一个全局的配置,使得CacheClient可以省去这些配置而直接使用CacheClientManager中的配置。整个应用中,只能有CacheClientManager实例,但这个实例可以管理一个或多个CacheClient。而通常况下,一些简单的应用系统中,往往只有一种缓存实现,也就是只需要配置一个CacheClient,因此AbstractCacheClientManager给出了两个不同的实现类,分别是上图中红颜色的SingleClientManager和MultiClientManager,分别表示单CacheClient管理或多CacheClient管理器。开发人员可跟据业务情况选择性使用其中一个进行配置。

   
前文提到,每个CacheClient在初始化时,会自动生成一个对应的CacheManager实例并注册到Spring上下文中,但Spring在操作缓存时,为了能准确的通过CacheManager找到相应名称的Cache实例,这就要求还需要对这些CacheManager实例进行统一管理。在spring原来的缓存模块中,提供了一个叫CompositeCacheManager的实现类,以组合设计模式的方式来管理这些CacheManager实例,这个CompositeCacheManager实现类有一个列表指向所有CacheManager实例的引用。在执行getCache(String)时会遍历这个列表,循环调用每个实例的getCache(String)方法,然后返回一个不为null的Cache,但是如果所有的CacheManager都获取不到Cache时,这个方法最终是会返回null的。然而,在本框架中,是支持找不到Cache时自动创建的,所以在本框架中,我设计了一个叫MasterCacheManager的类来管理这些CacheManager,同时,这个类有一个叫defCacheManager的CacheManager属性来指定一个默认的CacheManager。在跟据名称找不到任何Cache时,就自动调用这个默认CacheManager的addCache()方法来自动创建一个。本框架CacheManager相关的类图如下所示:

图:CacheManager类图

   
上图灰色总分是spring缓存模块自带的类,其它颜色是扩展类。其中最右边的MasterCacheManager类就是上文提到管理所有CacheManager的实现类。上图左边的JadCacheManager是一个接口,提供initCache(JadCache)和newCache(JadCache)两个方法,这两个方法,主要是用于在CacheClient初始化过程自动生成CacheManager实现类后,准备通过initCache()方法来初始化配置中的Cache来调用的。对于MasterCacheManager类的实例化,开发人员无需额外在spring

context中配置,因为在CacheClientManager的初始化过程中,会自动注册一个MasterCacheManager类型的实例到spring

context中(上面类图AbstractCacheClientManager这个类中的registerMasterManager()方法就是用来做这个事的)。而这个类中cacheManagerMap的属性就是按名称组织起来的CacheManager类型的集合。在每个CacheClient初始化时自动生成CacheManager实例后,会调用CacheClient类的registryToMasterCacheManager()方法,这个方法会从当前Spring

conetxt中获取到MasterCacheManager实例,然后调用它的register()方法,把它注册到MasterCacheManager实例中(添加到cacheManagerMap集合)。

上面类图中的JadAbstractCacheManger是借用了Srping的AbstractCacheManger对JadCacheManager的一个抽象实现,后面在集成EhCache或MemCache等所有缓存时就从这个类往下扩展。

以上就是本框架的一个总体设计,因为篇附过长。后面再在其它的文章中给出在这个基础上扩展EhCache和MemCache的方案,并实现一个基于Map的内存缓存实现。想关注更多信息或者想及时了解动态的同学们可以扫以下二维码关注我的微信公众号,多谢

作者:何川jad

来源:51CTO

时间: 2024-09-12 03:15:12

写自己的缓存框架,JAD-CACHE架构设计篇的相关文章

开源缓存框架比自己写的缓存类好在哪里?

问题描述 自己写的缓存类只有几百行,就是放在map中,有get()put()方法,可设置大小.失效时间等.看到java有许多开源缓存框架,代码量大得多,应该带来更多价值,有没有用过的,比自己写的缓存类好在哪里?我想评估一下,如果有意义就用缓存框架 解决方案 如果是单一的项目我觉的使用ehcahce就差不多了,自己写的话有很多问题考虑不到,例如线程安全,序列化等问题.ehcahce使用也很简单 . 如果是多个项目共享同一块缓存,那就不是map能解决的问题了,这样可以考虑用可以考虑用memchahe

JAVA 开源缓存框架

  JBossCache/TreeCache  JBossCache是一个复制的事务处理缓存,它允许你缓存企业级应用数据来更好的改善性能.缓存数据被自动复制,让你轻松进行Jboss服务器之间的集群工作.JBossCache能够通过Jboss应用服务或其他J2EE容器来运行一个Mbean服务,当然,它也能独立运行. JBossCache包括两个模块:TreeCache和TreeCacheAOP. TreeCache --是一个树形结构复制的事务处理缓存. TreeCacheAOP --是一个"面向

请教一下C#有哪些缓存框架?

问题描述 最近做一个winform项目,里面有大量的数据字典记录.如果客户端每次请求,字典数据都从数据库里获取,这样效率上都有一定影响.能不能在服务端缓存这些数据,用到的时候直接从缓存里取.因为才转过来做C#,之前一直做java开发.对c#了解不是太多.请问一下c#有没有类似JAVA的ehcache这样的缓存框架?或者大家都用什么解决方案来进行缓存? 解决方案 解决方案二:一直都用redis,偶尔也用一些类似key-value结构的数据库解决方案三: 解决方案四:刚才找了看了ehcache一下,

Objective-C的缓存框架EGOCache在iOS App开发中的使用_IOS

EGOCache简介 EGOCache is a simple, thread-safe key value cache store. It has native support for NSString, UI/NSImage, and NSData, but can store anything that implements <NSCoding>. All cached items expire after the timeout, which by default, is one da

缓存一致性(Cache Coherency)入门(转)

参考原文:http://fgiesen.wordpress.com/2014/07/07/cache-coherency/ 本文是RAD Game Tools程序员Fabian "ryg" Giesen在其博客上发表的<Cache coherency primer>一文的翻译,经作者许可分享至InfoQ中文站.该系列共有两篇,本文系第一篇. 我计划写一些关于多核场景下数据组织的文章.写了第一篇,但我很快意识到有大量的基础知识我首先需要讲一下.在本文中,我就尝试阐述这些知识.

MyBatis的一级缓存和二级缓存 以及 mybatis和ehcache缓存框架整合

查询缓存 缓存的意义 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题. 基本由此图可以看出,我们在每一层都需要相应的缓存. mybatis持久层缓存 mybatis提供一级缓存和二级缓存 mybatis一级缓存是一个SqlSession级别,sqlsession只能访问自己的一级缓存的数据,二级缓存是跨sqlSession,是mapper级别的缓存,对于mapper级别的缓存不同的sql

Oracle结果集缓存(Result Cache)--服务器、客户端、函数缓存

Oracle结果集缓存(Result Cache)--服务器.客户端.函数缓存 在11g中,Oracle提供了结果集缓存特性.该缓存是在共享内存中存储全部的结果集,如果一个查询SQL被执行,且它对应的结果集在缓存中,那么,该SQL的几乎全部开销都可以避免.这些开销包括,解析时间.逻辑读.物理读和任意的通常可能会遭遇的争用.但是,在实际的情况中,结果集缓存仅在少数的情况下是有效的,原因有如下几点: (1)有数据重叠的多个SQL会在缓存中保存冗余的数据. (2)对依赖对象的任何改变(包括对查询中引用

C#开发微信门户及应用(48) - 在微信框架中整合CacheManager 缓存框架

 在我们的很多框架或者项目应用中,缓存在一定程度上可以提高程序的响应速度,以及减轻服务器的承载压力,因此在一些地方我们都考虑引入缓存模块,这篇随笔介绍使用开源缓存框架CacheManager来实现数据的缓存,在微信开发框架中,我们有一些常用的处理也需要应用到缓存,因此本随笔以微信框架为例介绍缓存的实际使用,实际上,在我们很多框架中,如混合式开发框架.Web开发框架.Bootstrap开发框架中,这个模块都是通用的. 1.框架的缓存设计 在我们的微信开发框架中,缓存作为数据库和对外接口之间的一个分

HTML 5缓存机制:Cache Manifest配置实例

Cache Manifest是HTML 5的一种缓存机制,文章作者直接用博客当测试环境,虽然应用起来非常简单,但效果却出奇的好.缓存后的速度,简直是惊人的快.像Yslow显示,打开一个缓存过的页面,只要0.729秒,比不缓存的差不多快了10倍. 一.Cache Manifest基础知识 作为一个Web开发相关的人员,都不会少听到.看到Cache这个词.是的,上面也已经说了,它是一种缓存的机制.它可以通过一个.manifest文件来配置需要缓存的或者一定要保持联网缓存的文件.而重点就是这个.man