Планировщик задач Rust: полное руководство для разработчиков
Планировщики задач — неотъемлемая часть современной разработки. Они позволяют автоматизировать выполнение рутинных операций: периодическую очистку кэша, отправку уведомлений, синхронизацию данных и многое другое. В экосистеме Rust существует несколько мощных библиотек для реализации планировщиков. В этой статье мы разберём три основных подхода: cron, tokio-cron-scheduler и job_scheduler.
1. Базовый планировщик с библиотекой cron
Библиотека cron предоставляет минималистичный функционал для парсинга cron-выражений и вычисления времени следующего запуска. Она не содержит встроенного цикла выполнения, поэтому её часто комбинируют с tokio или std::thread.
Установка:
[dependencies]
cron = "0.12"
chrono = "0.4"
tokio = { version = "1", features = ["full"] }
Пример простого планировщика:
use cron::Schedule;
use chrono::Utc;
use std::str::FromStr;
use std::thread;
use std::time::Duration;
fn main() {
// Каждую минуту
let expression = "0 * * * * *";
let schedule = Schedule::from_str(expression).unwrap();
loop {
let now = Utc::now();
if let Some(next) = schedule.upcoming(Utc).next() {
let duration = next - now;
if duration.num_seconds() > 0 {
thread::sleep(Duration::from_secs(duration.num_seconds() as u64));
}
println!("[{}] Задача выполнена!", Utc::now());
}
}
}
Этот подход даёт полный контроль, но требует ручного управления циклом. Подходит для простых сценариев.
2. Асинхронный планировщик tokio-cron-scheduler
Для современных асинхронных приложений на базе tokio идеально подходит библиотека tokio-cron-scheduler. Она предоставляет готовый планировщик с поддержкой сложных cron-расписаний, джобов с идентификаторами и обработчиков ошибок.
Установка:
[dependencies]
tokio-cron-scheduler = "0.10"
tokio = { version = "1", features = ["full"] }
Пример с двумя задачами:
use tokio_cron_scheduler::{Job, JobScheduler};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let sched = JobScheduler::new().await?;
// Задача 1: каждые 5 секунд
let job1 = Job::new_async("*/5 * * * * *", |_uuid, _lock| {
Box::pin(async move {
println!("[Job1] Выполнено в {:?}", std::time::SystemTime::now());
})
})?;
// Задача 2: каждую минуту
let job2 = Job::new_async("0 * * * * *", |_uuid, _lock| {
Box::pin(async move {
println!("[Job2] Минутная задача");
})
})?;
sched.add(job1).await?;
sched.add(job2).await?;
sched.start().await?;
// Держим приложение запущенным
tokio::signal::ctrl_c().await?;
println!("Планировщик остановлен");
Ok(())
}
Преимущества: автоматическое управление временем, поддержка удаления/паузы задач, асинхронное выполнение без блокировок.
3. Простой планировщик job_scheduler
Библиотека job_scheduler ориентирована на максимальную простоту. Она не требует асинхронного рантайма и подходит для консольных утилит или встраивания в уже существующие синхронные проекты.
Установка:
[dependencies]
job_scheduler = "1.2"
chrono = "0.4"
Пример:
use job_scheduler::{Job, JobScheduler};
use std::time::Duration;
fn main() {
let mut sched = JobScheduler::new();
sched.add(Job::new("1/10 * * * * *".parse().unwrap(), || {
println!("Выполнение задачи каждые 10 секунд");
}));
loop {
sched.tick();
std::thread::sleep(Duration::from_millis(500));
}
}
Этот планировщик крайне лёгкий, но не поддерживает асинхронные замыкания и сложные сценарии отмены задач.
Сравнительная таблица
- cron — минималистичный парсер, полный контроль
Также в библиотеке
Тестирование в TypeScript: Полное руководство по фреймворкам и инструментам
Планировщик задач на Go (Golang): cron, gocron и asynq
Мониторинг приложений Go (Golang): библиотеки и инструменты