spring+redis+mysql定时重发

产品需求:发布信息时候,用户可以选择重新发布时间,重新发布间隔时间以及次数,如重发6次时间间隔为2分钟。

实现设计:

    可以通过定时查询数据库的发布时间,以及信息中重发次数时间,update数据(或者直接写一个厉害的update语句也可以),但是定时的查询整个数据库表,而且中间还会涉及到运算,无法命中索引,会使数据库压力较大,咨询好多朋友,大多数建议降低数据查询次数,如半个小时update一次,但是这样时间误差会很大,产品无法接受。

    在通过仔细调研以之后,计算使用spring定时任务、redis中list结构、update数据实现。当用户发布信息的时候,依据次数生成多个序列的key,并将信息id存储到redis中(如果多个信息,在同一个序列中,直接在list尾部添加数据),spring通过定时任务,每隔10秒依据生成的序列,读取redis中list数据,将list中数据,转化为id1,id2,id3,通过数据库in关键字,实现更新数据。

关键代码:

    spring定时任务配置

    <bean id="republishCargoJobInvake" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="republishCargoJob" />
        <property name="targetMethod" value="doRepublish" />
        <property name="concurrent" value="false" />
    </bean>
        <bean id="triggerRepublishCargo" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="republishCargoJobInvake" />    
        <property name="cronExpression" value="0/10 * * * * ?" />

    </bean>
         <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="triggerRepublishCargo" />
            </list>
        </property>   
    </bean>                
public class RepublishCargoJob {
    private Log log = LogFactory.getLog(RepublishCargoJob.class);
    /**
     * 货源重发定时任务
     */
    public void doRepublish(){
        //获取WebApplicationContext
        WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
        //获取redis读取序列工具
        TimedPublishTask timedPublishTask=(TimedPublishTask) wac.getBean("timedPublishTask");
        //获取本次需要更新的id list
        List<String>list=timedPublishTask.getBatch();
        if(list!=null&&list.size()>0){
            //update数据库
            ResourceCargoInfoDAO resourceCargoInfoDAO=(ResourceCargoInfoDAO) wac.getBean("resourceCargoInfoDAO");
            ResourceCargoInfo rci=new ResourceCargoInfo();
            rci.setRciPublishDatetime(DateUtils.getDateTime());
            ResourceCargoInfoExample resourceCargoInfoExample=new ResourceCargoInfoExample();
            resourceCargoInfoExample.createCriteria().andRciIdIn(list).andRciAvalibleStatusEqualTo(1);
        try {
            resourceCargoInfoDAO.updateByExampleSelective(rci, resourceCargoInfoExample);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            log.error("=======重发货源失败======");
        }
        }
    }
package com.ada.wuliu.common.redis.timertask;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.InitBinder;

import com.ada.wuliu.common.dictionaries.utils.DateUtils;
import com.ada.wuliu.common.dictionaries.utils.StateCode;
import com.ada.wuliu.common.dto.result.SimpleResult;
import com.ada.wuliu.common.redis.dao.RedisList;
import com.ada.wuliu.common.redis.dao.RedisString;
import com.ada.wuliu.common.redis.dto.TimerPublish;
import com.ada.wuliu.common.redis.util.SessionImpl_Temp_01;
import com.ada.wuliu.common.redis.util.StringUtil;
@Component("timedPublishTask")
public class TimedPublishTask {
    //定时任务执行周期
    public Integer taskFrequency=10000;
    //默认任务开始\(^o^)/
    private String defaultIncr="1000";

    private static String TIMER_PUBLISH_KEY_TODO=null;

    @Autowired
    private RedisList redisList;
    @Autowired
    private RedisString redisString;
    private final static Log log = LogFactory.getLog(TimedPublishTask.class);
    @Resource(name="connectionFactory")
    private JedisConnectionFactory connectionFactory;
    public TimedPublishTask(){
        initKeyHeader();
    }
    private void initKeyHeader(){
        if(TIMER_PUBLISH_KEY_TODO==null){
            TIMER_PUBLISH_KEY_TODO="TIMER_PUBLISH_KEY_TODO_CYC_"+StringUtil.getLocalOsName();
        }
    }
    /**
     * 
     * @param timerPublish 需要重新发布的对象
     */
    public  SimpleResult 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();
            List<Long>list=new ArrayList<Long>();
            for(int i=1;i<number+1;i++){
                //生成添加的批次号
                long addBatchNo=i*frequency/taskFrequency+Long.parseLong(batchNo);
                addBatchNo=addBatchNo-1;
                String batchKey=TIMER_PUBLISH_KEY_TODO+addBatchNo;
                //list 第一个为更新时间 如果更新时间<当前时间10秒则updatelist索引key对应数据
                log.info("新添加批次号:======"+batchKey+"====="+key);
                List<String> 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();
        }
    }
    
    /**
     * 
     * @param batchNo 获取本批次需要更新的对象的key
     * @return
     */
    public  List<String> getBatch(){

        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();
            }
            List<String>list=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<offset;i++){
                        batchNoRe++;
                        log.info("添加任务"+TIMER_PUBLISH_KEY_TODO+batchNoRe);
                        List<String>listre=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);
            return list;
        } 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(List<String>list){
        if(list==null)return null;
        StringBuffer sb=new StringBuffer();
        for(String s:list){
            sb.append(s+",");
        }
        return sb.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,
posted @ 2017-09-05 13:37  流氓剑客  阅读(1420)  评论(0编辑  收藏  举报