记录下项目中用到的缓存的功能,个人感觉这种实现方式还是挺不错的,该实现方式是通过结合spring的IOC容器进行bean管理。
1. 缓存类型配置(这里实现了两种memcached和redis),因为是结合spring使用,所以这个类型配置就可以像数据库连接源那样在properties文件中进行配置,在config.properties配置文件中的memcached配置如下:
#缓存类型
cache.provinder=memcached
#缓存服务器地址和端口
cache.addresses=172.16.1.114:11211
#连接池大小
cache.poolsize=1
2. 缓存接口方法定义,接口中的方法个数可根据实际情况进行添加或删减:
public interface CacheManager {
/**
* 保存键值并指定存储时长,单位秒。
* @param key
* @param value
* @param duration
* @return
*/
void set(String key, String value, int duration);
/**
* 保存键值并指定存储时长和时长单位
* @param key
* @param value
* @param duration
* @param unit
* @return
*/
void set(String key, String value, int duration, TimeUnit unit);
/**
* 永久保存键值
* @param key
* @param value
* @return
*/
void set(String key, String value);
/**
* 根据键获取值
* @param key
* @return null表示没有找到
*/
String get(String key);
/**
* 根据键删除对应键值对
* @param key
* @return
*/
void delete(String key);
/**
* 获取一个计数器,增加相应的值并返回
* @param counter 计数器键名
* @param incrBy 增加的值
* @param defaultValue 计数器默认初始值
* @return
*/
long incrementAndGet(String counter, long incrBy, long defaultValue);
/**
* 获取一个计数器,减少相应的值并返回
* @param counter 计数器键名
* @param decrBy 减少的值
* @param defaultValue 计数器默认初始值
* @return
*/
long decrementAndGet(String counter, long decrBy, long defaultValue);
/**
* 关掉链接,释放资源
*/
void shutdown();
}
2. 缓存工厂类实现
public class CacheManagerFactoryBean implements FactoryBean<CacheManager>{
private Logger log = LoggerFactory.getLogger(CacheManagerFactoryBean.class);
private String provinder;
private String addresses;
private int poolsize;
private CacheManager cm;
public CacheManager getObject() throws Exception {
if("memcached".equals(provinder)){
addresses = addresses.replaceAll("(,\\s*|\\s+)", " ");
cm = new MemcachedCacheManager(addresses, poolsize);
return cm;
}else if("redis".equals(provinder)){
cm = new RedisCacheManager(addresses, poolsize);
return cm;
}else{
throw new Exception("缓存服务器提供商不正确,请设置config.properties中的cache.provinder,值为memcached或redis");
}
}
public void shutdown(){
log.info("shutdown cache manager.");
cm.shutdown();
}
public Class<?> getObjectType() {
return CacheManager.class;
}
public boolean isSingleton() {
return true;
}
public String getAddresses() {
return addresses;
}
public void setAddresses(String addresses) {
this.addresses = addresses;
}
public int getPoolsize() {
return poolsize;
}
public void setPoolsize(int poolsize) {
this.poolsize = poolsize;
}
public String getProvinder() {
return provinder;
}
public void setProvinder(String provinder) {
this.provinder = provinder;
}
}
3. memcached的实现
import java.io.IOException;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.utils.AddrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MemcachedCacheManager implements CacheManager {
private Logger log = LoggerFactory.getLogger(MemcachedCacheManager.class);
private MemcachedClient client;
public MemcachedCacheManager(String addresses, int poolsize){
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(addresses));
builder.setConnectionPoolSize(poolsize);
try {
client = builder.build();
} catch (IOException e) {
log.error("failed to build memcached client params:");
log.error("[addresses:{}, poolsize:{}]", new Object[]{addresses, poolsize});
log.error("failed to build memcached client details:", e);
throw new RuntimeException(e);
}
}
public void delete(String key){
try {
client.delete(key);
} catch(Exception e){
log.error("memcached delete failed params:");
log.error("[key:{}]", new Object[]{key});
log.error("memcahced delete failed details:", e);
throw new RuntimeException(e);
}
}
public String get(String key){
try {
return client.get(key);
} catch(Exception e){
log.error("memcached get failed params:");
log.error("[key:{}]", new Object[]{key});
log.error("memcahced get failed details:", e);
throw new RuntimeException(e);
}
}
public void set(String key, String value){
set(key, value, 0);
}
public void set(String key, String value, int duration) {
try {
client.set(key, duration, value);
} catch(Exception e){
log.error("memcached set failed params:");
log.error("[key:{}, value:{}, duration:{}]", new Object[]{key, value, duration});
log.error("memcached set failed details:", e);
throw new RuntimeException(e);
}
}
public void set(String key, String value, int duration, TimeUnit unit) {
set(key, value, duration*unit.getValue());
}
public long incrementAndGet(String counter, long incrBy,
long defaultValue) {
try {
return client.incr(counter, incrBy, defaultValue);
} catch(Exception e){
log.error("memcached incr failed params:");
log.error("[counter:{}, incrBy:{}, defaultValue:{}]", new Object[]{counter, incrBy, defaultValue});
log.error("memcached incr failed details:", e);
throw new RuntimeException(e);
}
}
public long decrementAndGet(String counter, long decrBy,
long defaultValue) {
try {
return client.decr(counter, decrBy, defaultValue);
} catch(Exception e){
log.error("memcached decr failed params:");
log.error("[counter:{}, decrBy:{}, defaultValue:{}]", new Object[]{counter, decrBy, defaultValue});
log.error("memcached decr failed details:", e);
throw new RuntimeException(e);
}
}
public void shutdown() {
try {
client.shutdown();
} catch (IOException e) {
log.error("failed to shutdown memcached client.", e);
}
}
}
4. redis的实现
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;
public class RedisCacheManager implements CacheManager {
private Logger log = LoggerFactory.getLogger(RedisCacheManager.class);
private JedisPool pool;
public RedisCacheManager(String addresses, int poolsize){
try{
String host = addresses.split(":")[0];
String port = addresses.split(":")[1];
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxActive(poolsize);
pool = new JedisPool(config, host, Integer.valueOf(port));
}catch(Exception e){
log.error("failed to build redis client params:");
log.error("[addresses:{}, poolsize:{}]", new Object[]{addresses, poolsize});
log.error("failed to build redis client details:", e);
throw new RuntimeException(e);
}
}
public String get(String key) {
Jedis jedis = null;
boolean success = true;
try{
jedis = pool.getResource();
String value = jedis.get(key);
return value;
}catch(Exception e){
// jedis 操作失败把无效的jedis 实例返回给池做销毁处理
success = false;
pool.returnBrokenResource(jedis);
log.error("redis get failed params:");
log.error("[key:{}]", new Object[]{key});
log.error("redis get failed details:", e);
throw new RuntimeException(e);
}finally{
if(success){
// jedis 操作成功,把jedis返回给池,得以重用
pool.returnResource(jedis);
}
}
}
public void set(String key, String value){
Jedis jedis = null;
boolean success = true;
try{
jedis = pool.getResource();
jedis.set(key, value);
}catch(Exception e){
success = false;
pool.returnBrokenResource(jedis);
log.error("redis set failed params:");
log.error("[key:{}, value:{}]", new Object[]{key, value});
log.error("redis set failed details:", e);
throw new RuntimeException(e);
}finally{
if(success){
pool.returnResource(jedis);
}
}
}
public void set(String key, String value, int duration){
Jedis jedis = null;
boolean success = true;
try{
jedis = pool.getResource();
Transaction tx = jedis.multi();
tx.set(key, value);
tx.expire(key, duration);
tx.exec();
}catch(Exception e){
success = false;
pool.returnBrokenResource(jedis);
log.error("redis set failed params:");
log.error("[key:{}, value:{}, duration:{}]", new Object[]{key, value, duration});
log.error("redis set failed details:", e);
throw new RuntimeException(e);
}finally{
if(success){
pool.returnResource(jedis);
}
}
}
public void set(String key, String value, int duration, TimeUnit unit) {
set(key, value, duration*unit.getValue());
}
public void delete(String key){
Jedis jedis = null;
boolean success = true;
try{
jedis = pool.getResource();
jedis.del(key);
}catch(Exception e){
success = false;
pool.returnBrokenResource(jedis);
log.error("redis delete failed params:");
log.error("[key:{}]", new Object[]{key});
log.error("redis delete failed details:", e);
throw new RuntimeException(e);
}finally{
if(success){
pool.returnResource(jedis);
}
}
}
public long incrementAndGet(String counter, long incrBy, long defaultValue) {
Jedis jedis = null;
boolean success = true;
try{
jedis = pool.getResource();
long count = jedis.incrBy(counter, incrBy);
return count;
}catch(Exception e){
success = false;
pool.returnBrokenResource(jedis);
log.error("redis incr failed params:");
log.error("[counter:{}, incrBy:{}, defaultValue:{}]", new Object[]{counter, incrBy, defaultValue});
log.error("redis incr failed details:", e);
throw new RuntimeException(e);
}finally{
if(success){
pool.returnResource(jedis);
}
}
}
public long decrementAndGet(String counter, long decrBy, long defaultValue) {
Jedis jedis = null;
boolean success = true;
try{
jedis = pool.getResource();
long count = jedis.decrBy(counter, decrBy);
return count;
}catch(Exception e){
success = false;
pool.returnBrokenResource(jedis);
log.error("redis decr failed params:");
log.error("[counter:{}, incrBy:{}, defaultValue:{}]", new Object[]{counter, decrBy, defaultValue});
log.error("redis decr failed details:", e);
throw new RuntimeException(e);
}finally{
if(success){
pool.returnResource(jedis);
}
}
}
public void shutdown() {
try{
pool.destroy();
}catch(Exception e){
log.error("failed to shutdown redis client.", e);
}
}
}
还可以添加其他的缓存实现,然后在缓存工厂类里面同步加上创建方式就可以了
5. spring中配置缓存工厂实体bean的生成
<bean id="cacheManager" class="com.lutongnet.game.apiserver.cache.CacheManagerFactoryBean" destroy-method="shutdown">
<property name="provinder" value="${cache.provinder}"></property>
<property name="addresses" value="${cache.addresses}"></property>
<property name="poolsize" value="${cache.poolsize}"></property>
</bean>
这样在其他地方就可以通过cacheManager这个实体bean去使用缓存的功能了