调度器集成
这一篇是扩展专题——如果你需要定时任务能力,怎么把它接进这个项目。
当前状态
这个项目目前没有集成 soulsoft_scheduler。代码里没有 addScheduler()、IJob 实现或 HostedService 注册。
不过,项目的架构(DI 容器、DbContext、分层结构)已经为接入调度器准备好了条件。
接入前的准备
先确保你理解这些基础概念(建议先看 Scheduler 专题文档):
- Scheduler 如何注册进 Spire 宿主
IJob接口的定义和生命周期HostedService如何驱动定时任务
专题入口:Scheduler 总览
适合接入的场景
这个博客项目有四类场景天然适合定时任务:
1. 定时发布
职责:查询"发布时间已到但还设为私密"的文章,批量公开。
cangjie
// 伪代码:定时发布 Job
public class PublishScheduledPostsJob <: IJob {
let blogRepo: IBlogRepository
public func execute(context: JobExecutionContext): Unit {
let now = DateTime.now()
let posts = blogRepo.findScheduledBefore(now) // 仓储层封装查询
for (post in posts) {
post.isPublic = 1
blogRepo.update(post)
}
blogRepo.saveChanges()
}
}2. 热门榜单重算
职责:定时刷新热门文章排名,避免每次请求实时计算。
3. 数据清理
职责:清理过期草稿、无效评论、临时文件。
4. 缓存预热
职责:首页加载慢时,定时把热门数据加载到缓存。
推荐接入方式
直接在现有 web-api 宿主里加,不要另开一个进程:
cangjie
// 在 main.cj 里加
builder.services.addScheduler() {
options =>
options.scheduleJob<PublishScheduledPostsJob>("0 */5 * * *") // 每5分钟
options.scheduleJob<RecalculatePopularJob>("0 0 2 * *") // 每天凌晨2点
}
builder.services.addSchedulerHostedService()原因:
- 现有 DI 容器、DbContext、Repository、配置体系全部可以直接复用
- 不需要为定时任务单独维护一套数据库连接
- 部署仍然是单进程
分层建议
定时任务也遵循分层原则:
text
IJob(调度入口) → 只做参数编排,不写业务
Application Service → 真正的业务逻辑
Repository → 数据访问cangjie
// ✅ IJob 只做编排
public class PublishScheduledPostsJob <: IJob {
let service: BlogService // 不直接注 DbContext
public func execute(context: JobExecutionContext): Unit {
service.publishScheduledPosts()
}
}
// ✅ BlogService 做业务
public func publishScheduledPosts(): Unit {
let posts = blogRepo.findScheduledBefore(DateTime.now())
for (post in posts) {
post.isPublic = 1
blogRepo.update(post)
}
blogRepo.saveChanges()
}不推荐在 IJob 里直接操作 DbContext——Job 是调度框架的入口,业务逻辑应该留在应用层。
注意事项
- 默认禁止同一任务并发执行,避免多个实例同时操作同一批数据
- 定时任务的事务管理独立于请求事务,需要在 Job 内部自行控制事务边界
- 调度器依赖于宿主进程一直在运行,部署时确保进程监控和自动重启