cache策略(五)memcached
下载client的jar包
http://www.whalin.com/memcached/
得到jar包,同时导入maven私服
pom.xml如下:
<dependency>
<groupId>com.danga</groupId>
<artifactId>memcached</artifactId>
<version>2.0.1</version>
</dependency>
memcached主要参考了北京一个同事的实现方法,同时为了适应总体的cache接口,做了些调整
MemCache.java如下:
package com.sillycat.easyview.plugin.cache.memcached;
import java.util.Calendar;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.danga.MemCached.MemCachedClient;
import com.sillycat.easyview.plugin.cache.CacheManager;
import com.sillycat.easyview.plugin.cache.base.Cache;
import com.sillycat.easyview.plugin.cache.base.Timestamper;
import com.sillycat.easyview.plugin.commons.exceptions.CacheException;
public class MemCache implements Cache {
private static final Log log = LogFactory.getLog(MemCache.class);
private static final int SIXTY_THOUSAND_MS = 60000; // 1分钟的,或称1分钟包含的毫秒值
private MemCachedClient mc;
private String regionName; // 缓存区域标识
private int expireTimeInSecond;// 该存储区域对缓存对象过期时间值
private String keyPrefix; // 通过对象的key前缀区别不同缓存区域的对象
public MemCache(String poolName, String regionName,
int expireTimeInSecond, Boolean compressEnable,
Integer compressThreshold) {
mc = new MemCachedClient(poolName);
if (compressEnable != null) {
mc.setCompressEnable(compressEnable.booleanValue());
}
if (compressThreshold != null) {
mc.setCompressThreshold(compressThreshold.intValue());
}
this.expireTimeInSecond = expireTimeInSecond;
this.regionName = regionName;
this.keyPrefix = regionName + '-';
}
private String rebuildKey(Object key) {
return keyPrefix + key.toString().hashCode();
}
public Object get(Object key) throws CacheException {
if (log.isDebugEnabled()) {
log.debug("get Element by key: " + key);
}
if (key == null) {
return null;
} else {
Object rt = mc.get(rebuildKey(key));
if (rt == null) {
if (log.isDebugEnabled()) {
log.debug("Element for " + key + " is null");
}
return null;
} else {
if (log.isDebugEnabled()) {
log.debug("Got an element by " + key);
}
return rt;
}
}
}
public Object read(Object key) throws CacheException {
return get(rebuildKey(key));
}
public void update(Object key, Object value) throws CacheException {
if (expireTimeInSecond <= 0)
mc.replace(rebuildKey(key), value);
else {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, expireTimeInSecond);
mc.replace(rebuildKey(key), value, cal.getTime());
}
}
public void put(Object key, Object value) throws CacheException {
if (expireTimeInSecond <= 0)
mc.set(rebuildKey(key), value);
else {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, expireTimeInSecond);
mc.set(rebuildKey(key), value, cal.getTime());
}
}
public void remove(Object key) throws CacheException {
mc.delete(rebuildKey(key));
}
public void clear() throws CacheException {
mc.flushAll();
}
public void destroy() throws CacheException {
}
public void lock(Object key) throws CacheException {
}
public void unlock(Object key) throws CacheException {
}
public long nextTimestamp() {
return Timestamper.next();
}
public int getTimeout() {
// 60 second lock timeout
return Timestamper.ONE_MS * SIXTY_THOUSAND_MS;
}
public String getRegionName() {
return regionName;
}
public String toString() {
return "MemCached(" + getRegionName() + ')';
}
public long getElementCountInMemory() {
return 0;
}
public long getElementCountOnDisk() {
return 0;
}
public long getSizeInMemory() {
return 0;
}
public Map toMap() {
return null;
}
}
MemCacheProvider.java 如下:
package com.sillycat.easyview.plugin.cache.memcached;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import com.danga.MemCached.SockIOPool;
import com.sillycat.easyview.plugin.cache.base.Cache;
import com.sillycat.easyview.plugin.cache.base.CacheProvider;
import com.sillycat.easyview.plugin.commons.exceptions.CacheException;
import com.sillycat.easyview.plugin.commons.utils.PropertiesUtil;
import com.sillycat.easyview.plugin.commons.utils.StringUtil;
public class MemCacheProvider implements CacheProvider {
private static final Log log = LogFactory.getLog(MemCacheProvider.class);
public final static String DEFAULT_REGION_NAME = "default";
public static final int DEFAULT_EXPIRE_TIME_IN_SEC = 10 * 60;
private Hashtable<String, MemCache> caches;
public Properties getProperties() throws CacheException {
Properties props = new Properties();
String properties = props.getProperty("memcached.conf",
"memcached.properties");
props.setProperty("memcached.conf", properties);
Resource propertiesResource = new ClassPathResource(properties);
try {
if (!propertiesResource.exists()) {
throw new CacheException("not found '" + properties
+ "' in classpath!");
}
InputStream propertiesInputStream = propertiesResource
.getInputStream();
props.load(propertiesInputStream);
propertiesInputStream.close();
// 如果没有设置poolName,则以属性文件地址名自动设置之
if (props.getProperty("memcached.poolName") == null) {
props.setProperty("memcached.poolName", properties.substring(0,
properties.lastIndexOf('.')));
}
log.info("Loaded '" + properties + "' from "
+ propertiesResource.getFile().getAbsolutePath());
} catch (Exception e) {
throw new CacheException("fail to load/read '" + properties + "'",
e);
}
return props;
}
public void start() throws CacheException {
Properties props = this.getProperties();
caches = new Hashtable<String, MemCache>();
//
SockIOPool pool = SockIOPool.getInstance(props
.getProperty("memcached.poolName"));
if (pool.isInitialized()) {
log.info("MemcachedCacheProvider.SockIOPool has been Started!");
} else {
String servers = props.getProperty("memcached.servers");
if (StringUtil.isBlank(servers)) {
throw new CacheException(
"configuration 'servers' get a empty value");
}
pool.setServers(servers.split(","));
//
String weights = props.getProperty("weights");
if (weights != null) {
String[] ws = weights.split(",");
Integer[] iws = new Integer[ws.length];
for (int i = 0; i < iws.length; i++) {
iws[i] = Integer.parseInt(ws[i]);
}
pool.setWeights(iws);
}
//
pool.setFailover(PropertiesUtil.getBoolean(props,
"memcached.failover"));
pool.setFailback(PropertiesUtil.getBoolean(props,
"memcached.failback"));
pool
.setInitConn(PropertiesUtil.getInt(props,
"memcached.initConn"));
pool.setMinConn(PropertiesUtil.getInt(props, "memcached.minConn"));
pool.setMaxConn(PropertiesUtil.getInt(props, "memcached.maxConn"));
pool.setMaintSleep(PropertiesUtil.getInt(props,
"memcached.maintSleep"));
pool.setNagle(PropertiesUtil.getBoolean(props, "memcached.nagle"));
pool
.setSocketTO(PropertiesUtil.getInt(props,
"memcached.socketTO"));
pool.setAliveCheck(PropertiesUtil.getBoolean(props,
"memcached.aliveCheck"));
pool.initialize();
}
log.info("MemcachedCacheProvider Started!");
}
private static int getSeconds(String str) {
try {
switch (str.charAt(str.length() - 1)) {
case 's':
return Integer.parseInt(str.substring(0, str.length() - 1));
case 'm':
return Integer.parseInt(str.substring(0, str.length() - 1)) * 60;
case 'h':
return Integer.parseInt(str.substring(0, str.length() - 1)) * 3600;
case 'd':
return Integer.parseInt(str.substring(0, str.length() - 1)) * 86400;
default:
return Integer.parseInt(str);
}
} catch (NumberFormatException e) {
log.warn("Illegal configuration value : " + str, e);
}
return -1;
}
public Cache buildCache(String regionName) throws CacheException {
if (caches == null) {
throw new IllegalStateException("Please start the provider first!");
}
Properties props = this.getProperties();
String poolName = props.getProperty("memcached.poolName");
if (StringUtil.isBlank(regionName)) {
regionName = DEFAULT_REGION_NAME;
}
MemCache mCache = caches.get(regionName);
if (mCache == null) {
String expireTimeExpression = props.getProperty("memcached.region."
+ regionName + ".expireTime");
if (StringUtil.isBlank(expireTimeExpression)) {
expireTimeExpression = props
.getProperty("memcached.expireTime");
}
int expireTimeInSecond = -1;
if (StringUtil.isNotBlank(expireTimeExpression)) {
expireTimeExpression = expireTimeExpression.toLowerCase()
.trim();
expireTimeInSecond = getSeconds(expireTimeExpression);
} else {
expireTimeInSecond = DEFAULT_EXPIRE_TIME_IN_SEC;
}
//
String pcompressEnable = props.getProperty("memcached.region."
+ regionName + ".compressEnable");
if (pcompressEnable == null) {
pcompressEnable = props.getProperty("memcached.compressEnable");
}
Boolean compressEnable = null;
if (pcompressEnable != null) {
compressEnable = Boolean.parseBoolean(pcompressEnable);
}
//
String pcompressThreshold = props.getProperty("memcached.region."
+ regionName + ".compressThreshold");
if (pcompressThreshold == null) {
pcompressThreshold = props
.getProperty("memcached.compressThreshold");
}
Integer compressThreshold = null;
if (compressThreshold != null) {
compressThreshold = Integer.parseInt(pcompressThreshold);
}
log.info("Building cache named " + regionName
+ " using expireTimeInSecond is " + expireTimeInSecond);
mCache = new MemCache(poolName, regionName, expireTimeInSecond,
compressEnable, compressThreshold);
caches.put(regionName, mCache);
}
return mCache;
}
public long nextTimestamp() {
return 0;
}
public void stop() {
}
}
memcached.properties配置文件,配置文件主要参考了北京同事的写法,学到了不少东东:
############################################################
## list of cache server, e.g: "memcached-1.nms:11211,memcached-2.nms:11211"
## memcached1.nms & memcached2.nms are host names here, they should be configuired in /etc/hosts
memcached.servers=192.168.10.253:11211
## Sets the list of weights to apply to the server list.
## This is an int array with each element corresponding to an element
## in the same position in the server String array.
#memcached.weights=
############################################################
## Sets the failover flag for the pool.
## If this flag is set to true, and a socket fails to connect,
## the pool will attempt to return a socket from another server if one exists.
## If set to false, then getting a socket will return null if it fails to connect to the requested server.
memcached.failover=true
## Sets the failback flag for the pool.
## If this is true and we have marked a host as dead,
## will try to bring it back. If it is false, we will never
## try to resurrect a dead host.
memcached.failback=true
############################################################
## Sets the initial number of connections per server in the available pool.
memcached.initConn=1
## Sets the minimum number of spare connections to maintain in our available pool.
memcached.minConn=1
## Sets the maximum number of spare connections allowed in our available pool.
memcached.maxConn=10
## maintenance thread sleep time
memcached.maintSleep=30000
## default timeout of socket reads
memcached.socketTO=30000
## default to not check each connection for being alive
memcached.aliveCheck=false
## enable/disable Nagle's algorithm
memcached.nagle=true
## Enable storing compressed data, provided it meets the threshold requirements.
## If enabled, data will be stored in compressed form if it is
## longer than the threshold length set with setCompressThreshold(int)
## The default is that compression is enabled.
## Even if compression is disabled, compressed data will be automatically decompressed.
memcached.compressEnable=true
#memcached.region.[regionName].compressEnable to spec for the named cache
## Sets the required length for data to be considered for compression.
## If the length of the data to be stored is not equal or larger than this value, it will
## not be compressed.
## This defaults to 15 KB(30720).
memcached.compressThreshold=30720
#memcached.region.[regionName].compressThreshold to spec for the named cache
##
memcached.expireTime=3m
#memcached.region.[regionName].memcached.expireTime to spec for the named cache
###################################################
## region cacha conf followed: