crontab用法 时间配置_定时任务系列之crontab&Timer实现

267d4731e467bf824b8e0bf37c8468ca.png

19d1a361cabc60b2f0aec04f1e25c03d.png

背景

我们在业务开发中经常碰到需要定时触发的一些业务场景。比如我们系统的一些业务需要统计一天的流量数据,在每天凌晨发送邮件给相关负责人XX。或者,每个月底要发送报表给XX。
人工实现是不可能的,我是不会凌晨爬起来给你手动触发事件的 ; )
所以我们需要一个定时任务处理框架。你有两个选择:选择开源的成熟框架或其他已有的轮子;现有框架满足不了,自己造轮子。
但我觉得大部分人对定时任务的要求都没有那么高… 我们还是要考虑开发成本的嘛,所以我们大可以选择现成的定时任务实现就可以啦。
我们在这一系列中会介绍shell、spring scheduler、quartz、timer、xxl-job等,会有相关代码实现以及部分的源码解读。

如何选择一个定时任务框架

这个问题是最重要的。选择一个技术不是因为这个技术好,而是适合你的业务场景
我们选择定时任务框架的时候可以根据几个简单的标准【标准可以根据业务适当调整】

  • 易于配置,低耦合。
  • 占用资源少,性能好。
  • 能配置失败策略,如重试 or 通知。
  • 支持分布式集群调度。
  • 易于扩展。
  • 执行记录可视化。
  • 支持kill正在执行的任务。
  • 有可视化界面。

crontab

如果你是linux系统,very good! 我们可以通过很简单的方式实现定时任务的配置和调用。
linux带有一个crontab的命令。这个命令如何使用呢?建议大家可以先使用man命令查下具体参数,下面给大家展示示例。

# [-e 编辑定时任务,编辑完成wq保存退出]
> crontab -e
*/1 * * * * echo `date` >> /jason/test-crontab.log
*/1 * * * * echo `time` >> /jason/crontab-test.log
 
# -l 展示定时任务列表
> crontab -l
*/1 * * * * echo `date` >> /jason/test-crontab.log
*/1 * * * * echo `date` >> /jason/crontab-test.log
 
# 查看相应的日志
> cat /jason/test-crontab.log
Sun Aug 25 21:12:01 CST 2019
Sun Aug 25 21:13:01 CST 2019
Sun Aug 25 21:14:01 CST 2019
Sun Aug 25 21:15:01 CST 2019
Sun Aug 25 21:16:01 CST 2019
 
> cat /jason/crontab-test.log
Sun Aug 25 21:12:01 CST 2019
Sun Aug 25 21:13:01 CST 2019
Sun Aug 25 21:14:01 CST 2019
Sun Aug 25 21:15:01 CST 2019
Sun Aug 25 21:16:01 CST 2019

优点

  • 简单粗暴。
  • 支持cron表达式。
  • 支持实时修改删除配置。

缺点

  • 过于简单,导致其他的定时模式不支持。
  • cron表达式支持尺度只能到分钟级别。
  • 在生产中可能会导致需要其他角色(运维)参与业务。
  • 不支持分布式调度等。【参考上面的标准】

应用场景

适用于那种调度频率不是很频繁、执行失败可以容忍的定时任务。
需要将自己的代码写的尽量健壮。

Timer

Timer是Java提供的一个定时任务工具。
我们可以通过代码来看看Timer是如何处理定时任务的。

// 声明一个Timer对象
Timer timer = new Timer();
// 调用schedule,传入task(继承Runnable),和需要触发任务的时间,此处设置为当前时间的5s后。
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        log.warn("timer --------- timer");
    }
}, new Date(new Date().getTime() + 5000));

Timer内部维护了一个TimeTask的数组,通过对添加的任务进行触发时间排序,挨个来触发。对于多线程的并发操作,内部采用了sync关键字的方式解决。简单看下面代码

private void sched(TimerTask task, long time, long period) {
        if (time < 0)
            throw new IllegalArgumentException("Illegal execution time.");

        // 防止值太大
        if (Math.abs(period) > (Long.MAX_VALUE >> 1))
            period >>= 1;

        // 加锁操作,因为要添加任务到queue了。
        synchronized (queue) {
            if (!thread.newTasksMayBeScheduled)
                throw new IllegalStateException("Timer already cancelled.");
            // 根据传入的参数,处理task
            synchronized (task.lock) {
                if (task.state != TimerTask.VIRGIN)
                    throw new IllegalStateException(
                            "Task already scheduled or cancelled");
                // 设置的触发时间被赋给task
                task.nextExecutionTime = time;
                task.period = period;
                task.state = TimerTask.SCHEDULED;
            }
            // 此处是添加操作,涉及到数组的copy & 任务排序等
            queue.add(task);
            if (queue.getMin() == task)
                queue.notify();
        }
    }

    // queue的添加方法
    void add(TimerTask task) {
        // Grow backing store if necessary
        if (size + 1 == queue.length)
            queue = Arrays.copyOf(queue, 2*queue.length);

        queue[++size] = task;
	// 处理排序
        fixUp(size);
    }

    // 处理触发时间的排序
    private void fixUp(int k) {
        while (k > 1) {
            int j = k >> 1;
            if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
                break;
            TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
            k = j;
        }
    }

优点

  • Timer的方式与上面相比至少不需要其他角色参与
  • Timer的方式比较简单。
    ps : 感觉在强行扯…

9a4b07b19c53fec7d63d70af454e715d.png

缺点

Timer的缺点比起优点来看,好像多了很多。比如没有可视化界面这种我们先不提,具体有以下几个

  • 只能支持传入触发时间和周期。不支持cron表达式等方式。
  • 每个任务都是一个线程,无线程池配置。
  • 配置需要重启工程。
  • 最上面其他功能都不支持。

总结

今天介绍了两个比较简单的方式,大家应该了解了其运行原理和它们存在的一些问题。后面我们继续介绍其他的定时任务的框架实现,看看其他的实现是怎么解决这些问题的,又提供了什么有用的功能。还是那句话,挑最适合自己的。如果今天介绍的这两种方式已经能满足你的业务场景,那么使用也是可以的。

6f590c5ce7c724762ea6f6fb62a27c28.png

觉得文章不错的话,欢迎转发收藏(请注明出处哦)。能够关注公众号就更好啦~
作者:BigBigBigPeach
地址:https://www.deepstack.top/articles/2019/08/28/1566987456928.html
微信公众号:deepstack

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值