登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

systemd.timer 怎样按日历表达式定时启动服务

来源:17golang原创

时间:2026-10-27 00:27:06 285浏览 收藏

systemd.timer 可以通过 OnCalendar 按日期、星期和时刻启动一个服务。我的配置取舍是先明确时区,再把任务命令写进 service,把日历规则留在 timer 中;这样调整时间时不用改动任务本身。

例如每天北京时间 03:20 执行一次,可设置 OnCalendar=*-*-* 03:20:00 Asia/Shanghai,并启用对应的 timer。计划触发与业务成功需要分别确认。

官方手册地址:https://www.freedesktop.org/software/systemd/man/latest/systemd.timer.html。下面使用系统级 systemd;示例命令供管理员按实际机器环境使用,文中没有本地运行记录。

明确每天几点运行以及使用哪个时区

这里的“每天”指墙上时钟的日期。每天固定时刻适合 OnCalendar;“上次任务结束后再等一段时间”属于另一类需求,不要把它翻译成一个固定钟点。表达式中显式写时区,可减少服务器默认时区不同造成的歧义。

任务要求日历表达式
每天北京时间03:20*-*-* 03:20:00 Asia/Shanghai
周一至周五UTC的09:00Mon..Fri *-*-* 09:00:00 UTC
每月1日北京时间03:20*-*-01 03:20:00 Asia/Shanghai

daily 是每天零点的简写,不能理解成“安装后每24小时”。星期条件还会约束日期,而不是和日期条件任选一个满足。使用有夏令时的地区时,还应依据该时区确认切换日的实际日历安排。

把任务命令放进独立的 service

我倾向于先用一个不修改业务数据的任务理解配置。创建 /etc/systemd/system/calendar-note.service,内容如下。这里用绝对路径的 echo 写入一条演示消息;正式使用时,替换成已经部署好的任务程序,并补齐适合该程序的运行用户、工作目录和权限。

# 演示服务:只输出消息,不处理业务数据
[Unit]
Description=Calendar note example

[Service]
# 一次性任务执行结束后退出
Type=oneshot
ExecStart=/usr/bin/echo calendar-note

calendar-note.timer 默认对应 calendar-note.service;也可以通过 Unit 显式指定其他单元。本例保留同名关系。不要为这种重复执行的一次性任务设置 RemainAfterExit=yes:服务一直保持 active,会影响后续触发。timer 也不会在目标服务已 active 时额外启动一个相同服务实例。

日历定时器与一次性服务的静态配置关系
左侧是 timer 配置,右侧是 service 配置;连线表达配置归属和同名关联,并非执行时间线。

为同名 timer 配置日历规则

创建 /etc/systemd/system/calendar-note.timer。本例选择每天03:20,允许短暂的调度精度窗口,并关闭额外随机延迟。

# 演示每天按北京时间安排任务
[Unit]
Description=Daily calendar note

[Timer]
OnCalendar=*-*-* 03:20:00 Asia/Shanghai
# 允许一秒精度窗口,并不承诺硬实时启动
AccuracySec=1s
RandomizedDelaySec=0
# 不要求停机期间错过的时刻补跑
Persistent=false

[Install]
WantedBy=timers.target

如果业务允许机器停机后恢复时补一次,可将 Persistent 改为 true。它会记录触发时间,并在重新激活定时器时判断是否错过日历时刻;错过多个时刻也不是逐条重放所有历史任务。是否补跑应由业务决定,例如日报通常还需要任务程序明确计算要处理的日期。

AccuracySec 约束允许的时间窗口;RandomizedDelaySec 用于额外打散触发时刻。两者都不是任务超时设置。即使缩小精度窗口,系统负载与任务自身执行耗时仍然存在,不能把03:20理解成业务结果必然已经落库。

日历匹配、精度窗口和补跑策略的配置边界
日历、时间容差和恢复策略分别承担不同职责;图中所有节点都是配置概念,不代表真实执行结果。

启用调度并区分计划与任务结果

保存两个单元后,让管理员在目标机器使用以下命令。只启用 timer;直接启动 service 会立即执行演示任务,与等待日历触发不是一回事。

# 通知系统管理器重新读取单元文件
sudo systemctl daemon-reload
# 设置开机启用,并立即激活定时器
sudo systemctl enable --now calendar-note.timer
# 查看定时器的下一次计划和上次触发信息
systemctl list-timers --all calendar-note.timer
# 查看任务服务的状态以及近期日志
systemctl status calendar-note.service
journalctl -u calendar-note.service -n 30 --no-pager

判断时分开看两件事:timer 是否有下一次安排,service 是否执行成功。首次尚未到点时,服务没有执行记录不等于定时器失败。已经触发却失败,则根据服务的退出状态与日志继续定位命令路径、权限和程序错误。业务是否成功仍应依据程序自身的结果;日志出现一条开始消息不能代替业务完成信号。

修改已激活的 timer 后,重新加载单元并重启该 timer,使新的安排生效;如果启用了 Persistent,重新激活时还要考虑补跑条件。只改配置文件而不通知管理器,会让文件内容与已加载配置不一致。

暂停调度与处理常见疑问

暂停示例调度时,执行以下操作。它停止并取消开机启用 timer,不会撤销此前业务操作,也不会自动终止已经运行的 service。是否中断正在执行的正式任务,需要先确认任务支持安全停止。

# 停止日历调度并取消开机启用
sudo systemctl disable --now calendar-note.timer
# 恢复已备份的单元文件后,让管理器重新读取
sudo systemctl daemon-reload

为什么到了设置时刻还没结束?

触发时刻只负责发起服务激活。先考虑精度窗口、额外随机延迟以及服务是否已经 active,再区分任务启动时间与任务完成时间。

为什么每天的任务没有补齐停机期间的数据?

Persistent 解决的是错过日历触发后的补一次激活,不是业务数据的逐日补账。任务程序应自行设计日期范围和幂等处理。

如何避免换机器后时间发生偏移?

表达式明确指定业务时区,并确保目标机器具备对应时区数据、系统时钟正常。部署前把运行用户、命令路径、日历时区、补跑选择和成功信号一同写入交接记录。

日历语法参考 systemd.time 官方说明,单元行为参考 systemd.timer 官方说明。发行版自带版本可能不同,具体选项以目标系统随附手册为准。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>