JBoss 系列二十一:JBossCache 核心API

内容简介

本处介绍JBossCache相关的主要API,我们目的通过本部分描述,读者可以使用JBossCache API,在自己的应用中使用JBossCache。

Cache接口

Cache 接口是和JBossCache交互的主要机制。它是用 CacheFactory 构建并可选择地启动的。CacheFactory 允许你从 Configuration 对象或 XML 文件创建 Cache。缓存将数据组织到由节点组成的树型结构里。一旦你具有了到 Cache 的引用,你可以用它来在树型结构里查找 Node 对象,并存储数据。

JBoss 系列二十一:JBossCache 核心API_第1张图片

请注意,上面的图表只是描述了一些流行的 API 方法。阅读上述接口的 Javadoc 是学习 API 的最甲途径。

初始化和启动缓存

Cache接口的实例只能通过 CacheFactory 创建。这和 JBoss Cache 1.x 不同,旧的 TreeCache 可以直接初始化。CacheFactory 提供了用于创建 Cache 的大量重载方法,但它们基本上都是做的同一件事情:

  • 获取对 Configuration 的访问, 或者是作为方法参数传入或通过解析 XML 内容并进行构建。XML 内容可以来自输入流、classpath 或文件系统位置。关于获取 Configuration 的更多信息随后将会有详细描述。
  • 初始化 Cache 并为其提供一个对 Configuration 的引用
  • 可选择地调用缓存的 create() 和 start() 方法
下面的代码段例子是使用缺省配置值创建和启动缓存的最简单机制:

CacheFactory factory = new DefaultCacheFactory();
Cache cache = factory.createCache();

在下面代码段里,我们告诉 CacheFactory 来查找并解析 classpath 上的配置文件:

CacheFactory factory = new DefaultCacheFactory();
Cache cache = factory.createCache("cache-configuration.xml");

在下面代码段里,我们通过文件配置缓存, 但希望通过程序修改配置元素。所以我们通知工厂不要启动缓存,而是自己来启动:

CacheFactory factory = new DefaultCacheFactory();
Cache cache = factory.createCache("/opt/configurations/cache-configuration.xml",false);
Configuration config = cache.getConfiguration();
config.setClusterName(this.getClusterName());
// Have to create and start cache before using it
cache.create();
cache.start();

缓存和获取数据

如上面我们初始化和启动了缓存,接下来让我们使用 Cache API 来访问缓存里的 Node 然后对这个节点做一些简单的读和写,如下代码段:

28     Cache cache = createCacheUseDefault();
 29     Node rootNode = cache.getRoot();
 30     Fqn helloWorldFqn = Fqn.fromString("/root/helloWorld");
 31     Node helloWorld = rootNode.addChild(helloWorldFqn);
 32     helloWorld.put("isJBossCache", Boolean.TRUE);
 33     helloWorld.put("content", new Content("HelloWorld"));
 35     System.out.println(helloWorld.get("isJBossCache"));
 36     System.out.println(helloWorld.get("content"));
 37     System.out.println(helloWorld.getFqn());
 38     System.out.println(helloWorld.getKeys());
 40     helloWorld.remove("isJBossCache");
 41     helloWorld.remove("content");
43     System.out.println(helloWorld.get("isJBossCache"));
 44     System.out.println(helloWorld.get("content"));
46     rootNode.removeChild(helloWorldFqn);

如上 28-29 行创建默认 Cach 后获取一个跟节点;30 行JBoss Cache 用树状的结构存储数据,树状结构包含多个节点,所有节点都用 Fqn 来识别; 31-33 行创建一个新节点,并向该节点存储数据; 35-38 行测试读取存储的数据; 40-41 行移除添加的数据; 46 行将新创建的节点从跟节点移除。

为了便于使用, Cache 接口也开放以 “Fqn 类” 作为参数执行 put/get/remove 操作:

Cache cache = createCacheUseDefault();
Node rootNode = cache.getRoot();
Fqn helloWorldFqn = Fqn.fromString("/root/helloWorld");
Node helloWorld = rootNode.addChild(helloWorldFqn);

cache.put(helloWorldFqn, "isJBossCache", Boolean.TRUE);
cache.put(helloWorldFqn, "content", new Content("HelloWorld"));

System.out.println(helloWorld.get("isJBossCache"));
System.out.println(helloWorld.get("content"));
System.out.println(cache.getRoot().hasChild(helloWorldFqn));

cache.removeNode(helloWorldFqn);

组织数据并使用节点结构

节点应该被看作一个命名逻辑数据组。节点应该用来包含单个数据记录里的数据, 例如,某个人或帐号的信息。它应该具有缓存的所有方面 - 锁、缓存加载、复制和逐出 - 对于每个节点设置。因此, 存储在单个节点里任何分组信息都将被当作单个的原子单元。

Fqn 类

前面的部分在其示例里使用了 Fqn 类;现在让我们对其进行进一步的了解。Fully Qualified Name (Fqn) 封装了代表对应缓存树型机构里某个位置的路径的名称列表。该列表里的元素通常是 String 但也可以是任何 Object 或混合类型。 Fqn 代表一个到特定节点的路径或 Cache 中的路径。

这个路径可以是绝对的(也就是相对于根节点),也可以相对于缓存里的任何节点。关于使用 Fqn 的 API 调用的文档里会告诉你该 API 是否使用相对还是绝对的 Fqn。Fqn 提供了大量的工厂方法,详情请参考JBossCache  Javadoc。下面的例子解释了创建 FQN 最常用的途径:
15    Fqn strFqn = Fqn.fromString("/people/Smith/Joe/");
16    Fqn eleDqn = Fqn.fromElements("accounts", "NY", new Integer(12345));

如上15 行我们创建一个 Fqn 指向节点 Joe,节点 Joe 在父节点 Smith 下,而 节点Smith 位于节点 people 下,我们通过 String 字符串创建 Fqn;16 行我们用其他的数据类型来创建 Fqn。特别注意,Fqn.fromElements("a", "b","c") 与Fqn.fromString("/a/b/c") 的等效的,它们都是创建了一个指向节点 c 的Fqn。

停止和销毁缓存

使用完毕后停止并销毁缓存是通常的做法,特别是在集群应用中缓存使用大量 JGroups 通道的情况下,停止并销毁缓存确保了能够正确地清理网络套接字和维护线程等资源。我们可以通过缓存提供的方法停止和销毁缓存,如下所示:

cache.stop();
cache.destroy();

注意,已调用 stop() 的缓存可以用 start() 重启启动。类似地,已调用 destroy() 的缓存也可以用 create() 重新创建,并可调用 start()重启启动缓存。

缓存模式

虽然严格来说,缓存模式并非 API 的一部分,但缓存所操作的模式可影响到任何 put 或 remove 操作的行为,所以在这里我们将简单的描述出这些模式。JBoss Cache 模式是通过org.jboss.cache.config.Configuration.CacheMode 定义的,它是一个枚举类型,包括的缓存模式如下:

  • LOCAL - 本地的、非群集的缓存模式。本地缓存不加入群集也不和群集里的其他节点通讯
  • REPL_SYNC - 同步模式。缓存复制群集里其他缓存的修改。同步复制意味着修改被复制且调用者阻塞,直至接收到复制确认。
  • REPL_ASYNC - 异步模式。和上面的 REPL_SYNC 类似,缓存复制群集里其他缓存的修改。但调用者不会阻塞到接收到复制确认为止
  • INVALIDATION_SYNC - 非验证同步模式。如果缓存被配置为失效而不是复制,每次数据有修改时,群集里的其他缓存将收到一条消息来通知它们这个数据已经陈旧且应该从缓存逐出。这样做减少了复制负载,同时还可以使远程缓存里的陈旧数据失效
  • INVALIDATION_ASYNC - 非验证异步模式。和上面的一样,除了这个失效模式会导致失效信息的异步广播

添加缓存 Listener - 注册缓存事件

JBoss Cache 提供一个方便的机制以注册缓存事件的通知:

Object myListener = new MyCacheListener();
cache.addCacheListener(myListener);

注解方法需要是 public 的,并具有 void 返回类型,还得接受org.jboss.cache.notifications.event.Event 类型或其子类型为唯一的参数。我们可以在缓存中添加的事件包括:

  •  @CacheStarted- 注解方法以在缓存启动时接收通知。这些方法需要接受一个属于 CacheStartedEvent 的参数类型
  • @CacheStopped- 注解方法以在缓存停止时接收通知。这些方法需要接受一个属于 CacheStoppedEvent 的参数类型
  • @NodeCreated- 注解方法以在节点创建时接收通知。这些方法需要接受一个属于 NodeCreatedEvent 的参数类型
  • @NodeRemoved- 注解方法以在删除节点时接收通知。这些方法需要接受一个属于 NodeRemovedEvent 的参数类型
  • @NodeModified- 注解方法以在修改节点时接收通知。这些方法需要接受一个属于 NodeModifiedEvent 的参数类型
  • @NodeMoved- 注解方法以在移动节点时接收通知。这些方法需要接受一个属于 NodeMovedEvent 的参数类型
  • @NodeVisited- 注解方法以在访问节点时接收通知。这些方法需要接受一个属于 NodeVisitedEvent 的参数类型
  • @NodeLoaded- 注解方法以在从 CacheLoader 里加载节点时接收通知。这些方法需要接受一个属于 NodeLoadedEvent 的参数类型
  • @NodeEvicted- 注解方法以在节点从内存里逐出时接收通知。这些方法需要接受一个属于 NodeEvictedEvent 的参数类型
  • @NodeInvalidated- 注解方法以在节点由于远程失效事件从内存里逐出时接收通知。这些方法需要接受一个属于 NodeInvalidatedEvent 的参数类型
  • @NodeActivated- 注解方法以在节点被激活时接收通知。这些方法需要接受一个属于 NodeActivatedEvent 的参数类型
  • @NodePassivated- 注解方法以在节点被钝化时接收通知。这些方法需要接受一个属于 NodePassivatedEvent 的参数类型
  • @TransactionRegistered- 注解方法以在缓存在已注册的事务管理者里注册javax.transaction.Synchronization 时接收通知。这些方法需要接受一个属于 TransactionRegisteredEvent 的参数类型
  • @TransactionCompleted - 注解方法以在缓存从已注册的事务管理者接收提交或回滚调用时接收通知。这些方法需要接受一个属于 TransactionCompletedEvent 的参数类型
  • @ViewChanged- 注解方法以在群集的组结构改变时接收通知。这些方法需要接受一个属于 ViewChangedEvent 的参数类型
  • @CacheBlocked- 注解方法以在缓存操作因为状态转换事件而阻塞时接收通知。这些方法需要接受一个属于 CacheBlockedEvent 的参数类型
  • @CacheUnblocked- 注解方法以在缓存操作因为状态转换事件而取消阻塞时接收通知。这些方法需要接受一个属于 CacheUnblockedEvent 的参数类型
  • @BuddyGroupChanged- 注解方法以在节点由于 Buddy 放弃群集或更新、更近的 Buddy 加入而修改其 Buddy 组时接收通知。这些方法需要接受一个属于 BuddyGroupChangedEvent 的参数类型    

如下我们给出一个缓存注册事件的例子:

14 @CacheListener
15 public class MyListener {
17         @CacheStarted
18         @CacheStopped
19         public void cacheStartStopEvent(Event e) {
20                 switch (e.getType()) {
21                 case CACHE_STARTED:
22                         System.out.println("Cache has started");
23                         break;
24                 case CACHE_STOPPED:
25                         System.out.println("Cache has stopped");
26                         break;
27                 }
28         }
30         @NodeCreated
31         @NodeRemoved
32         @NodeVisited
33         @NodeModified
34         @NodeMoved
35         public void logNodeEvent(NodeEvent e) {
36                 System.out.println(e.getType() + " on node " + e.getFqn() + " has occured");
37         }

如上 17-18 行在缓存启动或停止时 cacheStartStopEvent()方法被调运,根据事件的类型打印输出相关提示信息;第 30-34 行在节点被创建,移除,访问,移动是 logNodeEvent()方法被调运,详细的事件类型被打印输出。我们用如下代码端测试 MyListener:

13     CacheFactory factory = new DefaultCacheFactory();
14     Cache cache = factory.createCache(false);
15     MyListener myListener = new MyListener();
16     cache.addCacheListener(myListener);
17     cache.start();
19     Node root = cache.getRoot();
20     Fqn abcFqn = Fqn.fromString("/a/b/c");
21     Node abc = root.addChild(abcFqn);
22     abc.put("content", new Content("abc test"));
23     abc.get("content");
24     cache.removeNode(abcFqn);
25     cache.stop();
26      cache.destroy();

如上 13-14 行使用 DefaultCacheFactory 创建一个 Cache; 15-16 行注册缓存事件; 17 行启动 Cache; 19-23 行创建 abc 节点,并向该节点添加,查取,删除数据; 24-26 行移除节点,关闭 Cache,运行该代码端输出结果如下:

Cache has started
NODE_CREATED on node /a has occured
NODE_CREATED on node /a has occured
NODE_CREATED on node /a/b has occured
NODE_CREATED on node /a/b has occured
NODE_CREATED on node /a/b/c has occured
NODE_CREATED on node /a/b/c has occured
NODE_MODIFIED on node /a/b/c has occured
NODE_MODIFIED on node /a/b/c has occured
NODE_MODIFIED on node /a/b/c has occured
NODE_MODIFIED on node /a/b/c has occured
NODE_VISITED on node /a/b/c has occured
NODE_VISITED on node /a/b/c has occured
NODE_REMOVED on node /a/b/c has occured
NODE_REMOVED on node /a/b/c has occured
Cache has stopped

同步和异步通知

在缺省情况下,所有的通知都是同步的,因此它们在产生事件的调用者线程里发生。确保缓存 listener 实现不会占用需长时间运行的任务中的线程是一个好的办法。或者,你可以设置 CacheListener.sync 属性为 false,此时你不会在调用者线程里得到通知。

使用缓存加载器

缓存加载器是 JBoss Cache 的重要组成部分。它们允许节点持久化到磁盘或远程缓存到群集里,而且允许在缓存用尽内存时进行钝化。此外,缓存加载器允许 JBossCache 执行 “warm starts”,此时的内存状态可以从持久性存储中预加载。JBossCache 附带了大量的缓存加载器的实现。如下列出JBossCache提供的缓存加载器:

  • org.jboss.cache.loader.FileCacheLoader- 是一个基本的、基于文件系统的缓存加载器,它将数据持久化到磁盘。它是非事务性的,而且性能一般,但确实非常简单的方案。它主要用于测试,不推荐将其用在产品环境中
  • org.jboss.cache.loader.JDBCCacheLoader- 它使用 JDBC 连接来存储状态。连接可从一个内部池(使用c3p0 pooling 库)或配置好的数据源里创建并维护。这个缓存加载器连接的数据库可以是本地的,也可以是远程的
  • org.jboss.cache.loader.BdbjeCacheLoader- 它使用 Oracle 的基于文件的 BerkeleyDB 事务性数据库来持久化数据。它是事务性的,而且性能非常好但可能具有受限的许可证
  • org.jboss.cache.loader.JdbmCacheLoader- BerkeleyDB 的开源替代方案
  • org.jboss.cache.loader.tcp.TcpCacheLoader- 通过『一种 "far cache" 模式』使用 TCP 套接字来 “持久化” 数据到远程群集里
  • org.jboss.cache.loader.ClusteredCacheLoader- 用作“只读”缓存加载器,此时群集里的其他节点按状态查询。当完整状态转移的代价过高时,它就是首选的,此时状态是 lazy 加载的   

使用逐出策略(Eviction Policy)

逐出策略是缓存加载器的对应物。要确保缓存在填充时不会用尽内存,使用逐出策略是必要的。在独立线程里运行的逐出算法逐出内存状态并释放内存。如果配有缓存加载器,在需要时状态可以从缓存加载器里获得。逐出策略可对每个区进行配置,所以缓存里不同的子树可以有不同的逐出首选项。JBossCache 附带几个注册策略:

  •  org.jboss.cache.eviction.LRUPolicy- 当到达极限时逐出最近最少使用的节点
  • org.jboss.cache.eviction.LFUPolicy- 当到达极限时逐出最不经常使用的节点
  • org.jboss.cache.eviction.MRUPolicy- 当到达极限时逐出最近使用最多的节点
  • org.jboss.cache.eviction.FIFOPolicy- 当到达极限时按照先入先出顺序逐出节点
  • org.jboss.cache.eviction.ExpirationPolicy- 基于每个节点配置的过期时间逐出节点的策略
  • org.jboss.cache.eviction.ElementSizePolicy - 根据节点保持的键/值数量选择节点逐出的策略





你可能感兴趣的:(JBoss,JBoss系列)