packagecom.common;importjava.io.File;importjava.text.DecimalFormat;importjava.util.ArrayList;importjava.util.Calendar;importjava.util.LinkedList;importjava.util.List;importjava.util.Date;importjava.util.HashMap;importjava.util.LinkedHashMap;importjava.util.Map;importorg.apache.commons.net.telnet.TelnetClient;importorg.dom4j.Document;importorg.dom4j.Element;importorg.dom4j.Node;importorg.dom4j.io.SAXReader;importcom.danga.MemCached.MemCachedClient;importcom.danga.MemCached.SockIOPool;/*** @category memcached内存数据库操作类
*@author网无忌(netwild)
* @email inetwild@gmail.com
* @qq 52100641
*@version1.0*/
public classMemCached {//是否启用MemCached内存数据库
protected static boolean enUsed = true;//创建全局唯一的可实例化对象
protected static MemCached memCached = newMemCached();//初始化MemCached客户端对象
protected static MemCachedClient memClient = newMemCachedClient();//定义MemCached服务器运行环境配置文件名称
private static final String MemCachedConfigFile_NAME = "MemCachedConfig.xml";//定义可用的MemCached服务器列表,用于分布式存储
private static String[] serverListArr = new String[1];//定义各MemCached服务器的负载权重列表,与服务器列表按先后顺序对应
private static Integer[] weightListArr = new Integer[1];;//定义MemCached服务器运行环境表,配置文件中关于参数相关数据将保存到该表
private static MapserverConfig;//定义MemCached服务器运行状态表,用于保存各状态的中文解释
protected static HashMapstatsItems;//设置全局静态参数,以下代码在整个服务器运行周期内仅运行一次!
static{//初始化MemCached运行环境配置//首先初始化各参数默认值,然后加载配置文件,遍历其中的参数值并进行覆盖。
initConfig();if(enUsed){ //如果已启用memcached缓存服务//获取socke连接池的实例对象
SockIOPool pool =SockIOPool.getInstance();//设置可用的MemCached服务器信息,实现分布式存储
pool.setServers(serverListArr);//设置各MemCached服务器的负载权重,根据可支配内存实现负载均衡
pool.setWeights(weightListArr);//设置初始连接数
pool.setInitConn(Integer.parseInt(serverConfig.get("initConn").toString()));//设置最小连接数
pool.setMinConn(Integer.parseInt(serverConfig.get("minConn").toString()));//设置最大连接数
pool.setMaxConn(Integer.parseInt(serverConfig.get("maxConn").toString()));//设置连接最大空闲时间
pool.setMaxIdle(Long.parseLong(serverConfig.get("maxIdle").toString()));//设置主线程的睡眠时间,每隔该时间维护一次各连接线程状态
pool.setMaintSleep(Long.parseLong(serverConfig.get("maintSleep").toString()));//关闭nagle算法
pool.setNagle(false);//读取操作的超时限制
pool.setSocketTO(Integer.parseInt(serverConfig.get("socketTO").toString()));//连接操作的超时限制,0为不限制
pool.setSocketConnectTO(Integer.parseInt(serverConfig.get("socketConnTO").toString()));//初始化连接池
pool.initialize();//压缩设置,超过指定大小的数据都会被压缩//从java_memcached-release_2.6.1开始已经不再支持内置的数据压缩功能//memClient.setCompressEnable(Boolean.parseBoolean(serverConfig.get("compressEnable").toString()));//memClient.setCompressThreshold(Long.parseLong(serverConfig.get("compressThreshold").toString()));
}
}/*** @category 初始化MemCached运行环境配置
* @category 注:该方法在整个服务器周期内仅运行一次*/
protected static voidinitConfig(){//初始化可用的MemCached服务器列表默认值(本机)
serverListArr[0] = "127.0.0.1:11211";
weightListArr[0] = 1;//初始化MemCached服务器运行环境表(默认值),当某参数未在配置文件中进行定义时,将使用该默认值
serverConfig = new HashMap(){private static final long serialVersionUID = 1L;
{
put("initConn", "5"); //设置初始连接数
put("minConn", "5"); //设置最小连接数
put("maxConn", "250"); //设置最大连接数
put("maxIdle", "21600000"); //设置连接最大空闲时间(6小时)
put("maintSleep", "30"); //设置主线程的睡眠时间(30秒)
put("socketTO", "10000"); //读取操作的超时限制(10秒)
put("socketConnTO", "0"); //连接操作的超时限制(不限制)
put("compressEnable", "true"); //是否启用自动压缩(启用)
put("compressThreshold", "65536"); //超过指定大小的数据都会被压缩(64K)
}
};//开始读取配置文件,并将其中的参数值向默认环境表中进行覆盖
String filePath = Thread.currentThread().getContextClassLoader().getResource("MemCachedConfig.xml").getPath().substring(1);
File file= new File(filePath.replaceAll("%20"," "));try{if(file.exists()){ //如果可以成功加载配置文件
SAXReader sr = newSAXReader();
Document doc=sr.read(file);
Element Root= doc.getRootElement(); //获得根节点
Element Enabled = (Element)Root.selectSingleNode("Enabled"); //获得是否启用memcached节点
Element Servers = (Element)Root.selectSingleNode("Servers"); //获得可用的服务器列表父节点
Element Config = (Element)Root.selectSingleNode("Config"); //获得运行环境参数列表父节点
enUsed = Boolean.parseBoolean(Enabled.getText()); //是否启用memcached缓存服务
List serverDoms = Servers.elements(); //备用的服务器列表
List serverUsed = new ArrayList(); //经检测,实际可用的服务器列表
TelnetClient telnet = new TelnetClient(); //初始化Telnet对象,用来检测服务器是否可以成功连接
telnet.setConnectTimeout(5000); //连接超时:5秒
for(Element serverTmp : serverDoms){try{
telnet.connect(serverTmp.attributeValue("host"), Integer.parseInt(serverTmp.attributeValue("post"))); //连接到服务器
telnet.disconnect(); //断开连接
serverUsed.add(serverTmp); //连接成功,将服务器添加到实际可用列表
}catch(Exception e){}
}int serverCount = serverUsed.size(); //经检测,实际可用的服务器个数
if(serverCount == 0){ //没有发现实际可用的服务器,返回
enUsed = false;return;
}
serverListArr= new String[serverCount]; //初始化服务器地址及端口号数组
weightListArr = new Integer[serverCount]; //初始化服务器负载权重数组
for(int ind=0; ind < serverCount; ind++){ //向服务器数组进行赋值
serverListArr[ind] = serverUsed.get(ind).attributeValue("host") + ":" + serverUsed.get(ind).attributeValue("post");
weightListArr[ind]= Integer.parseInt(serverUsed.get(ind).attributeValue("weight").toString());
}
Object[] serverConfigArr= serverConfig.keySet().toArray(); //返回服务器运行环境参数列表,用于遍历配置文件
for(Object cfgItem : serverConfigArr){
Node node= Config.selectSingleNode("//property[@name='" + cfgItem + "']"); //查找指定的参数节点
if(node == null) continue; //如果该参数节点不存在,则继续查找下一个参数,该参数将采用默认值
Element configNode =(Element)node;
serverConfig.put(cfgItem.toString(), configNode.getTextTrim());//添加配置文件中定义的参数值
}
}
}catch(Exception e){
System.out.println(e.toString());
}//初始化MemCached服务器运行状态表,对各状态进行中文解释
statsItems = new HashMap(){
{
put("pid", "MemCached服务进程ID");
put("version", "MemCached服务版本");
put("pointer_size", "MemCached服务器架构");
put("time", "服务器当前时间");
put("uptime", "服务器本次启动以来,总共运行时间");
put("connection_structures", "服务器分配的连接结构数");
put("total_connections", "服务器本次启动以来,累计响应连接总次数");
put("curr_connections", "当前打开的连接数");
put("limit_maxbytes", "允许服务支配的最大内存容量");
put("bytes", "当前已使用的内存容量");
put("bytes_written", "服务器本次启动以来,写入的数据量");
put("bytes_read", "服务器本次启动以来,读取的数据量");
put("total_items", "服务器本次启动以来,曾存储的Item总个数");
put("curr_items", "当前存储的Item个数");
put("cmd_get", "服务器本次启动以来,执行Get命令总次数");
put("get_hits", "服务器本次启动以来,Get操作的命中次数");
put("get_misses", "服务器本次启动以来,Get操作的未命中次数");
put("cmd_set", "服务器本次启动以来,执行Set命令总次数");
}
};
}/*** @category 保护型构造方法,不允许实例化!*/
protectedMemCached() {
}/*** @category 操作类入口:获取唯一实例.
*
*@returnMemCached对象*/
public staticMemCached getInstance() {returnmemCached;
}/*** @category 返回是否已经启用memcached内存服务器
*
*@returnboolean*/
public static booleanused(){returnenUsed;
}/*** @category 插入新记录.
* @category 前提:记录的Key在缓存中不存在
*@paramkey 记录的主键
*@paramvalue 记录的内容
*@returnboolean 操作结果*/
public booleanadd(String key, Object value) {if(!enUsed){return false;
}else{returnmemClient.add(key, value);
}
}/*** @category 插入新记录并设置超时日期
* @category 前提:记录的Key在缓存中不存在
*@paramkey 记录的主键
*@paramvalue 记录的内容
*@paramexpiryDate 超时日期
*@returnboolean 操作结果*/
public booleanadd(String key, Object value, Date expiryDate) {if(!enUsed){return false;
}else{returnmemClient.add(key, value, expiryDate);
}
}/*** @category 插入新记录并设置超时天数
* @category 前提:记录的Key在缓存中不存在
*@paramkey 记录的主键
*@paramvalue 记录的内容
*@paramexpiryDays 超时天数
*@returnboolean 操作结果*/
public boolean add(String key, Object value, intexpiryDays){if(!enUsed){return false;
}else{
Calendar calendar=Calendar.getInstance();
calendar.setTime(newDate());
calendar.add(Calendar.DATE,expiryDays);//增加天数
returnmemClient.add(key, value, calendar.getTime());
}
}/*** @category 插入新记录或更新已有记录
* @category 解释:记录的Key在缓存中不存在则插入;否则更新
*@paramkey 记录的主键
*@paramvalue 记录的内容
*@returnboolean 操作结果*/
public booleanset(String key, Object value){if(!enUsed){return false;
}else{returnmemClient.set(key, value);
}
}/*** @category 插入新记录或更新已有记录,并设置超时日期
* @category 解释:记录的Key在缓存中不存在则插入;否则更新
*@paramkey 记录的主键
*@paramvalue 记录的内容
*@paramexpiryDate 超时日期
*@returnboolean 操作结果*/
public booleanset(String key, Object value, Date expiryDate){if(!enUsed){return false;
}else{returnmemClient.set(key, value, expiryDate);
}
}/*** @category 插入新记录或更新已有记录,并设置超时天数
* @category 解释:记录的Key在缓存中不存在则插入;否则更新
*@paramkey 记录的主键
*@paramvalue 记录的内容
*@paramexpiryDate 超时天数
*@returnboolean 操作结果*/
public boolean set(String key, Object value, intexpiryDays){if(!enUsed){return false;
}else{
Calendar calendar=Calendar.getInstance();
calendar.setTime(newDate());
calendar.add(Calendar.DATE,expiryDays);//增加天数
returnmemClient.set(key, value, calendar.getTime());
}
}/*** @category 更新已有记录
* @category 前提:记录的Key在缓存中已经存在
*@paramkey 记录的主键
*@paramvalue 记录的内容
*@returnboolean 操作结果*/
public booleanreplace(String key, Object value) {if(!enUsed){return false;
}else{returnmemClient.replace(key, value);
}
}/*** @category 更新已有记录,并设置超时日期
* @category 前提:该值在缓存中已经存在
*@paramkey 记录的主键
*@paramvalue 记录的内容
*@paramexpiryDate 超时日期
*@returnboolean 操作结果*/
public booleanreplace(String key, Object value, Date expiryDate) {if(!enUsed){return false;
}else{returnmemClient.replace(key, value, expiryDate);
}
}/*** @category 更新已有记录,并设置超时天数
* @category 前提:该值在缓存中已经存在
*@paramkey 记录的主键
*@paramvalue 记录的内容
*@paramexpiryDays 超时天数
*@returnboolean 操作结果*/
public boolean replace(String key, Object value, intexpiryDays) {if(!enUsed){return false;
}else{
Calendar calendar=Calendar.getInstance();
calendar.setTime(newDate());
calendar.add(Calendar.DATE,expiryDays);//增加天数
returnmemClient.replace(key, value, calendar.getTime());
}
}/*** @category 返回单条记录
*
*@paramkey 记录的主键
*@return记录的内容*/
publicObject get(String key) {if(!enUsed){return null;
}else{returnmemClient.get(key);
}
}/*** @category 返回多条记录
*
*@paramkeys 记录的主键数组
*@returnMap 多条记录的内容*/
public Mapget(String[] keys) {if(!enUsed){return null;
}else{returnmemClient.getMulti(keys);
}
}/*** @category 删除记录
* @category 执行该方法之后,使用stats的统计结果会同步更新
*@paramkey 记录的主键
*@return操作结果*/
public booleandelete(String key){if(!enUsed){return false;
}else{returnmemClient.delete(key);
}
}/** ****************************************************************************************************************
* 下面的6个方法都是为了对memcached服务器进行监控及管理所用的,可能对服务器造成阻塞,所以除Debug以外,不推荐使用!*/
/*** @category 清空全部缓存数据。*慎用!!
* @category 执行该方法之后,使用stats的统计结果不会马上发生变化,每get一个不存在的item之后,该item的值才会被动清空
*@return操作结果*/
public booleanflushAll(){if(!enUsed){return false;
}else{returnmemClient.flushAll();
}
}/*** @category 返回可用的服务器列表
*@return数组(服务器地址:端口)*/
publicString[] servers(){if(!enUsed) return null;returnserverListArr;
}/*** @category 返回所有缓存服务器当前的运行状态
*@return*
* Map
* |-- Key : ServerName01, Value : LinkedHashMap
* | |-- Key : statName01, Value : statValue
* | |-- ...
* |
* |-- Key : ServerName02, Value : LinkedHashMap
* | |-- Key : statName01, Value : statValue
* | |-- ...
* |
* |-- ...
**/
public Map>stats(){if(!enUsed) return null;
Map> retMap = new HashMap>();for(String server : serverListArr){
LinkedHashMap serverStats = this.stats(server);
retMap.put(server, serverStats);
}returnretMap;
}/*** @category 返回指定服务器当前的运行状态
*@paramserver 服务器地址:端口
*
* 优化: 参数名称中文显示
* 优化: 毫秒数转换为小时
* 优化: 字节数转换为MB或KB
* 优化: UNIX时间戳转换为标准时间
* 优化: 参数显示顺序更加直观
*
*@returnLinkedHashMap 可对Map进行有序遍历
**/
public LinkedHashMapstats(String server){if(!enUsed) return null;
LinkedHashMap retMap = new LinkedHashMap();
Map> statsList = memClient.stats(newString[]{server});//System.out.println(memClient.stats().toString());
DecimalFormat format = new DecimalFormat("0.0");for(Object serverTitle : statsList.keySet().toArray()){
Map serverStats = (Map)statsList.get(serverTitle);
retMap.put(statsItems.get("pid"), serverStats.get("pid").replaceAll("\\r\\n", ""));
retMap.put(statsItems.get("version"), serverStats.get("version").replaceAll("\\r\\n", ""));
retMap.put(statsItems.get("pointer_size"), serverStats.get("pointer_size").replaceAll("\\r\\n", "") + "位");
retMap.put(statsItems.get("time"), new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(Long.parseLong(serverStats.get("time").replaceAll("\\r\\n", "")) * 1000)).toString());
retMap.put(statsItems.get("uptime"), format.format(Double.parseDouble(serverStats.get("uptime").replaceAll("\\r\\n", ""))/(60*60)) + "小时");
retMap.put(statsItems.get("connection_structures"), serverStats.get("connection_structures").replaceAll("\\r\\n", ""));
retMap.put(statsItems.get("total_connections"), serverStats.get("total_connections").replaceAll("\\r\\n", ""));
retMap.put(statsItems.get("curr_connections"), serverStats.get("curr_connections").replaceAll("\\r\\n", ""));
retMap.put(statsItems.get("limit_maxbytes"), format.format(Double.parseDouble(serverStats.get("limit_maxbytes").replaceAll("\\r\\n", ""))/(1024*1024)) + "MB");
retMap.put(statsItems.get("bytes"), format.format(Double.parseDouble(serverStats.get("bytes").replaceAll("\\r\\n", ""))/(1024*1024)) + "MB");
retMap.put(statsItems.get("bytes_written"), format.format(Double.parseDouble(serverStats.get("bytes_written").replaceAll("\\r\\n", ""))/(1024)) + "KB");
retMap.put(statsItems.get("bytes_read"), format.format(Double.parseDouble(serverStats.get("bytes_read").replaceAll("\\r\\n", ""))/(1024)) + "KB");
retMap.put(statsItems.get("total_items"), serverStats.get("total_items").replaceAll("\\r\\n", ""));
retMap.put(statsItems.get("curr_items"), serverStats.get("curr_items").replaceAll("\\r\\n", ""));
retMap.put(statsItems.get("cmd_get"), serverStats.get("cmd_get").replaceAll("\\r\\n", ""));
retMap.put(statsItems.get("get_hits"), serverStats.get("get_hits").replaceAll("\\r\\n", ""));
retMap.put(statsItems.get("get_misses"), serverStats.get("get_misses").replaceAll("\\r\\n", ""));
retMap.put(statsItems.get("cmd_set"), serverStats.get("cmd_set").replaceAll("\\r\\n", ""));
}returnretMap;
}/*** @category 返回指定服务器及Slab中当前使用的item列表
*@paramserver 服务器地址:端口
*@paramslab SlabId
*@paramcounter 最多显示items条数
*@return
*/
public LinkedList items(String server, int slab, intcounter){if(!enUsed) return null;
LinkedList ret = new LinkedList();
Map itemsKey = memClient.statsCacheDump(newString[]{server}, slab, counter).get(server);for(Object key : itemsKey.keySet().toArray()){
ret.add(key.toString());
}returnret;
}/*** @category 返回指定服务器当前使用的SlabsID列表
*@paramserver 服务器地址:端口
*@return
*/
public LinkedListslabs(String server){if(!enUsed) return null;
LinkedList slabsId = new LinkedList();
Map itemsMap = memClient.statsItems(newString[]{server}).get(server);
Object[] itemsArr=itemsMap.keySet().toArray();for(int i=0, len=itemsArr.length; i
slabsId.add(Integer.parseInt(itemsArr[i].toString().split(":")[1]));
}returnslabsId;
}/** 上面的6个方法都是为了对memcached服务器进行监控及管理所用的,可能对服务器造成阻塞,所以除Debug以外,不推荐使用!
* *****************************************************************************************************************/
/*** 使用示例*/
public static voidmain(String[] args) {//初始化memcached操作类对象
MemCached cache =MemCached.getInstance();//验证memcached服务是否已启用
if(!cache.used()){
System.out.println("memcached服务未启用!");return;
}//插入新记录
System.out.println("开始插入新记录(add):\r\n===================================");
System.out.println("keyTest01:" + cache.add("keyTest01", "keyTest01Content"));
System.out.println("keyTest02:" + cache.add("keyTest02", "keyTest02Content"));
System.out.println("插入新记录操作完成\r\n===================================");//读取单条记录
System.out.println("读取单条记录(get):\r\n===================================");
System.out.println("keyTest01:" + cache.get("keyTest01"));
System.out.println("keyTest02:" + cache.get("keyTest02"));
System.out.println("读取单条记录操作完成\r\n===================================");//读取多条记录
System.out.println("读取多条记录(add):\r\n===================================");
System.out.println("keyTest01、keyTest02:" + cache.get(new String[]{"keyTest01", "keyTest02"}));
System.out.println("读取多条记录操作完成\r\n===================================");//修改记录值
System.out.println("修改记录值(replace):\r\n===================================");
System.out.println("keyTest01:" + cache.get("keyTest01"));
System.out.println("keyTest01:" + cache.replace("keyTest01", "keyTest01ContentReplace!"));
System.out.println("keyTest01:" + cache.get("keyTest01"));
System.out.println("修改记录值操作完成\r\n===================================");//添加或修改记录
System.out.println("添加或修改记录(set):\r\n===================================");
System.out.println("keyTest03:" + cache.set("keyTest03", "keyTest03Content"));
System.out.println("keyTest03:" + cache.get("keyTest03"));
System.out.println("keyTest03:" + cache.set("keyTest03", "keyTest03ContentReplace!"));
System.out.println("keyTest03:" + cache.get("keyTest03"));
System.out.println("添加或修改记录操作完成\r\n===================================");//删除记录
System.out.println("删除记录(delete):\r\n===================================");
System.out.println("keyTest01:" + cache.delete("keyTest01"));
System.out.println("keyTest02:" + cache.delete("keyTest02"));
System.out.println("keyTest03:" + cache.get("keyTest03"));
System.out.println("keyTest03:" + cache.delete("keyTest03"));
System.out.println("keyTest03:" + cache.get("keyTest03"));
System.out.println("修改记录值操作完成\r\n===================================");//打印当前的服务器参数及统计信息
System.out.println("服务器参数及统计信息(stats):\r\n===================================");
Map statsList=cache.stats();for(Object server : statsList.keySet().toArray()){
System.out.println("-------------------------\r\n服务器:" + server + " : \r\n-------------------------");
LinkedHashMap serverStats=(LinkedHashMap)statsList.get(server);for(Object statKey : serverStats.keySet().toArray()){
System.out.println(statKey+ " : " +serverStats.get(statKey));
}
}
}
}