从双十一凌晨准时开启秒杀,看任务调度实践历程

每天早上七点三十,准时推送干货

一、介绍

说到定时任务,相信大家都不陌生,在我们实际的工作中,用到定时任务的场景可以说非常的多,例如:

  • 双 11 的 0 点,定时开启秒杀

  • 每月1号,财务系统自动拉取每个人的绩效工资,用于薪资计算

  • 使用 TCP 长连接时,客户端按照固定频率定时向服务端发送心跳请求

等等,定时器像水和空气一般,普遍存在于各个场景中,在实际的业务开发中,基本上少不了定时任务的应用。

总结起来,一般定时任务的表现有以下几个特征:

  • 1.在某个时刻触发,例如11.11号0点开启秒杀

  • 2.按照固定频率周期性触发,例如每分钟发送心跳请求

  • 3.预约指定时刻开始周期性触发,例如从12.1号开始每天7点闹钟响起

说了这么多,也不BB了,下面我们就来点干活,列举一些实际项目中使用的相关工具!

二、crontab 定时器

2.1、介绍

crontab 严格来说并不是属于 java 内的,它是 linux 自带的一个工具,可以周期性地执行某个shell脚本或命令。

由于 crontab 在实际开发中应用比较多, 特别是对于运维的人,crontab 命令是必须用到的命令,自动化运维中一定少不了它,而且 crontab 表达式跟我们后面要介绍的其他定时任务框架,例如 Quartz,Spring Schedule 的 cron 表达式类似,所以这里先介绍 crontab。

简而言之,crontab 就是一个自定义定时器。

命令格式如下:

.---------------- minute (0 - 59)
|  .------------- hour (0 - 23)
|  |  .---------- day of month (1 - 31)
|  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
|  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) ...
|  |  |  |  |
*  *  *  *  *  command
  • 第一个参数(minute):代表一小时内的第几分,范围 0-59。

  • 第二个参数(hour):代表一天中的第几小时,范围 0-23。

  • 第三个参数(day):代表一个月中的第几天,范围 1-31。

  • 第四个参数(month):代表一年中第几个月,范围 1-12。

  • 第五个参数(week):代表星期几,范围 0-7 (0及7都是星期天)。

  • 第六个参数(command):所要执行的指令。

具体应用的时候,时间定义大概是长下面这个样子!

# 每5分钟执行一次命令
*/5 * * * * Command
# 每小时的第5分钟执行一次命令
5 * * * * Command
# 指定每天下午的 6:30 执行一次命令
30 18 * * * Command
# 指定每月8号的7:30分执行一次命令
30 7 8 * * Command
# 指定每年的6月8日5:30执行一次命令
30 5 8 6 * Command
# 指定每星期日的6:30执行一次命令
30 6 * * 0 Command
2.2、具体示例

centOS操作系统为例,创建一个定时任务,每分钟执行某个指定shell脚本,过程如下!

  • 首先安装 crond 相关服务

yum -y install cronie yum-cron
  • 编写一个输出当前时间到日志的shell脚本

#创建一个test.sh脚本
vim /root/shell/test.sh

#脚本内容如下,将内容输出到file.log文件
echo `date '+%Y-%m-%d %H:%M:%S'` >> /root/shell/file.log
  • 先执行一下脚本,观察内容是否输出到file.log文件

sh /root/shell/test.sh
  • 查看日志文件内容

cat /root/shell/file.log

如果出现以下内容,说明运行正常!

  • 接着再来创建一个定时任务,每分钟执行一次test.sh

#编辑定时任务【删除-添加-修改】
crontab -e
  • 在文件末尾加入如下信息

#每分钟执行一次test.sh脚本
*/1 * * * * sh /root/shell/test.sh
  • 如果想查定时任务是否加入,可以通过如下命令

#查看crontab定时任务
crontab -l
  • 最后就是重启定时任务

##重新载入配置
systemctl reload crond
#重启服务
systemctl restart crond
  • 查看file.log文件实时输出内容,观察test.sh脚本是否被执行

tail -f /root/shell/file.log

从结果上看,运行正常!

  • 如果想查看定时任务日志,可通过如下命令进行查看

tail -f /var/log/cron

如果想移除某个定时任务,直接输入crontab -e命令,并移除对应的脚本,然后刷新配置、重启服务即可!

如果想深入的了解crontab使用,可以参考这篇文章,里面总结得比较好, 这里就不再多说了。

三、java 自带的定时器

3.1、Timer

Timer 定时器,由 jdk 提供的java.util.Timerjava.util.TimerTask两个类组合实现。

其中TimerTask表示某个具体任务,而Timer则是进行调度任务处理。

实现过程也很简单,示例如下:

import java.util.Timer;
import java.util.TimerTask;

public class TimerTest extends TimerTask {

    private String jobName;

    public TimerTest(String jobName) {
        this.jobName = jobName;
    }

    @Override
    public void run() {
        System.out.println("execute " + jobName);
    }

    public static void main(String[] args) {
        Timer timer = new Timer();
        long delay1 = 1 * 1000;
        long period1 = 1 * 1000;
        // 从现在开始 1 秒钟之后,每隔 1 秒钟执行一次 job1
        timer.schedule(new TimerTest("job1"), delay1, period1);

        long delay2 = 2 * 1000;
        long period2 = 2 * 1000;
        // 从现在开始 2 秒钟之后,每隔 2 秒钟执行一次 job2
        timer.schedule(new TimerTest("job2"), delay2, period2);
    }
}

输出结果:

execute job1
execute job2
execute job1
execute job1
execute job2
execute job1
execute job1
execute job2
...

Timer 的优点在于简单易用,由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务

具体原因如下:

  • 1.当一个线程抛出异常时,整个 Timer 都会停止运行。例如上面的 job1 抛出异常的话,job2 也不会再跑了

  • 当一个线程里面处理的时间非常长的时候,会影响其他 job 的调度。例如,如果 job1 处理的时间要 1 分钟, 那么 job2 至少要等 1 分钟之后才能跑。

基于上面的原因,Timer 现在生产环境中都不在使用!

3.2、ScheduledExecutor

鉴于 Timer 的上述缺陷,从 Java 5 开始,推出了基于线程池设计的 ScheduledExecutor。

其设计思想是,每一个被调度的任务都会由线程池中一个线程来管理执行,因此任务是并发执行的,相互之间不会受到干扰。需要注意的是,只有当任务的执行时间到来时,ScheduedEx

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值