使用 Rust 运行 Cron 作业

什么是 Cron 作业?
Cron 作业是计划定期执行的自动化任务。它们经常用于多种任务,包括备份、数据库更新、电子邮件发送等等,并且不需要每次都由专人启动。

cron 作业的核心是 cron 守护进程。将其视为一个幕后工作人员,不断检查一个特殊列表(称为 cron 表或 crontab),以查看需要完成哪些任务以及何时完成。列表中的每个任务都包含一个唯一的计划(一个 cron 表达式),指示守护程序何时运行它,然后是要采取的特定操作。

cron 的工作方式非常简单,但也很聪明。每分钟,该工作人员都会检查列表以查找当前时间安排的任何任务。如果找到任何任务,它会立即在单独的进程中启动这些任务。这种设置可确保任务准确地按预期发生,无论是每天的特定时间,还是更不寻常的模式,例如仅在特定日期或月份。

Cron 语法
cron 模式是使用 cron 作业配置自动化操作的一个重要方面。它经过巧妙设计,可让您几乎在您能想到的任何时间安排任务,从每一分钟到特定日期或月份的精确时间。该模式包含六个字段,每个字段由空格分隔,每个字段代表一个不同的时间单位。

  * * * * * *
  | | | | | |
  | | | | | └─── day of week (0 - 7) (Sunday to Saturday; 7 is also Sunday)
  | | | | └───── month (1 - 12)
  | | | └─────── day of month (1 - 31)
  | | └───────── hour (0 - 23)
  | └─────────── minute (0 - 59)
  └───────────── second (optional, 0 - 59)


在 Rust 中实现 Cron 作业
Rust 生态系统拥有丰富的用于各种任务的库,包括调度 cron 作业。其中,由于多种原因, cron crate脱颖而出成为最合适的选择。它易于集成,文档齐全,并且不断更新,使其比其他库更可靠。

首先,通过在终端中运行以下命令来创建一个新的 Rust 项目。

cargo new rust_cron_job
cd rust_cron_job

这些命令rust_cron_job在同名的新目录中创建一个新的 Rust 项目,其中包含基本的Rust 二进制项目结构。

接下来,将 cron 库添加到您的项目中,方法是将其包含在Cargo.toml文件的以下[dependencies]部分中:

[dependencies]
cron = "0.12.1"
chrono = "0.4" 

此步骤通知 Cargo 您的项目依赖于 cron 和 chronocrates;后者是在 Rust 中处理日期和时间的先决条件。

现在您已经设置了 Rust 项目并声明了必要的依赖项,您可以开始编写您的第一个 cron 作业。编辑 项目 src目录中的main.rs文件以包含以下 Rust 代码:


use chrono::Utc;
use cron::Schedule;
use std::str::FromStr;
use std::thread;

fn main() {
    let expression = "0/5 * * * * *";
    let schedule = Schedule::from_str(expression).expect("Failed to parse CRON expression");

    for datetime in schedule.upcoming(Utc).take(1) {
        let now = Utc::now();
        let until = datetime - now;
        thread::sleep(until.to_std().unwrap());
        println!("Hello, world!");
    }
}

在此示例中,任务设置为按照 cron 表达式 的定义每五秒运行一次"0/5 * * * * *"。该代码将此表达式转换为时间表,并计算下一次执行之前的确切时间。然后,它会使用 , 暂停执行thread::sleep(),直到达到该时间,以确保精确计时。一旦等待结束,它就会执行任务,在本例中打印“ Hello, world! ”。

通过在终端中运行以下命令来执行 cron 作业:

cargo run
此命令编译并运行您的 Rust 应用程序,您应该看到“Hello, World!”运行代码五秒后打印到您的终端,展示了 cron 作业的运行情况。

但是,重要的是要了解该任务不会继续每五秒执行一次,而是在第一次执行后停止。这种行为是由于 Rust 的设计造成的,与 JavaScript 等语言不同,Rust 不包含内在的事件循环。 Rust 需要显式管理连续任务执行或循环。对于在 Rust 应用程序的单次运行中定期重复的任务,您必须显式实现循环逻辑。

为了修改我们的示例以实现连续执行,我们可以引入一个循环,该循环不断检查下一个计划时间并相应地执行任务。使用以下代码 更新 src/main.rs以实现此目的。

use chrono::{Local, Utc};
use cron::Schedule;
use std::str::FromStr;
use std::thread;

fn main() {
    let expression = "0/5 * * * * *";
    let schedule = Schedule::from_str(expression).expect("Failed to parse CRON expression");

    loop {
        let now = Utc::now();
        if let Some(next) = schedule.upcoming(Utc).take(1).next() {
            let until_next = next - now;
            thread::sleep(until_next.to_std().unwrap());
            println!(
                "Running every 5 seconds. Current time: {}",
                Local::now().format("%Y-%m-%d %H:%M:%S")
            );
        }
    }
}

在此连续版本中,应用程序保持活动状态,进入一个循环,频繁检查计划以查看是否到了再次执行任务的时间。如果是这样,应用程序将休眠直到下一个执行时间,然后打印当前时间,演示任务每 5 秒运行一次,如下所示。


Running every 5 seconds. Current time: 2024-03-12 18:22:35
Running every 5 seconds. Current time: 2024-03-12 18:22:40
Running every 5 seconds. Current time: 2024-03-12 18:22:45
Running every 5 seconds. Current time: 2024-03-12 18:22:50
Running every 5 seconds. Current time: 2024-03-12 18:22:55
Running every 5 seconds. Current time: 2024-03-12 18:23:00
Running every 5 seconds. Current time: 2024-03-12 18:23:05
Running every 5 seconds. Current time: 2024-03-12 18:23:10
...