You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rails中定时执行任务?含Action Cable广播场景及工具选型

Rails定时任务与Action Cable广播方案解答

问题1:每隔几秒执行动作,持续10分钟的实现

首先说结论:rufus-scheduler完全适配这个场景,它比Rails自带的Active Job更适合这种短间隔、有明确时长限制的定时任务——毕竟Active Job更偏向异步任务调度,而非这种周期性的短期任务。

给你一个可直接复用的实现思路:

  1. 先在Gemfile里加一行gem 'rufus-scheduler',然后跑bundle install
  2. 在config/initializers/下新建rufus_scheduler.rb初始化调度器
  3. 编写任务逻辑时,计算好结束时间(当前时间+10分钟),每次执行前检查是否超时,到点就停掉任务

示例代码:

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

scheduler = Rufus::Scheduler.new

# 把要重复执行的逻辑抽成方法,方便维护
def run_repeated_task
  # 这里替换成你的实际业务逻辑,比如更新状态、发送通知等
  puts "执行重复动作:#{Time.now}"
end

# 每隔3秒执行一次,持续10分钟
scheduler.every '3s' do |job|
  stop_time = job.start_time + 10 * 60
  if Time.now >= stop_time
    job.stop
    puts "任务已终止,已运行满10分钟"
  else
    run_repeated_task
  end
end

生产环境注意事项:如果用Puma这类多进程服务器,要么改成单进程模式,要么给rufus-scheduler加分布式锁(比如用Redis),不然每个进程都会启动一个任务,导致重复执行。

问题2:2分钟内每隔10秒通过Action Cable广播随机题目

这个场景rufus-scheduler同样好用,核心就是把定时任务和Action Cable的广播逻辑绑定,同时控制好任务的总时长。

具体实现步骤:

  1. 先确保Action Cable配置完成,有对应的频道(比如QuizChannel)来处理客户端订阅
  2. 用rufus-scheduler创建定时任务,每隔10秒触发一次广播,持续2分钟
  3. 每次触发时从题库里随机挑一道题,推送给指定的客户端群体(比如某个答题房间的所有用户)

示例代码:

# app/channels/quiz_channel.rb
class QuizChannel < ApplicationCable::Channel
  def subscribed
    # 让客户端订阅对应房间的流
    stream_from "quiz_room_#{params[:room_id]}"
  end
end

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

scheduler = Rufus::Scheduler.new

# 封装启动答题广播的方法,接收房间ID作为参数
def start_quiz_broadcast(room_id)
  # 2分钟=120秒,每隔10秒一次,总共12次
  total_rounds = 12
  current_round = 0

  # 创建定时任务
  job = scheduler.every '10s' do
    current_round += 1
    # 随机获取一道题目(假设你有Question模型)
    random_question = Question.order('RANDOM()').first
    # 广播到对应房间的流
    ActionCable.server.broadcast(
      "quiz_room_#{room_id}",
      { type: 'question', content: random_question.content }
    )

    # 达到次数后停止任务,并广播结束消息
    if current_round >= total_rounds
      job.stop
      ActionCable.server.broadcast(
        "quiz_room_#{room_id}",
        { type: 'notification', content: "答题时间到!" }
      )
    end
  end
end

# 你可以在控制器或服务类里调用这个方法,比如用户点击"开始答题"时:
# start_quiz_broadcast(params[:room_id])

额外小技巧:如果需要支持用户中途退出答题,建议把任务的引用存在Redis里,这样可以在用户退出时手动停止对应的任务,避免无效的广播占用资源。

内容的提问来源于stack exchange,提问作者Rohan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:24:20