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

Rails 5.2.4中如何从Sidekiq Worker调用控制器Action方法

问题解答

原实现错误原因

你的当前实现逻辑存在本质错误:

  • Sidekiq Worker 完全脱离 Rails 控制器的HTTP请求上下文运行,redirect_to 重定向方法、execute_scheduler_production_execution_path 这类路由辅助方法都属于ActionController的专属实例方法,Worker中不存在这些方法,所以会抛出NoMethodError。
  • 你的需求是把耗时的脚本执行逻辑移到后台,而非在后台进程中调用控制器接口,当前实现方向完全错误。

修复方案

步骤1:抽离公共脚本执行逻辑

把你原控制器execute方法中--- 脚本交互逻辑部分 ---的代码抽离为独立的服务类,避免逻辑重复冗余:

# app/services/script_executor.rb
class ScriptExecutor
  # 入参为ProductionExecution实例,返回脚本执行的输入、输出计数
  def self.run(production_execution)
    # 此处粘贴你原控制器中的脚本执行逻辑
    # 把原逻辑中的global_input_count、global_output_count整理为哈希返回
    {
      input_count: 实际输入条数,
      output_count: 实际处理条数
    }
  end
end

步骤2:重写Sidekiq Worker

直接在Worker中执行业务逻辑,不要调用控制器相关方法:

class Scheduler::ScriptWorker
  include Sidekiq::Worker
  sidekiq_options queue: :default, tags: ['script']

  def perform(execution_id)
    Rails.logger.info "Sidekiq任务启动,执行ID:#{execution_id}"
    production_execution = ProductionExecution.find(execution_id)
    # 此处把原控制器中获取statuses的逻辑移过来
    statuses = 原控制器中statuses的获取逻辑

    # 更新任务为运行中状态
    production_execution.update!(
      started_at: Time.now, 
      status_id: statuses.find { |x| x["code"] == "RUNNING" }.id
    )

    begin
      # 执行脚本逻辑
      result = ScriptExecutor.run(production_execution)
      # 执行成功更新状态
      production_execution.update!(
        ended_at: Time.now,
        status_id: statuses.find { |x| x["code"] == "FINISHED" }.id,
        source_records_count: result[:input_count],
        processed_count: result[:output_count],
        error_message: nil
      )
    rescue => e
      # 异常捕获,执行失败更新状态,方便排查问题
      production_execution.update!(
        ended_at: Time.now,
        status_id: statuses.find { |x| x["code"] == "FAILED" }.id,
        error_message: e.message
      )
      # 可按需抛出异常触发Sidekiq重试机制
      raise e
    end
  end
end

步骤3:调整控制器execute方法

现在控制器仅负责接收请求、将任务推入Sidekiq队列后直接返回响应:

# POST /production_executions/1/execute
def execute
  @production_execution = ProductionExecution.find(params[:id])
  # 将任务推入后台队列
  Scheduler::ScriptWorker.perform_async(@production_execution.id)

  respond_to do |format|
    format.html { redirect_back fallback_location: @production_execution, notice: "任务已提交后台执行" }
    format.js
  end
end

补充说明

  • 如果需要定时调度任务,可直接使用sidekiq-cron配置定时规则触发Scheduler::ScriptWorker即可,不需要再走控制器接口。
  • 可以在ProductionExecution的详情页增加状态刷新逻辑,方便用户查看后台任务的执行进度和结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 02:36:03