Sapphire是一个高并发、高缓存吞吐性、高性能的Java分布式内存对象缓存系统,其具有简单易学、方便实用等特点。它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。简单的说就是将数据源中的数据临时存储于内存中,然后从内存中读取,从而大大提高读取速度。
Sapphire目前最新版本为1.1.7-beta,主要特性包含:
1.敏捷快速;
2.体系结构中立、跨平台支持;
3.多种缓存管理容器实现;
4.多种缓存策略(LRU、LFU、FIFO、RDM);
5.支持缓存注解服务驱动(Annotation方式直接缓存方法);
6.支持缓存持久化及加载虚拟机运行期数据;
7.单个缓存最大缓存容量为1gByte;
8.支持缓存容量单位设置(byte、kByte、mByte、gByte)
9.支持TCP单播集群、P2P广播、组播集群、RMI组播集群;
Cache提供的常用方法如下:
put(Object key, Object value):Object 添加缓存数据
get(Object key):Object 获取缓存数据
remove(Object key):Object 清除指定缓存数据
clear():void 清除所有缓存数据
getElementsInSize():long 获取当前缓存已用元素数量
getSurplusElementsInSize(): long 获取当前缓存剩余元素数量
getMaxElementsInSize(): long 获取当前缓存最大元素数量
getCacheInMemory(): long 获取当前缓存已用缓存容量
getMaxCacheInMemory(): long 获取当前缓存最大缓存容量
getSurplusCacheInMemory():long 获取当前缓存剩余缓存容量
cacheUseRecords(Object key): void 计算缓存使用记录
getCacheUseRecords():HashMap 获取缓存使用记录
getMaxCacheInMemoryForByte(long maxCacheInMemory,
String capacityUnit): long 计算当前缓存的最大字节缓存容量
5.使用Sapphire缓存缺省数据类型
缺省情况下Sapphire可以为您缓存的数据类型为如下3类:
基本数据类型;
数组类型;
String类型;
使用Sapphire缓存缺省数据类型实现:
/* 缓存基本数据类型 */
cache.put("byte", 1);
cache.put("short", 10);
cache.put("int", 100);
cache.put("long", 1000L);
cache.put("float", 1.5);
cache.put("double", 10.5D);
cache.put("char", 'A');
cache.put("boolean", true);
/* 缓存数组数据类型 */
cache.put("byte[]", new byte[100]);
/* 缓存String数据类型 */
cache.put("String", "Sapphire Cache");
6.使用Sapphire缓存复合数据类型
缺省情况下Sapphire并没有提供对复合数据类型的支持,但您可以将您需要缓存的复合对象实现Sapphire为您提供的CacheSerializable接口(该接口为标记接口,无需任何实现)。一旦您的复合对象实现了该接口,Sapphire便会允许缓存您所指定的复合对象,除了可以实现CacheSerializable接口,Sapphire仍然允许您实现JDK原生的Serializable接口。
至于复合数据类型为什么需要实现CacheSerializable接口或者实现Serializable接口Sapphire才允许将其缓存呢?其实Sapphire在对您的数据进行缓存之前需要将所有数据进行序列化处理,以便计算当前所需缓存的数据所占缓存容量的内存大小。
使用Sapphire缓存复合数据类型实现:
class SaveObject implements CacheSerializable
{
//...
}
/* 初始化Sapphire容器方式 */
CacheManager cacheManager = new SapphireCacheManager();
/* 获取缓存实例 */
Cache cache = cacheManager.getCache("defaultCache");
/* 缓存复合数据类型 */
cache.put("object", new SaveObject());
System.out.println(cache.get("object") instanceof CacheSerializable);
注意:
如果您在使用Sapphire为您缓存复合对象时并没有将所需缓存的数据实现CacheSerializable接口或者Serializable接口,Sapphire将会抛出java.io.NotSerializableException异常信息。
7.启动缓存注解服务驱动
通过Sapphire的配置文件我们可以观察到<service:annotation-driven>标签。该标签主要用作于开启缓存注解服务驱动,其中包含有一个auto属性,该属性所允许的参数范围为“true”与“false”。如果您将auto属性设置为“true”,则意味着您将成功开启Sapphire的缓存注解服务驱动服务,否则意味着该服务的状态为关闭状态。
至于开启Sapphire的缓存注解服务驱动有何用处,想必这是您最为关心的问题。其实不难发现在很多复杂应用的情况下,我们经常需要缓存具体的方法返回结果(比如服务层与持久层),面对这种情况虽然我们可以使用Cache对象的put方法进行数据缓存,但这并不灵活。所以Sapphire将为您提供了一个基于注解方式的方法返回值缓存技术,这便是刚才我们所提到的缓存注解服务。
位于org.sapphire.cache.annotation包下的@CacheService类型正是我们即将使用到的方法返回值缓存技术的实现。
缓存注解服务驱动实例:
此处省略导包过程…
public class SapphireCacheTest extends TestCase
{
/**
* @param args
*/
public static void main(String[] args) throws Exception
{
/* 初始化Sapphire容器方式 */
CacheManager cacheManager = new SapphireCacheManager();
/* 获取缓存实例 */
Cache cache = cacheManager.getCache("defaultCache");
Element element1 = new Element(SaveObject.class.getName(),
"testService1", cache);
element1.put(26);
System.out.println("age: " + cache.get("age"));
Element element2 = new Element(SaveObject.class.getName(),
"testService2", cache);
element2.put(1, "JohnGao");
System.out.println("UserBean: " + cache.get("info"));
}
}
此处省略导包过程…
public class SaveObject implements CacheSerializable
{
@CacheService(cacheElementKey = "age")
public int testService1(int age)
{
return age;
}
@CacheService(cacheElementKey = "info")
public Object testService2(int userId, String userName)
{
class InfoBean implements CacheSerializable
{
int userId;
String userName;
}
InfoBean info = new InfoBean();
info.userId = userId;
info.userName = userName;
return info;
}
}
在您使用Sapphire为您提供的缓存注解服务器时,您务必先找到Sapphire的配置文件中的<service:annotation-driven>标签,并将该标签的auto属性设置为“true”则意味着注解服务成功开启。接着您便可以通过@CacheService(cacheElementKey = "age")的注解方式对所需缓存的方法进行标注。这里需要提醒一下@CacheService中需要设置一个cacheElementKey的属性,该属性所代表的是缓存Key。
当您成功的在方法前添加@CacheService标注后,仍然还需要使用到一个类型,那便是Element类型。该类型作为Sapphire的缓存元素类型,在使用缓存注解服务的时候您必须使用它才能够对您的方法返回值进行动态缓存。
Element提供的常用方法如下:
Element(String serviceClass, String serviceMethod,
Cache cache) 构造函数(缓存类型名称、缓存方法名称、Cache实例)
put(Object... params):void 添加服务参数
cacheParam(Object... params):void 缓存服务参数
注意:
如果您并没有开启Sapphire的缓存注解服务便使用时,Sapphire将会抛出org.sapphire.cache.
exception.CacheServiceException异常。
8.设置缓存所能存储的最大元素数量
Sapphire在缓存管理上为您提供了更为灵活的缓存管理方式,但首先您应该先了解Sapphire到底是如何对缓存进行管理的。
通过Sapphire的配置文件我们可以在<cache/>标签中找到一个名为maxElementsInSize的属性,该属性用于定义单个缓存所允许存放的最大缓存元素数,假设您将maxElementsInSize属性设置为10,那么您的缓存实例将只允许缓存10个数据元素。
一旦您所定义的maxElementsInSize溢出,Sapphire将会抛org.sapphire.cache.exception.
CacheElementsException异常。
9.设置缓存所能存储的最大缓存容量
Sapphire作为一个高效的缓存Framework,单个缓存允许您存储高达1Gbyte的缓存数据。当然Sapphire的缓存容量与实际的物理内存与VM内存精密相关,也就是说如果您希望将Sapphire的单个缓存容量设置为1GByte,那么你需要观察您的实际物理内存及VM内存是否允许,否则Sapphire将会抛出java.lang.OutOfMemoryError异常。
通过Sapphire的配置文件我们可以在<cache/>标签中找到2个名为maxCacheInMemory和capacityUnit的属性,其中maxCacheInMemory属性用于设置您所定义的缓存容量大小,而maxCacheInMemory则用于定义您的缓存容量单位,假设maxCacheInMemory定义为“10”,capacityUnit定义为“mByte”,那么也就是说您则定义了一个缓存容量为10mByte的单个缓存实例。一旦您所定义的maxCacheInMemory溢出,Sapphire将会抛出org.sapphire.cache.exception.
OutOfCacheError异常。
在程序中您可以通过Cache类型提供的一些缓存容量及元素检测方法来观测您具体的缓存容量开销。
观测缓存容量开销实例:
/* 初始化Sapphire缓存管理容器 */
CacheManager cacheManager = new SapphireCacheManager();
/* 获取缓存实例 */
Cache cache = cacheManager.getCache("defaultCache");
/* 添加缓存元素 */
cache.put("key", new byte[100]);
System.out.println("最大缓存元素长度:" + cache.getMaxElementsInSize());
System.out.println("当前缓存元素长度:" + cache.getElementsInSize());
System.out.println("剩余缓存元素长度:" + cache.getSurplusElementsInSize());
System.out.println("最大缓存容量:" + cache.getMaxCacheInMemory());
System.out.println("当前缓存容量:" + cache.getCacheInMemory());
System.out.println("剩余缓存容量:" + cache.getSurplusCacheInMemory());
注意:
如果最大缓存元素长度<=当前缓存元素长度时,剩余缓存元素长度则为“-1”。如果当前缓存容量>最大缓存容量时,剩余缓存容量则为“-1”。
10.缓存持久化
在您使用Sapphire缓存数据时,极有可能会出现maxElementsInSize溢出及maxCacheInMemory溢出。但这些溢出的数据可能恰恰是较为重要且不希望丢失的数据,这个时候该怎么办呢?值得庆幸的是Sapphire为您带来了高效的缓存持久化技术,也就是说您可以通过Sapphire的配置文件找到<cache/>标签的overflowToDisk属性,并设置为“true”时,那么一旦maxElementsInSize或者maxCacheInMemory溢出时,其溢出的数据并不会直接丢失,Sapphire则将会为您将这一部分溢出的数据持久化于本地进行存储,以便于您继续使用。
如果您希望使用Sapphire为您提供的缓存持久化技术,那么您不仅仅需要将Sapphire配置文件中得overflowToDisk属性设置为“true”,您还需要设置<diskStore>标签中得path、diskEternal、timeToRemoveSeconds等3个属性。path属性缺省为“java.io.tmpdir”,也就是说一旦出现maxElementsInSize或者maxCacheInMemory溢出时,Sapphire会将溢出数据缓存于操作系统的临时目录(不同的操作系统临时目录不同)中进行存储,存储格式为SAPPHIRE_CACHE.data。
除了path属性外,<diskStore>标签中的另外2个属性diskEternal和timeToRemoveSeconds则将满足您更多的需求。如果您希望您所持久化的缓存数据是永远有效的,则需要将diskEternal属性设置为“true”,这样一来持久化于本地的缓存将永不失效。如果您将其设置为“false”时,则意味着timeToRemoveSeconds属性所定义的内容将影响到持久化缓存的失效周期,假设您将timeToRemoveSeconds属性定义为“10”,当10秒以后,Sapphire的回收器将会对持久化缓存进行回收。
11.加载虚拟机重启期数据
当您成功将缓存进行持久化后,Sapphire为您提供了一种持久化读取机制来加载虚拟机重启期数据。当然您需要将Sapphire配置文件中的diskPersistent属性设置为“true”
这里有一点您需要稍加注意,如果您在Sapphire的配置文件中设置的maxElementsInSize或者maxCacheInMemory属性不再重启期数据的范围内,也就是说Sapphire仍然会将其认为溢出,导致重复缓存持久化。
12.缓存回收策略
Sapphire为您提供有3种缓存回收策略,分别为:LRU、LFU、RMD。
LRU(最少使用, Least Recently Used)将会根据您对缓存元素的实际使用来动态收回缓存元素,使用次数最少的缓存元素则优先被Sapphire回收器进行回收,当然您需要通过Sapphire的配置文件找到<cache>标签并设置timeToLiveSeconds属性,该属性主要用于设置缓存的失效周期,单位为秒。假设您将timeToLiveSeconds属性设置为“10”,则意味着10秒以后Sapphire回收器将会按照cacheCleanupPolicy属性所定义的回收策略对缓存进行动态回收。LFU和RMD等回收策略也是经常较为适用的,但一般来说我们推荐您使用LRU缓存回收策略。
注意:
如果您在<cache>标签中将eternal属性设置为“true”时,则意味着您的缓存为永不失效,同样cacheCleanupPolicy则也就意味着失效。
13.分布式缓存——TCP集群配置模式
Sapphire为您提供了3种类型的分布式缓存机制,但Sapphire推荐您使用TCP集群为首选。因为该分布式缓存的实现方式为TCP单播机制,无论是安全性、正确性、稳定性,哪怕是效率上都是较为优秀的。
如果您希望使用TCP集群配置模式的方式来实现缓存共享,那么首先您需要在Sapphire的配置文件中添加TCP集群配置。
您可以使用Sapphire为您提供的<distributedCacheManagerFactory>标签,该标签中一共包含了3个属性,分别是:class、serverHost以及serverPort。其中class用于指定集群类型,serverHost属性用于指定集群地址,serverPort属性用于指定集群端口。
假设您已经成功配置TCP集群后,您还需在<cache>标签中添加<distributedCacheListener>子标签,该标签用于启动缓存复制监听,并包含一个名为replicateCache的属性,如果您将其设置为“true”则意味着您允许缓存实例实现缓存共享,否则将意味着拒绝缓存共享。
TCP集群配置示例:
<!-- 缓存注解服务驱动 -->
<service:annotation-driven auto="true" />
<!-- 缓存持久化全局配置 -->
<diskStore path="java.io.tmpdir" diskEternal="false"
timeToRemoveSeconds="60" />
<!-- TCP集群配置模式 -->
<distributedCacheManagerFactory
class="org.sapphire.cache.distributed.tcp.TcpDistributedCacheManagerFactory" serverHost="127.0.0.1" serverPort="30051" />
<!-- 缺省缓存配置 -->
<cache name="defaultCache" eternal="false" maxElementsInSize="100"
maxCacheInMemory="1" capacityUnit="mByte" overflowToDisk="true"
diskPersistent="false" timeToLiveSeconds="60" cacheCleanupPolicy="LRU">
<!-- 启动缓存复制监听 -->
<distributedCacheListener replicateCache="true" />
</cache>
通过上述示例,您可以发现< distributedCacheManagerFactory >标签中的class属性其值为一个TcpDistributedCacheManagerFactory类型(位于org.sapphire.cache.distributed.tcp包下)。该类型作用于搭建一个稳定且高性能的TCP分布式缓存服务器。
使用Sapphire来为您实现分布式缓存是一件及其简单的事情,只要您在Sapphire的配置文件中启用了TCP集群配置模式,并成功开启了缓存复制监听,那么就无需您在做任何操作,便可实现分布式缓存共享。接下来您可以通过一段简单的测试代码来实现Sapphire为您提供的基于TCP集群配置模式的分布式缓存。
分布式缓存示例:
/* 初始化Sapphire缓存管理容器 */
CacheManager cacheManager = new SapphireCacheManager();
/* 获取缓存实例 */
Cache cache = cacheManager.getCache("defaultCache");
while(true)
{
cache.put("cacheKey", "cacheValue");
System.out.println(cache.get("key"));
Thread.sleep(1000);
}
上述程序一旦运行后,Sapphire便会成功开启TCP缓存服务器,启动缓存服务,并成功的实现缓存共享。但这个时候您可能会觉得奇怪,因为您并没有使用客户端对TCP缓存服务器进行连接,为什么您却可以获取共享后的缓存?其实这是因为在缺省环境下Sapphire的TCP缓存服务器既是服务端又是客户端,能够自身实现缓存共享。
如果您的网络中有其余的节点需要共享缓存信息,那么您便可以将< distributedCacheManagerFactory >标签中的class属性的值更改为org.sapphire.cache.distributed.tcp. TcpDistributedCacheConnection即可,该类型为TCP集群连接类型。
注意:
如果程序中,您并没有成功启动TCP集群配置模式,而直接为class属性去配置TcpDistributedCacheConnection类型,这个时候Sapphire则会抛出org.sapphire.cache.exception.
TcpCacheDistributedException异常。
还有一点您需要注意,假设您程序中有多个缓存实例都需要实现缓存共享时,您可以在Sapphire配置文件中为需要实现缓存共享的缓存类型开启缓存复制监听即可。