用Rust编写后台调度计划任务Cronjob

💡 原文中文,约5600字,阅读约需14分钟。
📝

内容提要

本文介绍了如何使用Shuttle将cron作业编写为Web服务。首先需要配置数据库,然后实现具体服务和tower服务。最后构建worker并运行。部署完成后,将获得部署信息和数据库URL字符串。

🔎

延伸解读

持久化队列:避免任务丢失的关键

文章强调使用PostgresStorage作为持久作业队列。如果没有持久化存储,当Web服务中断时,已计划的任务会丢失。通过将任务存储在Postgres中,即使服务重启,任务也能恢复执行。这对于数据备份、提醒等关键任务尤为重要,确保了可靠性。

Apalis与Tower的协作模式

文章展示了如何结合Apalis和Tower构建cron服务。Tower的ServiceBuilder用于添加重试层和共享数据扩展,而Apalis则负责调度和执行。这种分层设计让任务处理逻辑与调度解耦,便于扩展和重用。例如,通过Extension注入CronjobData,任务函数可以访问共享状态。

Cron表达式与任务定义细节

文章使用Schedule::from_str("* * * * * *")定义每秒执行,但实际cron作业通常按分钟或小时调度。注意,Apalis要求任务结构体实现From<DateTime<Utc>>且不能包含其他字段,这限制了任务数据的传递方式。共享数据需通过Extension注入,在任务函数中通过JobContext获取。

部署与生产环境考量

文章提到使用cargo shuttle deploy一键部署,并自动提供数据库URL。但在生产环境中,需要手动管理Postgres实例或使用Terraform等IaC工具。此外,本地开发依赖Docker启动数据库,而Shuttle运行时在部署时会提供数据库,简化了配置但可能隐藏了生产环境的复杂性。

❓

Q&A

如何用Rust和Shuttle编写一个cron作业?

使用Shuttle将cron作业编写为Web服务。主要步骤包括:配置数据库、实现具体服务、创建tower服务、构建worker并运行。部署时运行cargo shuttle init和cargo shuttle deploy。

用Shuttle部署cron作业需要哪些依赖?

需要安装以下依赖:serde(序列化任务)、chrono(时间戳)、apalis(任务队列框架)、sqlx(数据库交互)、shuttle-shared-db(提供数据库)、tower(托管cron作业)。

为什么cron作业需要持久化存储?

如果没有持久的作业队列,当网络服务出现中断时,作业会消失。使用PostgresStorage可以将Postgres用于持久作业队列,确保作业不会丢失。

如何配置Shuttle中的数据库连接?

在main函数上添加#[shuttle_shared_db::Postgres]注解,获取连接字符串,然后使用PgPoolOptions设置Postgres连接池,例如最小和最大连接数均为5。

如何定义apalis的cron作业结构体?

实现apalis::prelude::Job的结构体,并实现From<chrono::DateTime<chrono::Utc>>,不能包含其他字段。例如:struct Reminder(DateTime<Utc>); impl Job for Reminder { const NAME: &'static str = "reminder::DailyReminder"; }

如何构建和启动worker来执行cron作业?

使用WorkerBuilder创建worker,指定存储和CronStream(包含调度和定时器),然后使用Monitor注册worker并运行。例如:let worker = WorkerBuilder::new("morning-cereal").with_storage(storage.clone()).stream(CronStream::new(schedule).timer(TokioTimer).to_stream()).build(service); Monitor::new().register(worker).run().await;

🏷️

标签

➡️

继续阅读