浏览 3338 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-12-04
最后修改:2009-12-04
前言:
看过JbossCache的开发手册,发现在JbossCache与AppServer的集成章节对JNDI方式的集成没有明确说明,在Jboss App Server 下,你可以使用MBean方式管理JbossCache,更多情况下,你可能需要写一个Factory类,通过API方法来启用JbossCache了。 在企业的应用中,尤其是SOA结构的系统中,在一个应用服务器上,同时运行多个应用的场景是经常遇到的,JbossCache作为群集缓存的实现,我们希望是对一个应用服务器上的多应用,分享一个JBossCache的实例,而不是为每个应用起一个单独的。 1.Tomcat JNDI 实现 Tomcat JNDI估计大家都接触过,最常用的莫过于数据库的DataSource了。在Tomcat服务器启动后,它的JNDI会呈现Read Only模式,无法通过API方式往上发布资源。因此我们需要实现一个可以通过配置声明的JNDI JbossCache服务。 声明一个Cache逻辑相关的接口 - ClusterCache ClusterCache接口是对JbossCache的Cache接口的简化封装。代码如下: package org.wltea.cache; import java.util.Map; import java.util.Set; import org.jboss.cache.Node; public interface ClusterCache { /** * 打开cache的批处理事务 */ public void beginBatchTx(); /** * 提交cache的批处理事务 */ public void commitBatchTx(); /** * 回滚cache的批处理事务 */ public void rollbackBatchTx(); /** * 在指定的结点上缓存一个key-value型数据 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @param key 缓存对象的键 * @param value 缓存对象 */ public void cacheData(String nodePath , String key , Object value); /** * 在指定的结点上缓存整个Map数据 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @param dataMap 缓存对象的Map */ public void cacheDataMap(String nodePath , Map<String , Object> dataMap); /** * 获取指定结点上缓存的数据对象 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @param key 缓存对象的键 * @return Object 返回缓存对象 */ public Object getCacheData(String nodePath , String key); /** * 获取指定结点上缓存的数据集 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @return Map 返回缓存的Map数据集 */ public Map<String , Object> getCacheDataMap(String nodePath); /** * 清除指定结点上的缓存数据 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , */ public void clearData(String nodePath); /** * 取得指定结点上的缓存数据的记录数 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , */ public int getDataSize(String nodePath); /** * 获取指定结点上的子结点集合 * @param nodePath * @return Set<Node<String,Object>> , 返回指定结点上的子结点集合 */ public Set<Node<String,Object>> getChildren(String nodePath); /** * 移出指定结点上的单个数据 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @param key 缓存对象的键 * @return Object 返回缓存对象 */ public Object removeData(String nodePath , String key); /** * 移出整个的指定结点 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @return boolean true:成功移除结点 ; false:没有找到指定结点 */ public boolean removeNode(String nodePath); } 将ClusterCache接口实现成可发布的JNDI 引用 - 任何资源要发布成JNDI,就必须实现javax.naming.Referenceable接口,该接口只有一个方法public Reference getReference() ;它返回一个javax.naming.Reference类型的对象。JNDI的SPI端需要这个Reference 对象提供足够的信息来初始化JNDI资源实例并提供给使用者。 对于ClusterCache业务接口的Referenceable实现如下: /** * */ package org.wltea.cache; import java.util.Map; import java.util.Set; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; /** * 群集缓存JNDI实现 * @author 林良益 * */ public class JNDIClusterCache implements Referenceable , ClusterCache{ //配置文件路径 private static String CONFIG_FILE_LOCATION = "jbosscache.cfg.xml"; //Jboss Cache 工厂 private static final CacheFactory<String , Object> factory = new DefaultCacheFactory<String , Object>(); //Jboss cache private static Cache<String , Object> cache = null; //默认JNDI工厂名 private String factoryClassName = "org.apache.naming.factory.BeanFactory"; public String getFactoryClassName() { return factoryClassName; } public void setFactoryClassName(String factoryClassName) { this.factoryClassName = factoryClassName; } public JNDIClusterCache(){ if(cache == null){ synchronized(JNDIClusterCache.class){ if(cache == null){ cache = factory.createCache(CONFIG_FILE_LOCATION); } } } } /** * 获取JNDI引用对象 */ public Reference getReference() throws NamingException { Reference ref=new Reference(getClass().getName(),getFactoryClassName(),null); //添加Reference属性 //ref.add(new StringRefAddr("location",location)); //ref.add(new StringRefAddr("state",state)); return ref; } /** * 打开cache的批处理事务 */ public void beginBatchTx(){ cache.startBatch(); } /** * 提交cache的批处理事务 */ public void commitBatchTx(){ cache.endBatch(true); } /** * 回滚cache的批处理事务 */ public void rollbackBatchTx(){ cache.endBatch(false); } /** * 在指定的结点上缓存一个key-value型数据 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @param key 缓存对象的键 * @param value 缓存对象 */ public void cacheData(String nodePath , String key , Object value){ Fqn<String> fqn = Fqn.fromString(nodePath); cache.put(fqn, key, value); } /** * 在指定的结点上缓存整个Map数据 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @param dataMap 缓存对象的Map */ public void cacheDataMap(String nodePath , Map<String , Object> dataMap){ Fqn<String> fqn = Fqn.fromString(nodePath); cache.put(fqn, dataMap); } /** * 获取指定结点上缓存的数据对象 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @param key 缓存对象的键 * @return Object 返回缓存对象 */ public Object getCacheData(String nodePath , String key){ Fqn<String> fqn = Fqn.fromString(nodePath); return cache.get(fqn, key); } /** * 获取指定结点上缓存的数据集 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @return Map 返回缓存的Map数据集 */ public Map<String , Object> getCacheDataMap(String nodePath){ Fqn<String> fqn = Fqn.fromString(nodePath); return cache.getData(fqn); } /** * 清除指定结点上的缓存数据 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , */ public void clearData(String nodePath){ Fqn<String> fqn = Fqn.fromString(nodePath); Node<String,Object> node = cache.getNode(fqn); node.clearData(); } /** * 取得指定结点上的缓存数据的记录数 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , */ public int getDataSize(String nodePath){ Fqn<String> fqn = Fqn.fromString(nodePath); Node<String,Object> node = cache.getNode(fqn); return node.dataSize(); } /** * 获取指定结点上的子结点集合 * @param nodePath * @return Set<Node<String,Object>> , 返回指定结点上的子结点集合 */ public Set<Node<String,Object>> getChildren(String nodePath){ Fqn<String> fqn = Fqn.fromString(nodePath); Node<String,Object> node = cache.getNode(fqn); return node.getChildren(); } /** * 移出指定结点上的单个数据 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @param key 缓存对象的键 * @return Object 返回缓存对象 */ public Object removeData(String nodePath , String key){ Fqn<String> fqn = Fqn.fromString(nodePath); return cache.remove(fqn, key); } /** * 移出整个的指定结点 * @param nodePath 缓存结点的路径 如:/aaa/bbb/ccc , * @return boolean true:成功移除结点 ; false:没有找到指定结点 */ public boolean removeNode(String nodePath){ Fqn<String> fqn = Fqn.fromString(nodePath); return cache.removeNode(fqn); } } 要说明的是,ClusterCache的实现中,对Jbosscache采用了单一实例。根据Jbosscache的API文档说明,其Cache的实现是ThreadSafe的。 2.在Tomcat的服务器全局域发布JbossCache的JNDI服务类 发布Tomcat的全局JNDI相信大家都做过,这里为了读者的思路连续,我再罗嗦一次吧。 发布jar包 进行全局JNDI的发布,需要将jar包拷贝到Tomcat的\lib目录下,因此我们要把上述的两个类打包成一个jar,比如:ClusterCache.jar,同时带上JbossCache的JAR包拷贝到lib目录下,JbossCache相关包清单如下:
此外,在JNDIClusterCache中,我们声明了jbosscache的配置文件为jbosscache.cfg.xml,因此我们也要把该配置文件拷到lib目录中。 配置Tomcat全局的资源 在Tomcat的\conf目录下,找到context.xml文件,在其中加上以下配置 <Resource name="ik/clustercache" auth="Container" type="com.wltea.cache.JNDIClusterCache" factory="org.apache.naming.factory.BeanFactory"/> 配置Web App应用的客户端JNDI引用 在要使用JNDI Cache应用的web.xml中加上对cache资源的引用配置 <resource-ref> <description>JNDI Cluster Cache</description> <res-ref-name>ik/clustercache</res-ref-name> <res-type>com.wltea.cache.ClusterCache</res-type> <res-auth>Container</res-auth> </resource-ref> 在应用取得JNDI上的JbossCache // Put your code here Context initContext; try { initContext = new InitialContext(); Context envContext = (Context)initContext.lookup("java:/comp/env"); ClusterCache cache = (ClusterCache)envContext.lookup("ik/clustercache"); } catch (NamingException e) { // TODO Auto-generated catch block e.printStackTrace(); } 到此为止,我们已经实现了将JbossCache发布到Tomcat的JNDI上。这样做的好处是,一个应用服务器只有一个JbossCache服务实例,群集服务器Cache间的同步将更有效率,内存及CPU资源消耗更少。 但这样做也有个问题,就是JNDI上的Jbosscache将无法使用分布在各个应用中CacheListener监听器。原因是Tomcat的全局lib的classLoader比web应用的classloader在类加载层次上更底层一些,简单的说,就是web-inf\lib中的类可以引用全局lib的类,但全局lib的类没法反向应用web-inf\lib中的jar类,除非你把CacheListener一起发布到全局的lib下。 【全文完】 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-07-24
兄弟,想不到你先干了我想干的事情,谢谢,太能理解你的思路,
|
|
返回顶楼 | |
发表时间:2010-07-24
呵呵,这篇文章发了好久了,终于有个回帖的了,看来在Tomcat下使用JbossCache的用户不多啊
|
|
返回顶楼 | |
发表时间:2010-07-24
3年的jbosscache用户表示使用jbosscache鸭梨很大。
|
|
返回顶楼 | |
发表时间:2010-07-24
IcyFenix 写道 3年的jbosscache用户表示使用jbosscache鸭梨很大。
前提在Tomcat环境下使用JbossCache的楼主,看来楼上的回复,表示西瓜很大 |
|
返回顶楼 | |
发表时间:2010-07-24
今年以来我们起码处理了5次因为jbosscache nakack栈导致的OOM……准确的说是jgroup,jbosscache在大规模应用时我觉得已经不是同步速度慢的问题了
|
|
返回顶楼 | |
发表时间:2010-07-25
IcyFenix 写道 今年以来我们起码处理了5次因为jbosscache nakack栈导致的OOM……准确的说是jgroup,jbosscache在大规模应用时我觉得已经不是同步速度慢的问题了
感谢提醒,我目前改行互联网了,JbossCache已经过气了,现在主要使用Memcached了,JbossCache建议用在群集系统同步控制领域,比如:服务路由表,用来避免单点故障。这些数据相对固定,增删改频度低,不容易造成OOM |
|
返回顶楼 | |
发表时间:2010-08-28
LZ把你的代码拷进去了 代码报错了 提示下 你的jbosscache 版本是多少。。。。
|
|
返回顶楼 | |