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

为同名 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 官方说明。发行版自带版本可能不同,具体选项以目标系统随附手册为准。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
215 收藏
-
219 收藏
-
184 收藏
-
261 收藏
-
284 收藏
-
478 收藏
-
318 收藏
-
422 收藏
-
392 收藏
-
272 收藏
-
252 收藏
-
432 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习