首页 > 代码库 > EJB+memcache

EJB+memcache

首先我们通过下图 了解到memcache在系统中处于大概什么的位置


然后我们了解下Memcached简介
1.1. 什么是Memcached  Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。
它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态、数据库驱动网站的速度。
Memcached基于一个存储键/值对的HashMap,它并不提供冗余(复制其HashMap条目),当某个服务器停止运行或崩溃了,所有存放在服务器上的键/值对都将丢失。
但目前Memcached的客户端和代理程序可以提供多服务器的并联方式,可以提供一定的处理能力。
1.2. Memcached与其他分布式缓存的区别  Memcached与其它常用的分布式缓存(例如EhCache、OSCache、JBoss Cache)最主要的区别在于Memcached采用集中式缓存方式(即一台或多台缓存服务器为所有应用系统提供缓存能力),自身不提供集群能力,不提供缓存复制功能;而其他分布式缓存系统采用分布式缓存方式,各个应用系统内部提供数据缓存的能力,
多个缓存间采用组播或点对点的方式进行缓存同步。 

在性能上来看,Memcached比其它分布式缓存系统低一半以上(未考虑大量数据在其它缓存系统进行复制的影响);但从管理方面来看,Memcached的缓存采用集中管理的模式,应用系统可以水平扩展,

而其它分布式缓存在水平扩展的同时,必须同时调整缓存复制策略,一旦应用服务器节点大量扩展,对于缓存服务器间的数据复制将成几何数增加。

然后我们从官网http://memcached.org/下载 适合自身系统的memcache安装包

下载好之后解压

因为我是windows 操作

所以解压后 直接CMD到改目录

到memcached根目录 
1、安装 
memcached.exe –d install  

2、启动 
memcached.exe -d start 

现在来试试是否可以连接: 

使用方法为“telnet ip 端口号”,登录后使用“stats”命令 
cmd下 
telnet 192.168.1.135 11211(注意:这里只能用IP 不能用 localhost 或127.0.0.1  11211端口默认的) 

然后使用:stats命令可以看到memcached的使用情况如下: 

time: 1255537291 服务器当前的unix时间戳 
total_items: 54 从服务器启动以后存储的items总数量 
connection_structures: 19 服务器分配的连接构造数 
version: 1.2.6 memcache版本 
limit_maxbytes: 67108864 分配给memcache的内存大小(字节) 
cmd_get: 1645 get命令(获取)总请求次数 
evictions: 0 为获取空闲内存而删除的items数(分配给memcache的空间用满后需 要删除旧的items来得到空间分配给新的items) 
total_connections: 19 从服务器启动以后曾经打开过的连接数 
bytes: 248723 当前服务器存储items占用的字节数 
threads: 1 当前线程数 
get_misses: 82 总未命中次数 
pointer_size: 32 当前操作系统的指针大小(32位系统一般是32bit) 
bytes_read: 490982 总读取字节数(请求字节数) 
uptime: 161 服务器已经运行的秒数
curr_connections: 18 当前打开着的连接数 
pid: 2816 memcache服务器的进程ID 
bytes_written: 16517259 总发送字节数(结果字节数)
get_hits: 1563 总命中次数 
cmd_set: 54 set命令(保存)总请求次数 
curr_items: 28 服务器当前存储的items数量

 

3.一些常用命令

-p 监听的端口
-l 连接的IP地址, 默认是本机
-d start 启动memcached服务
-d restart 重起memcached服务
-d stop|shutdown 关闭正在运行的memcached服务
-d install 安装memcached服务
-d uninstall 卸载memcached服务
-u 以的身份运行 (仅在以root运行的时候有效)
-m 最大内存使用,单位MB。默认64MB
-M 内存耗尽时返回错误,而不是删除项
-c 最大同时连接数,默认是1024
-f 块大小增长因子,默认是1.25
-n 最小分配空间,key+value+flags默认是48
-h 显示帮助

 

到此整个的memcache安装结束

 

那么该怎么样集成到EJB呢?

客户端调用有两种

一种是java_memcached-release_2.6.3依赖于此包

二种是alisoft-xplatform-asf-cache-2.5.1依赖于此包

我选择的是第二种原因是阿里软件的架构师岑文初进行封装的。里面的注释都是中文的,比较好详情http://www.infoq.com/cn/articles/memcached-java

重点我就讲这种结合EJB的使用

首先对xxxEJB引入alisoft-xplatform-asf-cache-2.5.1.jar

然后配置memcache.xml文件 记住一定要放在classpath下否则找不到

<memcached>
    <!--memcached Client的配置,也就是一个IMemcachedCache的配置。Name必须填,表示Cache的名称,socketpool必须填,
    表示使用的远程通信连接池是哪一个,参看后面对于socketpool的定义。后面都是可选的,第三个参数表示传输的时候是否压缩,第四个参数表示默认的编码方式 -->
 <client name="mclient1" socketpool="pool1" compressEnable="true" defaultEncoding="UTF-8" >
        <!--errorHandler></errorHandler可定义错误处理类,一般不需要定义 -->
    </client>
    <client name="mclient2" socketpool="pool2" compressEnable="true" defaultEncoding="UTF-8" >
    </client> 
     <!--socketpool是通信连接池定义,每一个memcached Client要和服务端交互都必须有通信连接池作为底层数据通信的支持,name必填,表示名字,
     同时也是memcached client指定socketpool的依据,failover表示对于服务器出现问题时的自动修复。
     initConn初始的时候连接数,minConn表示最小闲置连接数,maxConn最大连接数,maintSleep表示是否需要延时结束(最好设置为0,如果设置延时的话那么就不能够立刻回收所有的资源,
     如果此时从新启动同样的资源分配,就会出现问题),nagle是TCP对于socket创建的算法,socketTO是socket连接超时时间,aliveCheck表示心跳检查,确定服务器的状态。
     Servers是memcached服务端开的地址和ip列表字符串,weights是上面服务器的权重,必须数量一致,否则权重无效  -->
    <socketpool name="pool1" failover="true" initConn="10" minConn="5" maxConn="250" maintSleep="0"
        nagle="false" socketTO="3000" aliveCheck="true">
        <servers>192.168.1.137:11211</servers>
        <weights>5</weights>
    </socketpool> 
    <socketpool name="pool2" failover="true" initConn="10" minConn="5" maxConn="250" maintSleep="0"
        nagle="false" socketTO="3000" aliveCheck="true">
        <servers>192.168.0.53:11211</servers>
        <weights>5</weights>
    </socketpool>
     <!--虚拟集群设置,这里将几个client的cache设置为一个虚拟集群,当对这些IMemcachedCache作集群操作的时候,就会自动地对集群中所有的Cache作插入,寻找以及删除的操作,做一个虚拟交互备份  -->
    <cluster name="cluster1">
        <memCachedClients>mclient1,mclient2</memCachedClients>
    </cluster>           
</memcached>

然后创建一个类进行对象初始化

 //缓存管理器变量 
    private ICacheManager manager; 
    //缓存变量 
    private IMemcachedCache cache; 
     
    //缓存名称 
    /********************************单例模式(饿汉式)  begin***********************************************/ 
     
    private  static CacheHandler cacheHandler=new CacheHandler(); 
    /**
     * 私有构造函数
     */ 
    private CacheHandler(){ 
        System.out.println("---------------创建单例 begin--------------"); 
        initCache(); 
        System.out.println("---------------创建单例 end--------------"); 
    } 
    public static CacheHandler getInstance(){ 
        return cacheHandler; 
    } 
    /********************************单例模式(饿汉式)  end***********************************************/ 
     
    /**
     * @MethodName  : initCache
     * @Description : 初始化缓存
     */ 
    private void initCache(){ 
        //1.创建cachemanager 
     manager=CacheUtil.getCacheManager(IMemcachedCache.class,MemcachedCacheManager.class.getName());;
        //如果cache是空,則手動創建 
        if(cache==null){ 
   manager.start();
   cache = (IMemcachedCache) manager.getCache("mclient1");
        } 
    } 
    /**
     * @MethodName  : getCache
     * @Description : 外部通过get方法拿到cache后,可清空cache
     * @return
     */ 
    public IMemcachedCache getCache() { 
        return cache; 
    } 
    
    /**
     * @MethodName  : clearCache
     * @Description : 清理缓存
     */ 
    public void clearCache(){ 
        if(cache!=null){ 
            cache.clear();
        } 
    }

因为我是测试 所以利用EJB的拦截器

首先创建一个类对查询的方法进行缓存

@AroundInvoke 
    public Object processCache(InvocationContext context) throws Exception{ 
        String targetName = context.getTarget().getClass().getName(); 
        String methodName = context.getMethod().getName(); 
        Object[] arguments =context.getParameters(); 
        Object result; 
        //获取缓存对象 
        IMemcachedCache cache = CacheHandler.getInstance().getCache(); 
        //如果方法名以find、query、或get开头则执行缓存策略 
        if(methodName.startsWith("find") || methodName.startsWith("get") || methodName.startsWith("query")){ 
            String cacheKey = getCacheKey(targetName, methodName, arguments); 
            Object element = cache.get(cacheKey); 
            if (element == null) { 
                result = context.proceed(); // 执行目标方法,并保存目标方法执行后的返回值 
                cache.put(cacheKey, result);
                element=result;
                System.out.println("==========================createCache-->" + cacheKey); 
            } else { 
                System.out.println("==========================hit Cache-->" + cacheKey); 
            } 
            return element; 
        } 
         
        //否则直接执行目标方法 
        return context.proceed(); 
    } 
     
    /**
     * @MethodName  : getCacheKey
     * @Description : 获得cache key的方法,cache key是Cache中一个Element的唯一标识 cache key包括
     * 包名+类名+方法名+各个参数的具体指,如com.jiwu.service.UserServiceImpl.getAllUser
     * @param targetName    类名
     * @param methodName    方法名
     * @param arguments     方法实参数组
     * @return                      cachekey
     */ 
    private String getCacheKey(String targetName, String methodName, 
            Object[] arguments) { 
        StringBuffer sb = new StringBuffer(); 
        sb.append(targetName).append(".").append(methodName); 
        if ((arguments != null) && (arguments.length != 0)) { 
            for (int i = 0; i < arguments.length; i++) { 
                if(arguments[i] instanceof String[]){ 
                    String[] strArray = (String[])arguments[i]; 
                    sb.append("."); 
                    for(String str : strArray){ 
                        sb.append(str); 
                    } 
                }else{ 
                    sb.append(".").append(arguments[i]); 
                } 
                 
            } 
        } 
         
        return sb.toString(); 
    }

然后manager引用的时候 只要在类上引用即可 例如以下

@Interceptors(CacheInterceptor.class)
public class AdminManagerImpl implements IAdminManager

这样的话就会对该类中所有的find,get,query 开始的方法进行拦截 缓存

 

由此整个EJB和memcache已关联,部署的时候还需要在deploy 同等目录的LIB下放入引用alisoft-xplatform-asf-cache-2.5.1.jar

 

OK启动你的JBOSS 打上你的断点看是否已经成功