实现一个精确到秒但是执行率不高的定时任务

这个需求的大约背景如下:

客户会在新建项目的时候选择项目的上下架时间,精确到秒,如果设置的是未上架,需要在过了上架时间之后将项目上架;同理,需要在下架时间过了之后将项目状态改为下架。不过系统里的项目不多,总数也就几百的样子,同时在发行中的项目数量更少。

实现思路(包括过程)

1. 首先考虑到的是spring的定时任务,每秒钟去扫一次库,如果有需要上下架的,做上下架的处理。

然后立刻马上就觉得这种实现方案不现实,这样系统会崩。

2. 经过沟通制定了第二种方案,基于切面的定时任务,在每次查询项目的时候判断这批次查出来的项目里有没有下架的,有的话启动一个线程,执行变更状态的操作。

这种方案的问题在于不占用系统的资源,但是数据很不准确,而且得到查询的结果之后可能会有后续的操作,后续要改代码的地方太多,而且会带来很多新的bug,不太可取

3. 得到一个折中方案:设置一个监听,把需要的项目上下架的时间都取到,每秒钟执行一次,判断当前时间有没有需要处理的上下架请求,有的话处理,没有的话等待下次执行。

实现如下:

1. 建立一个任务,继承TimerTask,在启动工程的时候加一个监听

ProjectTaskListener projectTaskListener = (ProjectTaskListener) context.getBean("compProjectTaskListener");
Timer timer = new Timer();
timer.schedule(projectTaskListener, 1000, 1000);

2. 任务实现如下的功能

※ 初始化数据 :将已经上下架的数据处理掉;将需要处理的数据放入到队列中

public void init() {
    //处理数据
    marketingProjectMapper.updateProjectUp();
    marketingProjectMapper.updateProjectDown();

    ProjectTaskDealVO projectTaskDeal = ProjectTaskDealVO.getInstance();

    //初始化数据
    List<ProjectTaskVO> projectTaskList = marketingProjectMapper.queryPreDealList();
    if(projectTaskList != null && projectTaskList.size() > 0) {
        System.out.println("have data");
        projectTaskDeal.setProjectTasks(projectTaskList);
        projectTaskDeal.setNextDealTime(projectTaskList.get(0).getDealTime());
    }
}

※逻辑处理: 判断当前时间和下个需要处理的时间,如果时间到了,根据业务类型处理数据,并将需要处理的时间列表中最早的时间移除,将时间队列中第一条数据的处理时间赋值给下次处理时间;如果未到时间,不做任何操作

/**
 * 逻辑处理
 */
public void deal() {
    ProjectTaskDealVO projectTaskDeal = ProjectTaskDealVO.getInstance();

    Long now = System.currentTimeMillis();
    if(projectTaskDeal.getProjectTasks().size() > 0) {
        if(now >= projectTaskDeal.getNextDealTime()) {
            ProjectTaskVO projectTask = projectTaskDeal.getProjectTasks().get(0);

            if(projectTask.getOperate() == 1) {
                marketingProjectMapper.updateProjectUp();
            } else {
                marketingProjectMapper.updateProjectDown();
            }

            projectTaskDeal.getProjectTasks().remove(0);
            if(projectTaskDeal.getProjectTasks().size() > 0) {
                projectTaskDeal.setNextDealTime(projectTaskDeal.getProjectTasks().get(0).getDealTime());
            } else {
                projectTaskDeal.setNextDealTime(0L);
            }
        }
    }

}

※执行方法: 继承父类需要实现的方法,加一个初始化次数的参数,只有在项目启动的时候执行一次

@Override
public void run() {
    if(firstRun == 0) {
        init();
        firstRun++;
    }
    deal();
}

3 业务数据的维护

从上面的代码中可以看出,业务的对象用到了单例模式实现,这样保证系统运行中只有一个实例存在,不论是在任务里还是在业务里取到的都是同一个实例

※ 单例模式实现代码如下: 私有化构造方法;静态的getInstance()方法:如果实例存在,返回实例,如果不存在,创建实例,所以在创建一次之后就一直存在了

private static  ProjectTaskDealVO instance;

private ProjectTaskDealVO() {

}

/**
 * 创建实体类
 * @return
 */
public static synchronized ProjectTaskDealVO getInstance() {
    if(instance == null) {
        instance = new ProjectTaskDealVO();
    }
    return instance;
}

※业务中维护如下

/**
 * 构想: 项目数量比较少,上下架修改时间操作也不会频繁,项目上下架有两种操作 1 新增 2 修改 队列中原有的数据不做处理
 *        可能队列中需要处理的时间要比实际上要处理的时间多一些,尽量保证数据有效性
 * @param dealTime 处理时间
 * @param operate 上架 下架 排队到期
 * @param
 */
private void addTimeToTask(long dealTime, int operate) {
    ProjectTaskDealVO projectTaskDeal = ProjectTaskDealVO.getInstance();

    ProjectTaskVO newTask = new ProjectTaskVO();
    newTask.setDealNum(1);
    newTask.setDealTime(dealTime);
    newTask.setOperate(operate);
    if(projectTaskDeal.getProjectTasks().size() > 0) {
        for(int i = 0; i < projectTaskDeal.getProjectTasks().size(); i++) {
            ProjectTaskVO compTask = projectTaskDeal.getProjectTasks().get(i);
            if(compTask.getDealTime().longValue() == newTask.getDealTime().longValue()) {
                if(compTask.getOperate() == newTask.getOperate()) {
                    compTask.setDealNum(compTask.getDealNum() + 1);

                    projectTaskDeal.getProjectTasks().set(i, compTask);

                    break;
                }
            } else if(compTask.getDealTime().longValue() >= newTask.getDealTime().longValue()) {
                newTask.setDealNum(1);
                projectTaskDeal.getProjectTasks().add(i, newTask);
            }
        }
        if(projectTaskDeal.getNextDealTime() > dealTime) {
            projectTaskDeal.setNextDealTime(dealTime);
        }
    } else {
        projectTaskDeal.setNextDealTime(dealTime);
        projectTaskDeal.getProjectTasks().add(newTask);
    }
}

 

在项目开发的过程中走过的一些误区:

※因为平时做的都是单线程的开发,对于一些定义特别不明确,开始考虑的是时间数据也在任务中维护,为了保证在业务中的调用,就把任务设置成了单例模式,然后这个就和spring的配置不兼容(spring是后来加载的),这样的设计确实也违反了类的单一性原则,然后就考虑把业务数据取出来单独维护,定时任务通过spring维护

※在timer和多线程中纠结了很久,最后还是选择了timer。。。。

转载于:https://my.oschina.net/u/3660594/blog/1636076

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值