产品需求:发布信息时候,用户可以选择重新发布时间,重新发布间隔时间以及次数,如重发6次时间间隔为2分钟。
实现设计:
可以通过定时查询数据库的发布时间,以及信息中重发次数时间,update数据(或者直接写一个厉害的update语句也可以),但是定时的查询整个数据库表,而且中间还会涉及到运算,无法命中索引,会使数据库压力较大,咨询好多朋友,大多数建议降低数据查询次数,如半个小时update一次,但是这样时间误差会很大,产品无法接受。
在通过仔细调研以之后,计算使用spring定时任务、redis中list结构、update数据实现。当用户发布信息的时候,依据次数生成多个序列的key,并将信息id存储到redis中(如果多个信息,在同一个序列中,直接在list尾部添加数据),spring通过定时任务,每隔10秒依据生成的序列,读取redis中list数据,将list中数据,转化为id1,id2,id3,通过数据库in关键字,实现更新数据。
关键代码:
spring定时任务配置
public classRepublishCargoJob {private Log log = LogFactory.getLog(RepublishCargoJob.class);/*** 货源重发定时任务*/
public voiddoRepublish(){//获取WebApplicationContext
WebApplicationContext wac =ContextLoader.getCurrentWebApplicationContext();//获取redis读取序列工具
TimedPublishTask timedPublishTask=(TimedPublishTask) wac.getBean("timedPublishTask");//获取本次需要更新的id list
Listlist=timedPublishTask.getBatch();if(list!=null&&list.size()>0){//update数据库
ResourceCargoInfoDAO resourceCargoInfoDAO=(ResourceCargoInfoDAO) wac.getBean("resourceCargoInfoDAO");
ResourceCargoInfo rci=newResourceCargoInfo();
rci.setRciPublishDatetime(DateUtils.getDateTime());
ResourceCargoInfoExample resourceCargoInfoExample=newResourceCargoInfoExample();
resourceCargoInfoExample.createCriteria().andRciIdIn(list).andRciAvalibleStatusEqualTo(1);try{
resourceCargoInfoDAO.updateByExampleSelective(rci, resourceCargoInfoExample);
}catch(SQLException e) {//TODO Auto-generated catch block
e.printStackTrace();
log.error("=======重发货源失败======");
}
}
}
packagecom.ada.wuliu.common.redis.timertask;importjava.util.ArrayList;importjava.util.List;importjavax.annotation.Resource;importorg.apache.commons.logging.Log;importorg.apache.commons.logging.LogFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.connection.jedis.JedisConnection;importorg.springframework.data.redis.connection.jedis.JedisConnectionFactory;importorg.springframework.stereotype.Component;importorg.springframework.web.bind.annotation.InitBinder;importcom.ada.wuliu.common.dictionaries.utils.DateUtils;importcom.ada.wuliu.common.dictionaries.utils.StateCode;importcom.ada.wuliu.common.dto.result.SimpleResult;importcom.ada.wuliu.common.redis.dao.RedisList;importcom.ada.wuliu.common.redis.dao.RedisString;importcom.ada.wuliu.common.redis.dto.TimerPublish;importcom.ada.wuliu.common.redis.util.SessionImpl_Temp_01;importcom.ada.wuliu.common.redis.util.StringUtil;
@Component("timedPublishTask")public classTimedPublishTask {//定时任务执行周期
public Integer taskFrequency=10000;//默认任务开始\(^o^)/
private String defaultIncr="1000";private static String TIMER_PUBLISH_KEY_TODO=null;
@AutowiredprivateRedisList redisList;
@AutowiredprivateRedisString redisString;private final static Log log = LogFactory.getLog(TimedPublishTask.class);
@Resource(name="connectionFactory")privateJedisConnectionFactory connectionFactory;publicTimedPublishTask(){
initKeyHeader();
}private voidinitKeyHeader(){if(TIMER_PUBLISH_KEY_TODO==null){
TIMER_PUBLISH_KEY_TODO="TIMER_PUBLISH_KEY_TODO_CYC_"+StringUtil.getLocalOsName();
}
}/***
*@paramtimerPublish 需要重新发布的对象*/
publicSimpleResult addBatch(TimerPublish timerPublish){
JedisConnection connection=connectionFactory.getConnection();
SimpleResult cr= null;try{//获取最新的批次号
String batchNo=redisString.get(TIMER_PUBLISH_KEY_TODO, connection);if(batchNo==null){
batchNo=redisString.incr(TIMER_PUBLISH_KEY_TODO, connection).toString();
}//刷新频率
Integer frequency=timerPublish.getFrequency()/10;//刷新次数
Integer number=timerPublish.getNumber();//对象key
Long key=timerPublish.getKey();
Listlist=new ArrayList();for(int i=1;i
long addBatchNo=i*frequency/taskFrequency+Long.parseLong(batchNo);
addBatchNo=addBatchNo-1;
String batchKey=TIMER_PUBLISH_KEY_TODO+addBatchNo;//list 第一个为更新时间 如果更新时间
log.info("新添加批次号:======"+batchKey+"====="+key);
List ll=redisList.get(batchKey, 1, 2, connection);if(ll==null||ll.size()==0){
redisList.lPush(batchKey, (System.currentTimeMillis()+i*frequency)+"", connection);
}
redisList.rPush(batchKey, key+"", connection);
}return new SimpleResult("成功", StateCode.SUCCESS, StateCode.SUCCESS, true);
}catch(Exception e) {
log.error("-----TimedPublishTask::addBatch Throwable Error! -----", e);return new SimpleResult("服务器错误", StateCode.SERVER_ERROR, null, false);
}catch(Throwable th) {
log.error("-----TimedPublishTask::addBatch Throwable Error! -----", th);return new SimpleResult("服务器错误", StateCode.SERVER_ERROR, null, false);
}finally{
connection.close();
}
}/***
*@parambatchNo 获取本批次需要更新的对象的key
*@return
*/
public ListgetBatch(){
JedisConnection connection=connectionFactory.getConnection();
SimpleResult cr= null;try{//获取当前需要更新的批次号
String batchNo=redisString.get(TIMER_PUBLISH_KEY_TODO, connection);//如果没有批次则初始化批次
if(batchNo==null){
batchNo=redisString.incr(TIMER_PUBLISH_KEY_TODO, connection).toString();
}
Listlist=redisList.getAll(TIMER_PUBLISH_KEY_TODO+batchNo, connection);
log.info("当前任务"+TIMER_PUBLISH_KEY_TODO+batchNo);//出现掉任务处理
if(list!=null&&list.size()>0){
Long addTime=Long.parseLong(list.get(0));
Long nowTime=System.currentTimeMillis();if(nowTime-addTime>2*taskFrequency){
log.info("发现队列冗余数据,开始尝试执行=========");//获取偏移量
long offset=(nowTime-addTime)/taskFrequency+1;
log.info("偏移量======"+offset);
Integer batchNoRe=Integer.parseInt(batchNo);int i=1;if(offset<50){ i=1;}else{i=(int) (offset-50L);}for(;i
batchNoRe++;
log.info("添加任务"+TIMER_PUBLISH_KEY_TODO+batchNoRe);
Listlistre=redisList.getAll(TIMER_PUBLISH_KEY_TODO+batchNoRe, connection);if(listre.size()>0) listre.remove(0);
redisString.del(TIMER_PUBLISH_KEY_TODO+batchNoRe, connection);
redisString.incr(TIMER_PUBLISH_KEY_TODO, connection);//redisString.incr(TIMER_PUBLISH_KEY_TODO, connection);
list.addAll(listre);
}
}
}if(list.size()>0) list.remove(0);
log.info("执行批次==========="+TIMER_PUBLISH_KEY_TODO+batchNo+"======VALUES====="+getListStr(list));
redisString.incr(TIMER_PUBLISH_KEY_TODO, connection);
redisString.del(TIMER_PUBLISH_KEY_TODO+batchNo, connection);returnlist;
}catch(Exception e) {
e.printStackTrace();
log.error("-----TimedPublishTask::getBatch Throwable Error! -----", e);return null;
}catch(Throwable th) {
log.error("-----TimedPublishTask::getBatch Throwable Error! -----", th);return null;
}finally{
connection.close();
}
}public String getListStr(Listlist){if(list==null)return null;
StringBuffer sb=newStringBuffer();for(String s:list){
sb.append(s+",");
}returnsb.toString();
}
}
初始化批次15秒执行一次,需要执行10次
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL1=====114102091
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL3=====114102091
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL4=====114102091
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL6=====114102091
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL7=====114102091
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL9=====114102091
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL10=====114102091
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL12=====114102091
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL13=====114102091
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL15=====114102091
初始化批次15秒执行一次,需要执行10次
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL1=====114102095
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL3=====114102095
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL4=====114102095
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL6=====114102095
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL7=====114102095
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL9=====114102095
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL10=====114102095
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL12=====114102095
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL13=====114102095
新添加批次号:======TIMER_PUBLISH_KEY_TODO_KTSDKLHL15=====114102095
当前任务TIMER_PUBLISH_KEY_TODO_KTSDKLHLSK1
发现队列冗余数据,开始尝试执行=========
偏移量======5
添加任务TIMER_PUBLISH_KEY_TODO_KTSDKLHLSK2
添加任务TIMER_PUBLISH_KEY_TODO_KTSDKLHLSK3
添加任务TIMER_PUBLISH_KEY_TODO_KTSDKLHLSK4
添加任务TIMER_PUBLISH_KEY_TODO_KTSDKLHLSK5
执行批次===========TIMER_PUBLISH_KEY_TODO_KTSDKLHLSK6======VALUES=====114102091,114102095,