告别定时任务烦恼:Rufus-Scheduler 全场景 Ruby 任务调度指南

告别定时任务烦恼:Rufus-Scheduler 全场景 Ruby 任务调度指南

【免费下载链接】rufus-scheduler scheduler for Ruby (at, in, cron and every jobs) 【免费下载链接】rufus-scheduler 项目地址: https://gitcode.com/gh_mirrors/ru/rufus-scheduler

你是否还在为 Ruby 项目中的定时任务管理而头疼?手动编写 cron 表达式易错难维护?担心任务重叠导致数据不一致?本文将系统讲解 Rufus-Scheduler 这款强大的 Ruby 任务调度库,从基础用法到高级特性,从常见陷阱到最佳实践,助你轻松掌控各种定时任务场景。读完本文,你将能够:

  • 熟练使用 5 种任务调度类型解决不同业务需求
  • 掌握线程安全与任务互斥的实现方案
  • 优雅处理任务超时、重试与错误捕获
  • 无缝集成 Rails 等 Web 框架
  • 避开 90% 的常见使用陷阱

项目概述:Ruby 世界的轻量级调度专家

Rufus-Scheduler 是一个纯 Ruby 实现的任务调度库,采用线程模式运行,支持 inateveryintervalcron 五种调度方式。作为进程内调度器,它无需额外服务即可运行,非常适合嵌入 Ruby 应用程序中管理定时任务。

# 一分钟上手示例
require 'rufus-scheduler'

scheduler = Rufus::Scheduler.new

# 3秒后执行
scheduler.in '3s' do
  puts 'Hello Rufus-Scheduler!'
end

# 每天9点执行
scheduler.cron '0 9 * * *' do
  puts 'Good morning!'
end

scheduler.join # 保持进程运行

核心优势与适用场景

特性优势适用场景
纯 Ruby 实现无需外部依赖,易于集成所有 Ruby 应用
多任务类型灵活应对各种时间需求定时任务、周期性任务
线程安全内置互斥与并发控制多任务并发执行
丰富选项超时、重试、互斥等高级特性复杂业务逻辑调度
轻量级内存占用小,性能优异嵌入式系统、微服务

与同类工具对比

工具类型优势劣势
Rufus-Scheduler进程内调度轻量、灵活、无需额外服务不支持持久化,进程退出任务丢失
WheneverCron 生成器与系统 cron 集成,持久化配置复杂,不支持动态任务
Sidekiq-Cron分布式调度支持持久化,分布式部署依赖 Redis,配置复杂
Clockwork进程内调度简单易用,低资源占用功能较少,扩展性有限

快速入门:从安装到第一个定时任务

环境准备与安装

Rufus-Scheduler 兼容 Ruby 2.5+ 版本,通过 RubyGems 安装:

# Gemfile
gem 'rufus-scheduler', '~> 3.8'

# 终端执行
bundle install

# 或直接安装
gem install rufus-scheduler

基础概念与核心组件

Rufus-Scheduler 主要由以下核心组件构成:

mermaid

你的第一个调度任务

创建一个简单的定时任务文件 scheduler_demo.rb

require 'rufus-scheduler'

# 创建调度器实例
scheduler = Rufus::Scheduler.new

# 1. 一次性任务:3秒后执行
scheduler.in '3s' do
  puts "一次性任务执行:#{Time.now}"
end

# 2. 指定时间任务:2025年1月1日执行
scheduler.at '2025-01-01 00:00:00' do
  puts "新年快乐!#{Time.now.year}"
end

# 3. 周期性任务:每10秒执行一次
scheduler.every '10s' do
  puts "周期性任务执行:#{Time.now}"
end

# 4. 间隔任务:任务执行完成后间隔5秒再次执行
scheduler.interval '5s' do
  puts "间隔任务开始:#{Time.now}"
  sleep 2 # 模拟任务执行耗时
  puts "间隔任务结束:#{Time.now}"
end

# 5. Cron任务:每分钟的第30秒执行
scheduler.cron '30 * * * *' do
  puts "Cron任务执行:#{Time.now}"
end

# 保持主进程运行
scheduler.join

运行程序:

ruby scheduler_demo.rb

你将看到各类任务按预定时间执行,展示了 Rufus-Scheduler 的基本功能。

深入任务类型:选择最适合你的调度方式

Rufus-Scheduler 提供五种任务调度类型,每种类型适用于不同场景。理解它们的特性与差异,是高效使用库的关键。

一次性任务:In 与 At

In 任务:延迟一段时间后执行,适用于需要延迟处理的场景。

# 基本语法:延迟字符串 + 代码块
scheduler.in '10s' do # 10秒后执行
  puts "10秒后执行"
end

# 支持多种时间单位
scheduler.in '1m' do # 1分钟
end
scheduler.in '2h' do # 2小时
end
scheduler.in '3d' do # 3天
end
scheduler.in '1w' do # 1周
end

# 数字形式(秒)
scheduler.in 3600 do # 3600秒 = 1小时
end

# 带参数的任务
scheduler.in '5s', argument: 'value' do |job|
  puts "任务参数:#{job.opts[:argument]}"
end

At 任务:在指定时间点执行,适用于特定时间点的任务。

# 基本语法:时间字符串 + 代码块
scheduler.at '2025-09-01 12:00:00' do
  puts "指定时间执行"
end

# 支持自然语言时间(需安装 chronic gem)
require 'chronic'
scheduler.at 'next tuesday 3pm' do
  puts "下周二下午3点执行"
end

# 时间对象
scheduler.at Time.now + 3600 do
  puts "1小时后执行"
end

In vs At 对比

特性In 任务At 任务
时间指定方式相对时间(如 '10s')绝对时间(如 '2025-09-01')
适用场景延迟执行定时执行
时间解析使用 fugit 库使用 fugit 或 chronic 库
精度秒级秒级

周期性任务:Every、Interval 与 Cron

Every 任务:按固定频率重复执行,注重执行间隔的规律性。

# 基本语法:频率字符串 + 代码块
scheduler.every '1m' do # 每分钟执行
  puts "每分钟执行一次"
end

# 复杂频率定义
scheduler.every '2h30m' do # 每2小时30分钟
end
scheduler.every '1d' do # 每天
end
scheduler.every '3w' do # 每3周
end

# 带选项的任务
scheduler.every '10s', overlap: false do # 禁止任务重叠
  puts "执行耗时操作,防止重叠"
  sleep 15 # 模拟耗时操作
end

# 限制执行次数
scheduler.every '5s', times: 3 do # 仅执行3次
  puts "这是第 #{job.count} 次执行"
end

# 设置开始与结束时间
scheduler.every '1m', first_in: '30s', last_in: '5m' do # 30秒后开始,5分钟后结束
  puts "限时执行任务"
end

Interval 任务:任务执行完成后间隔固定时间再次执行,注重任务间的间隔。

# 基本语法:间隔字符串 + 代码块
scheduler.interval '5s' do
  puts "任务执行完成后间隔5秒再次执行"
  sleep 2 # 模拟任务执行时间
end

# Every 与 Interval 对比示例
scheduler.every '10s' do
  puts "every任务:每隔10秒执行,不管前次是否完成"
  sleep 15 # 这里会导致任务重叠
end

scheduler.interval '10s' do
  puts "interval任务:执行完成后间隔10秒再次执行"
  sleep 15 # 实际执行间隔将是15+10=25秒
end

Cron 任务:基于 cron 表达式的高级定时任务,适合复杂时间规则。

# 基本语法:cron表达式 + 代码块
scheduler.cron '*/5 * * * *' do # 每5分钟执行
  puts "每5分钟执行一次"
end

# 常用cron表达式示例
scheduler.cron '0 9 * * *' do # 每天9点
end
scheduler.cron '30 8 * * 1' do # 每周一8:30
end
scheduler.cron '0 12 1 * *' do # 每月1日12点
end
scheduler.cron '0 0 * * 0' do # 每周日0点
end

# 带时区的cron任务
scheduler.cron '0 9 * * *', tz: 'Asia/Shanghai' do # 指定上海时区
  puts "上海时间每天9点执行"
end

# 六字段cron表达式(支持秒级)
scheduler.cron '*/10 * * * * *' do # 每10秒执行(六字段格式)
  puts "秒级cron任务"
end

三种周期性任务对比

特性EveryIntervalCron
时间定义执行频率任务间隔cron表达式
时间计算基于上次计划执行时间基于上次实际完成时间基于日历时间规则
适用场景严格频率控制避免任务重叠复杂时间规则
灵活性中等
学习曲线简单简单复杂

mermaid

高级特性:掌控任务调度的每一个细节

任务选项:定制你的调度规则

Rufus-Scheduler 提供丰富的任务选项,满足各种复杂调度需求:

并发控制

# 禁止任务重叠执行
scheduler.every '5s', overlap: false do
  puts "任务执行中,后续任务将等待"
  sleep 10 # 即使任务耗时超过调度频率,也不会重叠
end

# 使用互斥锁控制不同任务间的并发
scheduler.every '10s', mutex: 'database_update' do
  puts "数据库更新任务A"
end

scheduler.every '15s', mutex: 'database_update' do
  puts "数据库更新任务B"
end
# 以上两个任务将串行执行,避免同时更新数据库

# 多互斥锁
scheduler.every '5s', mutex: ['db', 'cache'] do
  puts "同时获取db和cache锁"
end

超时控制

# 设置任务超时时间
scheduler.every '1m', timeout: '30s' do
  begin
    puts "执行可能超时的任务"
    # 模拟可能超时的操作
    sleep 40
  rescue Rufus::Scheduler::TimeoutError
    puts "任务超时,已被中断"
  end
end

# 使用时间点作为超时
scheduler.in '10s', timeout: '12:00' do
  puts "必须在12点前完成"
end

生命周期控制

# 设置任务开始执行时间
scheduler.every '10s', first_in: '1m' do # 1分钟后开始执行
  puts "延迟启动任务"
end

# 设置任务结束时间
scheduler.every '10s', last_in: '5m' do # 5分钟后停止执行
  puts "限时执行任务"
end

# 结合使用
scheduler.every '1m', first_at: '2025-01-01 00:00', last_at: '2025-12-31 23:59' do
  puts "2025年全年执行的任务"
end

# 限制执行次数
scheduler.every '5s', times: 10 do
  puts "这是第 #{job.count} 次执行"
end

任务元数据与标识

# 为任务命名
scheduler.every '10s', name: '数据同步任务' do |job|
  puts "任务 #{job.name} 执行中"
end

# 为任务添加标签
scheduler.every '5s', tags: ['监控', '系统状态'] do
  puts "系统状态监控"
end

# 通过标签查找任务
jobs = scheduler.jobs(tag: '监控')
puts "找到 #{jobs.size} 个监控任务"

任务管理:监控与控制

任务查询与筛选

# 获取所有任务
all_jobs = scheduler.jobs
puts "当前调度任务总数:#{all_jobs.size}"

# 按类型筛选任务
cron_jobs = scheduler.cron_jobs
every_jobs = scheduler.every_jobs
interval_jobs = scheduler.interval_jobs

# 按标签筛选任务
monitor_jobs = scheduler.jobs(tag: 'monitor')
critical_jobs = scheduler.jobs(tags: ['critical', 'db'])

# 查找特定任务
job = scheduler.job('job_id') # 通过ID查找

任务状态监控

# 检查任务是否正在运行
scheduler.every '5s' do |job|
  if job.running?
    puts "任务 #{job.id} 正在运行"
  else
    puts "任务 #{job.id} 等待执行"
  end
end

# 获取任务执行次数
scheduler.every '10s' do |job|
  puts "任务已执行 #{job.count} 次"
end

# 查看任务下次执行时间
scheduler.every '1m' do |job|
  puts "下次执行时间:#{job.next_time}"
end

任务操作

# 动态修改任务频率
scheduler.every '10s' do |job|
  if some_condition
    job.frequency = 30 # 修改为30秒
    puts "任务频率已调整为30秒"
  end
end

# 暂停与恢复任务
scheduler.every '5s' do |job|
  if system_load_high?
    job.pause
    puts "系统负载过高,任务已暂停"
  end
end

# 恢复暂停的任务
def resume_jobs
  scheduler.jobs.each do |job|
    if job.paused? && system_load_normal?
      job.resume
      puts "任务 #{job.id} 已恢复"
    end
  end
end

# 立即执行任务
job = scheduler.every '1h' do
  puts "定期任务"
end
job.call # 立即执行一次

任务终止

# 取消单个任务
job = scheduler.every '5s' do
  puts "这是一个临时任务"
end

# 某个条件满足时取消任务
scheduler.every '1s' do |self_job|
  if job.completed?
    job.unschedule
    self_job.unschedule
    puts "临时任务已完成并取消"
  end
end

# 取消所有任务
def stop_all_jobs
  scheduler.jobs.each(&:unschedule)
  puts "所有任务已取消"
end

# 优雅关闭调度器
def shutdown_scheduler
  scheduler.shutdown(wait: true) # 等待当前任务完成
  puts "调度器已关闭"
end

错误处理与日志

全局错误处理

# 配置全局错误处理器
scheduler = Rufus::Scheduler.new

def scheduler.on_error(job, error)
  # 记录错误信息
  puts "任务 #{job.id} 执行出错:#{error.message}"
  puts error.backtrace.join("\n")
  
  # 根据错误类型处理
  if error.is_a?(DatabaseError)
    puts "数据库错误,尝试重新连接..."
    reconnect_database
  elsif error.is_a?(TimeoutError)
    puts "任务超时,已自动重试"
    job.call # 立即重试
  end
  
  # 严重错误时取消任务
  if error.is_a?(FatalError)
    job.unschedule
    puts "发生致命错误,任务 #{job.id} 已取消"
  end
end

任务级错误处理

# 任务内局部错误处理
scheduler.every '10s' do
  begin
    risky_operation
  rescue StandardError => e
    puts "任务执行出错:#{e.message}"
    # 局部错误处理逻辑
  end
end

# 结合全局和局部错误处理
scheduler.every '5s' do |job|
  begin
    # 可能出错的操作
    perform_database_operation
  rescue DatabaseConnectionError => e
    # 局部处理特定错误
    reconnect_database
  rescue StandardError => e
    # 其他错误交给全局处理器
    raise e
  end
end

日志配置

# 配置详细日志
require 'logger'
scheduler = Rufus::Scheduler.new(
  logger: Logger.new('scheduler.log'),
  log_level: :debug
)

# 自定义日志格式
class SchedulerLogger < Logger
  def format_message(severity, timestamp, progname, msg)
    "[#{timestamp.strftime('%Y-%m-%d %H:%M:%S')}] [#{severity}] #{msg}\n"
  end
end

scheduler.logger = SchedulerLogger.new('scheduler.log')

实战场景:从简单到复杂的调度方案

系统监控与健康检查

服务器资源监控

# 系统CPU监控
scheduler.every '30s' do
  cpu_usage = `top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}'`.to_f
  puts "当前CPU使用率:#{cpu_usage}%"
  
  if cpu_usage > 80
    send_alert("CPU使用率过高:#{cpu_usage}%")
  end
end

# 内存使用监控
scheduler.every '1m' do
  mem_info = `free -m`.split("\n")[1].split
  total = mem_info[1].to_i
  used = mem_info[2].to_i
  usage_percent = (used.to_f / total * 100).round(2)
  
  puts "内存使用率:#{usage_percent}% (#{used}/#{total}MB)"
  
  if usage_percent > 90
    send_alert("内存使用率过高:#{usage_percent}%")
  end
end

# 磁盘空间监控
scheduler.every '5m' do
  disk_usage = `df -h / | tail -n1 | awk '{print $5}'`.chomp
  puts "根分区使用率:#{disk_usage}"
  
  if disk_usage.to_i > 90
    send_alert("磁盘空间不足:#{disk_usage}")
  end
end

应用健康检查

# 数据库连接检查
scheduler.every '10s' do
  begin
    ActiveRecord::Base.connection.execute('SELECT 1')
    db_status = '正常'
  rescue
    db_status = '异常'
    reconnect_database
  end
  puts "数据库连接状态:#{db_status}"
end

# API端点监控
scheduler.every '30s' do
  begin
    response = Faraday.get('https://api.example.com/health')
    if response.status == 200
      puts "API健康检查通过"
    else
      send_alert("API健康检查失败,状态码:#{response.status}")
    end
  rescue
    send_alert("API健康检查失败,无法连接")
  end
end

数据同步与处理

数据库定时备份

# 每日数据库备份
scheduler.cron '0 2 * * *' do # 每天凌晨2点执行
  backup_time = Time.now.strftime('%Y%m%d_%H%M%S')
  backup_file = "backup_#{backup_time}.sql"
  
  puts "开始数据库备份:#{backup_file}"
  
  # 执行备份命令
  system("mysqldump -u#{DB_USER} -p#{DB_PASS} #{DB_NAME} > #{BACKUP_DIR}/#{backup_file}")
  
  # 检查备份结果
  if $?.success?
    puts "数据库备份成功:#{backup_file}"
    # 压缩备份文件
    system("gzip #{BACKUP_DIR}/#{backup_file}")
    
    # 删除7天前的备份
    system("find #{BACKUP_DIR} -name 'backup_*.sql.gz' -mtime +7 -delete")
  else
    send_alert("数据库备份失败")
  end
end

数据同步任务

# 定时数据同步
scheduler.every '1m' do
  # 仅同步新增数据
  last_sync_time = get_last_sync_time
  new_records = fetch_new_records(since: last_sync_time)
  
  if new_records.any?
    puts "发现 #{new_records.size} 条新记录,开始同步"
    sync_records(new_records)
    update_last_sync_time(Time.now)
    puts "数据同步完成"
  else
    puts "没有新记录需要同步"
  end
end

# 增量同步与全量同步结合
scheduler.every '5m' do # 每5分钟增量同步
  sync_incremental_data
end

scheduler.cron '0 1 * * *' do # 每天凌晨1点全量同步
  sync_full_data
end

定时任务与业务流程

订单超时处理

# 检查超时未支付订单
scheduler.every '1m' do
  timeout_orders = Order.where(status: 'pending').where('created_at < ?', 30.minutes.ago)
  
  if timeout_orders.any?
    puts "发现 #{timeout_orders.size} 个超时未支付订单"
    timeout_orders.each do |order|
      order.update(status: 'cancelled')
      # 恢复库存
      restore_inventory(order)
      # 发送通知
      send_order_cancelled_notification(order)
    end
  end
end

# 定时检查订单状态
scheduler.every '30s' do
  # 处理待发货订单
  process_pending_shipments
  
  # 自动确认收货
  confirm_received_orders
end

会员权益管理

# 会员到期提醒
scheduler.every '12h' do
  expiring_soon = User.where(membership_end_date: (Time.now)..(Time.now + 3.days))
  expiring_soon.each do |user|
    days_left = (user.membership_end_date - Time.now).to_i / (24 * 3600)
    send_reminder(user, "会员将于 #{days_left} 天后到期")
  end
end

# 会员状态自动更新
scheduler.cron '0 0 * * *' do # 每天凌晨执行
  # 会员到期处理
  expired_members = User.where(membership_end_date: ..Time.now).where(membership_status: 'active')
  expired_members.update_all(membership_status: 'expired')
  
  # 新会员欢迎邮件(延迟发送,确保数据稳定)
  new_members = User.where(created_at: (Time.now - 1.day)..Time.now, welcome_email_sent: false)
  new_members.each do |user|
    scheduler.in '10m' do # 10分钟后发送
      send_welcome_email(user)
      user.update(welcome_email_sent: true)
    end
  end
end

批量处理与报告生成

数据报表定时生成

# 每日销售报表
scheduler.cron '30 23 * * *' do # 每天23:30执行
  report_date = Date.yesterday
  puts "生成 #{report_date} 销售报表"
  
  sales_data = generate_sales_report(report_date)
  
  # 保存报表数据
  SalesReport.create(
    date: report_date,
    total_sales: sales_data[:total],
    order_count: sales_data[:count],
    average_order_value: sales_data[:average],
    data: sales_data[:details]
  )
  
  # 生成CSV报表
  generate_csv_report(sales_data, "sales_report_#{report_date.strftime('%Y%m%d')}.csv")
  
  # 发送报表邮件
  send_report_email(
    to: 'management@example.com',
    subject: "#{report_date} 销售报表",
    body: "昨日总销售额:#{sales_data[:total]} 元,订单数:#{sales_data[:count]}",
    attachments: ["sales_report_#{report_date.strftime('%Y%m%d')}.csv"]
  )
end

# 每周数据汇总
scheduler.cron '0 1 * * 0' do # 每周日凌晨1点执行
  generate_weekly_report
end

# 每月业务分析
scheduler.cron '0 3 1 * *' do # 每月1日凌晨3点执行
  generate_monthly_business_analysis
end

批量数据处理

# 批量处理待审核内容
scheduler.every '5m' do
  pending_items = ContentItem.where(status: 'pending').limit(100)
  
  if pending_items.any?
    puts "处理 #{pending_items.size} 条待审核内容"
    pending_items.each do |item|
      begin
        # 自动审核处理
        if auto_approve?(item)
          item.update(status: 'approved')
        else
          item.update(status: 'needs_review')
        end
      rescue => e
        puts "处理内容 #{item.id} 出错:#{e.message}"
      end
    end
  end
end

# 异步处理大文件
scheduler.every '1m' do
  pending_files = FileProcessingJob.where(status: 'pending')
  
  pending_files.each do |job|
    # 检查系统负载,负载低时才处理
    if system_load_low?
      process_file_async(job) # 异步处理文件
      job.update(status: 'processing')
    end
  end
end

Rails 集成:构建企业级定时任务系统

Rails 环境配置

Gemfile 配置

# Gemfile
gem 'rufus-scheduler', '~> 3.8'
gem 'fugit', '~> 1.8' # 时间解析库

初始化配置

# config/initializers/rufus_scheduler.rb
require 'rufus-scheduler'

# 确保只在生产环境的web服务器中启动一个调度器实例
if defined?(Rails) && Rails.env.production?
  # 对于Passenger部署
  if defined?(PhusionPassenger)
    PhusionPassenger.on_event(:starting_worker_process) do |forked|
      if forked
        # 工作进程 fork 后启动调度器
        Rails.application.config.rufus_scheduler = Rufus::Scheduler.new
        Rails.application.config.rufus_scheduler.start
        load Rails.root.join('config', 'scheduler_tasks.rb')
      end
    end
  # 对于Puma部署
  elsif defined?(Puma) && File.basename($0) == 'puma'
    Rails.application.config.rufus_scheduler = Rufus::Scheduler.new
    Rails.application.config.rufus_scheduler.start
    load Rails.root.join('config', 'scheduler_tasks.rb')
  end
else
  # 开发环境直接启动
  Rails.application.config.rufus_scheduler = Rufus::Scheduler.new
  Rails.application.config.rufus_scheduler.start
  load Rails.root.join('config', 'scheduler_tasks.rb')
end

任务文件组织

# config/scheduler_tasks.rb
# 主任务调度文件

# 导入所有任务模块
Dir[Rails.root.join('app', 'schedulers', '**', '*.rb')].each { |file| require file }

# 注册所有任务
Rails.application.config.rufus_scheduler.instance_eval do
  # 系统监控任务
  SystemMonitorScheduler.new(self).schedule!
  
  # 数据同步任务
  DataSyncScheduler.new(self).schedule!
  
  # 定时报表任务
  ReportScheduler.new(self).schedule!
  
  # 业务逻辑任务
  OrderProcessingScheduler.new(self).schedule!
  UserManagementScheduler.new(self).schedule!
end

任务组织与封装

任务类封装

# app/schedulers/base_scheduler.rb
class BaseScheduler
  def initialize(scheduler)
    @scheduler = scheduler
  end
  
  # 子类必须实现该方法
  def schedule!
    raise NotImplementedError, "子类必须实现 schedule! 方法"
  end
  
  protected
  
  # 日志辅助方法
  def log(message)
    Rails.logger.info "[Scheduler] #{self.class.name}: #{message}"
  end
  
  # 错误处理辅助方法
  def handle_error(job, error)
    log "任务 #{job.id} 执行出错: #{error.message}"
    ErrorNotifier.notify(error, context: { job: job.inspect })
  end
end

# app/schedulers/order_processing_scheduler.rb
class OrderProcessingScheduler < BaseScheduler
  def schedule!
    # 定时检查超时订单
    @scheduler.every '1m', overlap: false, name: '超时订单处理' do |job|
      begin
        process_timeout_orders
        log "超时订单处理完成"
      rescue => e
        handle_error(job, e)
      end
    end
    
    # 定时处理待发货订单
    @scheduler.every '30s', overlap: false, name: '待发货订单处理' do |job|
      begin
        process_pending_shipments
        log "待发货订单处理完成"
      rescue => e
        handle_error(job, e)
      end
    end
    
    # 每日订单统计
    @scheduler.cron '0 0 * * *', name: '每日订单统计' do |job|
      begin
        generate_daily_order_report
        log "每日订单统计完成"
      rescue => e
        handle_error(job, e)
      end
    end
  end
  
  private
  
  def process_timeout_orders
    # 实现超时订单处理逻辑
    OrderTimeoutProcessor.call
  end
  
  def process_pending_shipments
    # 实现待发货订单处理逻辑
    PendingShipmentProcessor.call
  end
  
  def generate_daily_order_report
    # 实现订单统计逻辑
    DailyOrderReportGenerator.call(date: Date.yesterday)
  end
end

任务依赖注入

# app/services/order_timeout_processor.rb
class OrderTimeoutProcessor
  # 使用类方法调用,便于测试和调度
  def self.call
    new.call
  end
  
  def call
    timeout_orders = Order.pending.where('created_at < ?', 30.minutes.ago)
    return if timeout_orders.empty?
    
    timeout_orders.each do |order|
      process_order(order)
    end
  end
  
  private
  
  def process_order(order)
    ActiveRecord::Base.transaction do
      order.update(status: 'cancelled')
      # 恢复库存
      InventoryService.restore(order)
      # 记录日志
      OrderLog.create(order: order, action: 'timeout_cancel', details: "订单超时自动取消")
    end
  end
end

部署与监控

Capistrano 部署配置

# config/deploy.rb
namespace :deploy do
  namespace :rufus do
    desc '重启 Rufus 调度器'
    task :restart do
      on roles(:app) do
        execute "cd #{current_path} && bundle exec rake scheduler:restart RAILS_ENV=#{fetch(:rails_env)}"
      end
    end
  end
  
  # 部署后重启调度器
  after 'deploy:published', 'deploy:rufus:restart'
end

# lib/tasks/scheduler.rake
namespace :scheduler do
  desc '启动 Rufus 调度器'
  task start: :environment do
    system("nohup bundle exec rails runner 'Rails.application.config.rufus_scheduler.join' > #{Rails.root}/log/scheduler.log 2>&1 &")

【免费下载链接】rufus-scheduler scheduler for Ruby (at, in, cron and every jobs) 【免费下载链接】rufus-scheduler 项目地址: https://gitcode.com/gh_mirrors/ru/rufus-scheduler

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值