SpringBoot秒杀系统实战07-通用缓存Key的设计与封装

为什么要这个通用缓存Key?
当项目中的模块越来越多的时候,需要存的缓存也越来越多,比如商品Id,订单Id,用户id等,此时若是id出现重复,将给系统带来错误。

那么使用KeyPrefix来更好的操作和管理缓存中对应的key。给不同模块的key带有一个前缀。

不加前缀的情况:

	public <T> boolean set(String key,T value){
		Jedis jedis=null;
		//在JedisPool里面取得Jedis
		try {
			jedis=jedisPool.getResource();
			//将T类型转换为String类型
			String s=beanToString(value);
			if(s==null) {
				return false;
			}
			jedis.set(key, s);
			return true;
		}finally {
			returnToPool(jedis);
		}
	}

解决覆盖key的办法:
将前缀+一个key一起作为一个redis里面真正的key,这样不同模块之间就不会重复。
好处:不同功能或者不同模块的前缀不同,即使有同名key出现,那么前缀不同,并不会引起key冲突被其他功能覆盖的情况。

使用一个模板模式来封装:

接口:

/**
 *做缓存的前缀接口 
 */
	public interface KeyPrefix {
	//有效期
	public int expireSeconds();
	//前缀
	public String getPrefix();
}

BasePrefix 抽象类:简单的实现一下KeyPrefix,定义成抽象类原因,防止不小心被创建,我们不希望BasePrefix被实例化,因为抽象类不允许实例化。我们只希望它被继承。不同模块的前缀类都继承他。

	//定义成抽象类
	public abstract class BasePrefix implements KeyPrefix{
	private int expireSeconds;
	private String prefix;
	public BasePrefix() {
	}
	public BasePrefix(String prefix) {
		//this(0, prefix);//默认使用0,不会过期
		this.expireSeconds=0;
		this.prefix=prefix;		
	}
	public BasePrefix(int expireSeconds,String prefix) {//覆盖了默认的构造函数
		this.expireSeconds=expireSeconds;
		this.prefix=prefix;
	}
	//默认为0代表永不过期
	public int expireSeconds() {
		return expireSeconds;
	}
	//前缀为类名:+prefix
	public String getPrefix() {
		String className=getClass().getSimpleName();
		return className+":"+prefix;
	}
	
}

注意:该类2种不同构造方法:用于继承。一个只带前缀名,一个带前缀名和过期时间。当实现public BasePrefix(String prefix)的时候,我们将默认这个key不会失效,因为有一些场景,我们不希望key失效,但是有些场景我们需要设置key的合适的有效期。

具体实现类:

用户UserKey只去继承了super(prefix),即public BasePrefix(String prefix),那么代表user的key的过期时间为不会过期。

public class UserKey extends BasePrefix{
	public UserKey(String prefix) {
		super(prefix);
	}
	public static UserKey getById=new UserKey("id");
	public static UserKey getByName=new UserKey("name");
}

秒杀用户的MiaoshaUserKey ,继承了super(expireSeconds,prefix),可以设置有效期时间为2天。

public class MiaoshaUserKey extends BasePrefix{
	public static final int TOKEN_EXPIRE=3600*24*2;//3600S*24*2    =2天
	public MiaoshaUserKey(int expireSeconds,String prefix) {
		super(expireSeconds,prefix);
	}
	public static MiaoshaUserKey token=new MiaoshaUserKey(TOKEN_EXPIRE,"tk");
	//对象缓存一般没有有效期,永久有效
	public static MiaoshaUserKey getById=new MiaoshaUserKey(0,"id");
}

具体实现类的具体使用场景:

	/**
	 *避免key被不同类的数据覆盖 
	 *使用Prefix前缀-->不同类别的缓存,用户、部门、
	 */
	@RequestMapping("/redis/set")
	@ResponseBody
	public Result<Boolean> redisSet() {//0代表成功		
		User user=new User(1,"1111");
		boolean f=redisService.set(UserKey.getById,""+1,user);
		return Result.success(true);
	}
	@RequestMapping("/redis/getbyid")
	@ResponseBody
	public Result<User> redisGetById() {//0代表成功		
		User res=redisService.get(UserKey.getById,""+1,User.class);
		//redisService.get("key1",String.class);
		//System.out.println("res:"+userService.tx());
		return Result.success(res);
	}

完善后的get和set缓存的Key值的方法:

public <T> T get(KeyPrefix prefix,String key,Class<T> data){
	Jedis jedis=null;
	//在JedisPool里面取得Jedis
	try {
		jedis=jedisPool.getResource();
		//生成真正的key  className+":"+prefix;  BasePrefix:id1
		String realKey=prefix.getPrefix()+key;
		//System.out.println("jedis:"+jedis);
		String sval=jedis.get(realKey);
		//将String转换为Bean入后传出
		T t=stringToBean(sval,data);
		return t;
	}finally {
		returnToPool(jedis);
	}
}
/**
* 设置单个、多个对象
*/						
 //MiaoshaUserKey.token, token, user
public <T> boolean set(KeyPrefix prefix,String key,T value){		
	Jedis jedis=null;
	try {
                //在JedisPool里面取得Jedis
	        jedis=jedisPool.getResource();
		String realKey=prefix.getPrefix()+key;		
		String s=beanToString(value);		
		if(s==null||s.length()<=0) {
			return false;
		}
		int seconds=prefix.expireSeconds();
		if(seconds<=0) {//有效期:代表不过期,这样才去设置
			jedis.set(realKey, s);
		}else {//没有设置过期时间,没有设置有效期,自己设置
			jedis.setex(realKey, seconds,s);				
	}
		return true;
	}finally {
		returnToPool(jedis);			
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章