在实现积分项目业务中,对不同场景设置了不同的key-value缓存到了redis中。但是因为对不同业务的key需要缓存的时间不尽相同,这里自定义工具类来实现。
设置redis缓存key,截取部分代码:
try{
//cacheManager就相当从redis链接池获取一个连接,具体工厂类获取在后面备注
cacheManager = (RedisCacheManager) CacheManagerFactory.getCacheManager();
totalMonCount = Float.parseFloat(cacheManager.getString(monthKey)) + centCount;
if (centLimitByMonth != -1){
if (totalMonCount > centLimitByMonth) {
// 超出月上限不再累计
logger.error("exceeds the month limit cents! [" + totalMonCount + "] code:[" + code + "]");
return null;
}
}
//当未超出月额度,此时要对日额度进行判断;只需判断其是否超出日上限积分
if (dayKey != null){
//累积积分;因为签到其实是没有每日积分的,是按次数规则累积的;但为了统一,直接用centCount代替(都是签一次1分)
totalDayCount = Float.parseFloat(cacheManager.getString(dayKey)) + centCount;
if (centLimitByDay != -1){
if (totalDayCount > centLimitByDay){
logger.info("[ERROR]teacher everyday assign cents > month limit! total: ["+totalDayCount+"]");
return null;
}
}
cacheManager.set(dayKey,totalDayCount.toString(),DateUtil.getSecsToEndOfCurrentDay());
}
//对月限制key进行积分累加
//每月1号凌晨1点启动脚本删除,同时设置了保存到月底缓存时间双重保障
cacheManager.set(monthKey, totalMonCount.toString(), DateUtil.getSecsToEndOfCurrentDay());
logger.info("==monthkey:"+monthKey+"---value:"+totalMonCount);
}
...
}catch(Exception e){
logger.error("===cache redis fail!");
e.printStackTrace();
}finally {
if (cacheManager != null){
cacheManager.close();
}
}
//工厂类获取redis链接
public class CacheManagerFactory {
private static ICacheManager cacheManager;
private CacheManagerFactory(){
};
public static ICacheManager getCacheManager(){
if(cacheManager == null){
synchronized (CacheManagerFactory.class) {
if(cacheManager == null){
JedisPooler jedisPooler = RedisPoolerFactory.getJedisPooler();
cacheManager = new RedisCacheManager(jedisPooler);
}
}
}
return cacheManager;
}
}
//redis链接池工厂类获取链接
public class RedisPoolerFactory {
private static JedisPooler jedisPooler;
private RedisPoolerFactory(){
};
public static JedisPooler getJedisPooler(){
if(jedisPooler == null){
synchronized (RedisPoolerFactory.class) {
if(jedisPooler == null){
jedisPooler = new JedisPooler();
}
}
}
return jedisPooler;
}
}
/**
*
* Redis 连接池实例
*
* @author Ethan.Lam
* @createTime 2011-12-3
*
*/
public class JedisPooler {
private JedisPool pool;
private String REDIS_HOST;
private String REDIS_PSW;
private int REDIS_PORT;
private int REDIS_MaxActive;
private int REDIS_MaxIdle;
private int REDIS_MaxWait;
public JedisPooler(String config) {
__init(config);
}
public JedisPooler() {
__init("/jedisPool.properties");
}
private void __init(String conf) {
// 完成初始化工作
Properties prop = new Properties();
try {
InputStream _file = loadConfig(conf);
prop.load(_file);
REDIS_HOST = prop.getProperty("REDIS.HOST");
REDIS_PSW = prop.getProperty("REDIS.PSW");
REDIS_PORT = Integer.parseInt(prop.getProperty("REDIS.PORT").trim());
REDIS_MaxActive = Integer.parseInt(prop.getProperty("REDIS.MaxActive").trim());
REDIS_MaxIdle = Integer.parseInt(prop.getProperty("REDIS.MaxIdle").trim());
REDIS_MaxWait = Integer.parseInt(prop.getProperty("REDIS.MaxWait").trim());
} catch (Exception e) {
e.printStackTrace();
REDIS_HOST = "localhost";
throw new NullPointerException(conf + " is not found !");
}
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxActive(REDIS_MaxActive);
config.setMaxIdle(REDIS_MaxIdle);
config.setMaxWait(REDIS_MaxWait);
config.setTestOnBorrow(true);
System.out.println("REDIS Cache服务信息: 当前连接的服务IP为:" + REDIS_HOST + ":" + REDIS_PORT);
if (null == REDIS_PSW || "".equals(REDIS_PSW.trim())){
pool = new JedisPool(config, REDIS_HOST, REDIS_PORT, 5000);
}
else{
pool = new JedisPool(config, REDIS_HOST, REDIS_PORT, 5000, REDIS_PSW);
}
}
public Jedis getJedis() {
return pool.getResource();
}
public void returnResource(Jedis jedis) {
pool.returnResource(jedis);
}
public JedisPool getPool() {
return pool;
}
InputStream loadConfig(String configPath) throws Exception {
InputStream _file = null;
try {
String file = JedisPooler.class.getResource(configPath).getFile();
file = URLDecoder.decode(file);
_file = new FileInputStream(file);
} catch (Exception e) {
System.out.println("读取jar中的配置文件....");
String currentJarPath = URLDecoder.decode(JedisPooler.class.getProtectionDomain()
.getCodeSource().getLocation().getFile(), "UTF-8"); // 获取当前Jar文件名
System.out.println("currentJarPath:" + currentJarPath);
java.util.jar.JarFile currentJar = new java.util.jar.JarFile(currentJarPath);
java.util.jar.JarEntry dbEntry = currentJar.getJarEntry("jedisPool.properties");
InputStream in = currentJar.getInputStream(dbEntry);
_file = in;
}
return _file;
}
}
可以看到,这里cacheManager.set(monthKey, totalMonCount.toString(), DateUtil.getSecsToEndOfCurrentDay());
就用到了工具类获取了指定的时间范围。
对于redis这种希望指定缓存有效时间,现在提供3种方案:
1、自定义确切时间:
public static final long LoginCentTimeByDay = 86400;//s 未认证失效时间 1天
public static final long LoginCentTimeByMonth = 86400*30; //s 时效时间 30天
直接指定:
cacheManager.set(monthKey, totalMonCount.toString(),LoginCentTimeByDay)
2、自定义工具类,获取当前时间到第二天的零点、下个月1号零点的时间差(s):
cacheManager.set(monthKey, totalMonCount.toString(), DateUtil.getSecsToEndOfCurrentDay());
public class DateUtil {
/**
*获取每月最后一天时间
* @param sDate1
* @return
*/
public static Date getLastDayOfMonth(Date sDate1) {
Calendar cDay1 = Calendar.getInstance();
cDay1.setTime(sDate1);
final int lastDay = cDay1.getActualMaximum(Calendar.DAY_OF_MONTH);
Date lastDate = cDay1.getTime();
lastDate.setDate(lastDay);
return lastDate;
}
/*
获取下一个月第一天凌晨1点
*/
public static Date nextMonthFirstDate() {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 1); //设置为每月凌晨1点
calendar.set(Calendar.DAY_OF_MONTH, 1); //设置为每月1号
calendar.add(Calendar.MONTH, 1); // 月份加一,得到下个月的一号
// calendar.add(Calendar.DATE, -1); 下一个月减一为本月最后一天
return calendar.getTime();
}
/**
* 获取第二天凌晨0点毫秒数
* @return
*/
public static Date nextDayFirstDate() throws ParseException {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.DAY_OF_YEAR, 1);
cal.set(Calendar.HOUR_OF_DAY, 00);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
return cal.getTime();
}
//*********
/**
* 获取当前时间到下个月凌晨1点相差秒数
* @return
*/
public static Long getSecsToEndOfCurrentMonth(){
Long secsOfNextMonth = nextMonthFirstDate().getTime();
//将当前时间转为毫秒数
Long secsOfCurrentTime = new Date().getTime();
//将时间转为秒数
Long distance = (secsOfNextMonth - secsOfCurrentTime)/1000;
if (distance > 0 && distance != null){
return distance;
}
return new Long(0);
}
/**
* 获取当前时间到明天凌晨0点相差秒数
* @return
*/
public static Long getSecsToEndOfCurrentDay() throws ParseException {
Long secsOfNextDay = nextDayFirstDate().getTime();
//将当前时间转为毫秒数
Long secsOfCurrentTime = new Date().getTime();
//将时间转为秒数
Long distance = (secsOfNextDay - secsOfCurrentTime)/1000;
if (distance > 0 && distance != null){
return distance;
}
return new Long(0);
}
}
3、使用定时任务定时清空redis缓存;避免出现定时任务异常,我的业务代码里都保障了两种方案都适用。
定时任务保证,到指定时间直接调用代码进行操作;代码里直接调用shell脚本直接删掉相关redis的缓存数据。
quartz定时任务就需要注意定义相应的cron时间:
我的定时任务的配置文件quartz.xml中定义:
<bean id="deleteRedisCacheDayUsersJob" class="cn.qtone.xxt.cent.quartz.delRedisCacheCentUsers" />
<bean id="deleteRedisCacheDayUsersTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="deleteRedisCacheDayUsersJob" />
<property name="targetMethod" value="delCurrentDayCacheUsersByDay" />
<property name="concurrent" value="false" />
bean>
<bean id="deleteRedisCacheDayUsersTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="deleteRedisCacheDayUsersTask" />
<property name="cronExpression" value="59 59 23 * * ?" />
bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="deleteRedisCacheDayUsersTrigger" />
<ref bean="deleteRedisCacheMonthUsersTrigger" />
list>
property>
bean>