【问题标题】:How to call controller action from _Sidekiq Worker in Rails 5.2.4?如何从 Rails 5.2.4 中的 _Sidekiq Worker 调用控制器操作?
【发布时间】:2021-12-19 12:38:18
【问题描述】:

我的 RoR 应用程序从 Linux 操作系统触发数据处理脚本(SAS 技术)。 Scheduler::ProductionExecutionsController 中的 execute 方法驱动与脚本的交互。

调度器::ProductionExecutionsController

  # POST /production_executions/1/execute
  def execute
    @production_execution = ProductionExecution.find(params[:id])
    @production_execution.update_attributes(started_at: Time.now, 
                                            status_id: statuses.find { |x| x["code"] == "RUNNING" }.id)

--- Scripts interaction ---

  @production_execution.update_attributes(ended_at: Time.now,
                                          status_id: statuses.find { |x| x["code"] == "FINISHED" }.id,
                                          source_records_count: global_input_count,
                                          processed_count: global_output_count,
                                          error_message: nil
                                          )

  respond_to do |format|
    format.html { redirect_back fallback_location: @production_execution, notice: @msg }
    format.js
  end

使用以下语法调用此方法:link_to "Go!", execute_scheduler_production_execution_path(execution)。路由已定义,并按预期工作。

由于某些脚本可能需要一分钟以上的时间才能执行,我需要在专门的作业中执行脚本,有时还要安排它们。所以我安装了 Sidekiq 并定义了一个 Scheduler::ScriptWorker :

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

  def perform(execution_id)
    puts "Sidekiq job start"
    puts execution_id
    redirect_to execute_scheduler_production_execution_path(execution_id) and return
  end

end

执行与Scheduler::ScriptWorker.perform_async(@execution.id)一起排队

Sidekiq 运行正常,但每次调用 worker 时,都会引发以下错误:

WARN: NoMethodError: undefined method `execute_scheduler_production_execution_path' for #<Scheduler::ScriptWorker:0x000000000b15ded8>

这样做是否正确,我该如何解决这个问题? 感谢您的帮助!

【问题讨论】:

  • 将执行脚本的代码移动到服务中。从控制器和工作人员调用服务。

标签: ruby-on-rails sidekiq


【解决方案1】:

正如 Sean Huber 所说,将前端作业转换为后端作业需要一些架构。通过重构,实验性的 execute 方法被移到了一个 helper 中,并重命名为 execute_ssh。控制器中的新 run_once 方法会触发助手的 execute_ssh 方法并重新加载页面。

script_worker.rb

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

  def perform(execution_id)
    puts "Sidekiq job start"
    puts execution_id
    execute_ssh execution_id
  end
end

scheduler_execution_helper.rb

  def execute_ssh(execution_id)
    puts "--- Switched to helper"
    @production_execution = ProductionExecution.find(execution_id)
    puts @production_execution.id

    @production_execution.update_attributes(started_at: Time.now, status_id: statuses.find { |x| x["code"] == "RUNNING" }.id)

--- Scripts interaction ---

  @production_execution.update_attributes(ended_at: Time.now,
                                          status_id: statuses.find { |x| x["code"] == "FINISHED" }.id,
                                          source_records_count: global_input_count,
                                          processed_count: global_output_count,
                                          error_message: nil
                                          )

  end

production_schedules_controller

  def run_once
    @job = @production_schedule.parent
    @execution = @job.production_executions.build(playground_id: @job.playground_id,
                                                  production_job_id: @job.id,
                                                  environment_id: @production_schedule.environment_id,
                                                  owner_id: current_user.id,
                                                  status_id: options_for('Statuses', 'Scheduler').find { |x| x["code"] == "READY" }.id || 0)
    if @execution.save
      @job.production_events.where(production_execution_id: nil).each do |event|
        execution_event = event.dup
        execution_event.production_execution_id = @execution.id
        execution_event.return_value = 0
        execution_event.status_id = statuses.find { |x| x["code"] == "READY" }.id
        execution_event.save
      end
      Scheduler::ScriptWorker.perform_async(@execution.id)
      redirect_to scheduler_production_job_path(@job)
    else
    end
  end

这样,控制器保持瘦身,工人可以很容易地重用,逻辑在辅助模块中。

【讨论】:

    【解决方案2】:

    简短的回答是:你不能

    您无法从异步 Sidekiq 工作器执行重定向。当用户单击“Go!”时,会向您的 Rails Web 应用程序发出 HTTP 请求。 Rails 将请求交给控制器操作,在您的情况下,这会产生一个异步 sidekiq 作业。控制器动作逻辑继续,Rails 使用 HTTP 响应完成 HTTP 请求,即使您的 sidekiq worker 仍在运行。您不能将来自 sidekiq 工作人员的消息广播回启动任务的用户

    很抱歉,这并不能解决您的问题。您需要采取不同的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-26
      • 1970-01-01
      • 1970-01-01
      • 2015-01-12
      • 2015-08-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多