定时任务
cron
安装
- centos
yum install vixie-cron crontabs
- debian
apt-get install cron
- 大多数发行版都会自带 cron
cron 通常分为三部分
- crond 是 cron 在系统内的守护进程,
- crontab 是管理 cron 任务的工具
- 配置文件
- 配置文件的位置?
cron 表达式
* * * * *
分 时 日 月 星期
特别的
@yearly 0 0 1 1 * 每年运行一次
@monthly 0 0 1 * * 每月运行一次
@weekly 0 0 * * 0 每星期运行一次
@daily 0 0 * * * 每日运行一次
@hourly 0 * * * * 每小时运行一次
* * * * * 每分钟运行一次 这个就没有特殊的名称了
@reboot
将作业配置为在守护程序启动时运行一次。
由于 cron 通常永远不会重新启动,因此这通常用于系统启动时运行的任务
cron 如何使用
常用的命令
- 列出定时任务
crontab -l
- 编辑定时任务
和直接修改配置文件相比, crontab -e 在退出时会检测一次语法crontab -e
cron 的实现
- vixie cron 这个应该是现在最流行的 cron 版本了 https://github.com/vixie/cron
- busybox 版的 cron https://github.com/mirror/busybox/blob/HEAD/miscutils/crond.c https://github.com/mirror/busybox/blob/HEAD/miscutils/crontab.c
- 其他流行的实现包括 anacron dcron mcron cronie
- 对于大多数发行版的 cron 而言
- cron 是无状态的,
- cron 在代码里写死了60秒扫描一次配置文件,
- 为什么cron没有秒级的任务?
- 因为代码里写死了60秒扫描一次配置文件
- 为什么是60秒?
- 大概可能因为是 祖宗之法不可变 吧
- 为什么cron没有秒级的任务?
- 扫描配置文件时,遇到符合规则的任务就会运行,
- 对于单个任务的状态, cron 是不会判断的,不判断上次任务的成功或失败,不判断上次任务运行的时间
- cron 的实现要比想象中的简单不少
- 其实现在的 cron 也是通过 systemd 运行的
crond.service systemctl status crond.service
使用bash脚本实现的隔秒运行和单例运行
- 隔秒运行
* * * * * cron.sh
#!/bin/bash
step=1 #间隔的秒数,不能大于60
for (( i = 0; i < 60; i=(i+step) )); do
$(php test.php)
sleep $step
done
exit 0
- 单例运行
要使用文件锁确保当前只有一个脚本在运行
flock命令
例子1
```
#!/usr/bin/env bash
LOCK_FILE=/var/lock/test.lock
exec 99>"$LOCK_FILE"
flock -n 99
if [ "$?" != 0 ]; then
echo "$0 already running"
exit 1
fi
#脚本要做的其他事情
```
例子2
```
#!/usr/bin/env bash
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
# 如果${FLOCKER}环境变量没有设置,则尝试将脚本本身加锁,如果加锁成功,则运行当前脚本,(并且带上原有的参数),否则的话静默退出。
#脚本要做的其他事情
systemd 的 timer
如何创建定时器
- 创建一个 service
- 在 systemd 的配置目录下新建一个名为 MyTimer.service 的文件 /usr/lib/systemd/system/MyTimer.service
[Unit] Description=MyTimer [Service] ExecStart=/bin/bash /path/to/MyTimer.sh
- 可以像这样运行一次测试是否有生效
systemctl start MyTimer.service
- 然后创建一个 timer
- 在 systemd 的配置目录下新建一个名为 MyTimer.service 的文件 /usr/lib/systemd/system/MyTimer.timer
[Unit] Description=Runs mytimer every hour [Timer] # 定时器 OnUnitActiveSec=1h # 定时器触发的任务 Unit=mytimer.service [Install] # 开机启动时的依赖项,大多数情况下都是填这个 WantedBy=multi-user.target
- 最后把 timer 加入到开机启动中
systemctl enable MyTimer.timer
定时器的相关命令
列出所有定时器
systemctl list-timers
systemctl 的命令也能直接用在 定时器中
start stop status enable disable
查看所有单元
systemctl list-unit-files
查看所有 Service 单元
systemctl list-unit-files --type service
查看所有 Timer 单元
systemctl list-unit-files --type timer
和 cron 比较
- systemed 的 timer 可以实现 秒级 任务,但 crond 不可以
- systemed 的 timer 比 crond 的灵活很多,基本接近 windows 的计划任务了
Windows 的 计划任务
- windows 的 计划任务 包括了 开机启动 和 定时任务
- 以前用 at 命令操作,现在用 schtasks 命令操作
- 用图形界面也是可以的
taskschd.msc https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks https://learn.microsoft.com/zh-cn/windows/win32/taskschd/task-scheduler-start-page https://learn.microsoft.com/zh-cn/windows/win32/taskschd/using-the-task-scheduler
- schtasks
- 查询任务
schtasks /Query schtasks /Query /TN "\Microsoft\Windows\WwanSvc\NotificationTask" schtasks /Query /V /TN "\Microsoft\Windows\WwanSvc\NotificationTask"
- 删除任务
schtasks /Delete /TN taskname /F taskname 是任务名 /F 是强制执行
- 创建任务
schtasks /Create /TN taskname /TR taskrun 定时任务的表达式有一点混乱,最好还是去看文档 每分钟运行一次 schtasks /create /sc minute /mo 1 /tn "task name" /tr "command" 开机启动,设置开机启动的任务需要管理员权限 schtasks /create /sc ONSTART /tn "task name onstart" /tr "command"
- 查询任务
- 用 powershell 的 cmdlets 也能创建 windows 的定时任务
https://learn.microsoft.com/en-us/powershell/module/scheduledtasks https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.utility/new-timespan
- 使用 powershell 创建定时任务
- 这是分开创建的,分开创建可以使用多个触发器
$Act1 = New-ScheduledTaskAction -Execute "command"; $Time = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 1) ; Register-ScheduledTask -TaskName "SoftwareScan" -Trigger $Time -Action $Act1;
- 这是合成一条命令
每分钟运行一次 Register-ScheduledTask -TaskName "SoftwareScan" -Trigger (New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 1)) -Action (New-ScheduledTaskAction -Execute "command"); 开机启动 Register-ScheduledTask -TaskName "SoftwareScan" -Trigger (New-ScheduledTaskTrigger -AtStartup) -Action (New-ScheduledTaskAction -Execute "command");
- 使用多个触发器的例子
每天11:00和23:00运行一次 $Act1 = New-ScheduledTaskAction -Execute "command"; $Time1 = New-ScheduledTaskTrigger -Daily -At 11am; $Time2 = New-ScheduledTaskTrigger -Daily -At 11pm; Register-ScheduledTask -TaskName "SoftwareScan2" -Trigger ($Time1, $Time2) -Action $Act1;
- 这是分开创建的,分开创建可以使用多个触发器
- 计划任务一个触发器只能设定一个时间,但一个任务可以有多个触发器,schtasks命令只能设置一个触发器
- 如果计划任务运行的是 bat 或 ps 或 bash 脚本,任务运行时会弹出一个黑框
- 如果要隐藏黑框,需要以管理员权限生成任务,或者用 vbs 的方式调用脚本,但vbs的方式很容易被杀毒软件拦截
schtasks /create /sc minute /mo 1 /ru System /tn "acme_cron" /tr "command" Register-ScheduledTask -User System -TaskName "SoftwareScan" -Trigger (New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 1)) -Action (New-ScheduledTaskAction -Execute "command");
- 通过 xml 文件你创建计划任务
- 例子
Register-ScheduledTask -Xml test.xml -TaskName "task name" schtasks /create /xml test.xml /tn "task name"
- xml 的具体语法 https://learn.microsoft.com/zh-cn/windows/win32/taskschd/task-scheduler-schema
- 通过 xml 创建的计划任务能实现多个触发器,和 gui 的功能基本一致了
- 例子
在 windows 下如何运行 cron
- 关键是要能每分钟扫描一次 cron 的配置文件,然后执行符合规则的任务
- 有一个能每分钟运行一次或持续运行的程序用来扫描 cron 的配置文件
- 直接运行一个命令行,然后不关闭窗口
- 让程序以服务的形式运行
- 可以直接在代码里写好
- 也可以用 nssm 或 winsw 这类工具把普通的程序封装成服务
- 使用 计划任务 ,每分钟运行一次
- 能解释 cron 表达式
- 这种库在 github 十分的多,而且各种语言实现的都有,但怎样实现 @reboot ?
- 如果是 一直运行的命令行 或 服务 的形式
- 就一开始启动时运行一次,以后就忽略
- 如果是 计划任务 ,每分钟运行一次
- 通过 系统启动时间 或 系统运行时长 来判断是否需要运行 @reboot
wmic path Win32_OperatingSystem get LastBootUpTime (get-date) - (gcim Win32_OperatingSystem).LastBootUpTime (Get-Date (Get-CimInstance -ClassName win32_operatingsystem).LastBootUpTime -UFormat %s).ToString() // 开机时间的10位时间戳 锁定 注销 睡眠 休眠 都 不会计入停机时间
其他
Linux 的 at 和 atq
at 和 atq 通常用于一次性任务
Windows 的 at
还有哪些好用的定时任务或计划任务工具?
- mysql 的事件调度器 (Event Scheduler)
- PostgreSQL 的 PgAgent 也能实现定时任务