【问题标题】:How to notify a user after background task is finished?后台任务完成后如何通知用户?
【发布时间】:2017-03-22 02:41:19
【问题描述】:

我使用带有ActiveJobsidekiq 的rails 作为后端。当用户来到一个页面sidekiq 创建一个长期后台任务时,我怎样才能注意到用户(通过在网页上呈现部分)何时完成任务?

Rails 和 sidekiq 作为不同的进程工作。这个事实让我很困惑,我不明白如何使用后台作业处理完成状态。

【问题讨论】:

    标签: ruby-on-rails background-process sidekiq


    【解决方案1】:

    ActiveJob 提供了一个 after_perform 回调,根据文档,它的工作方式如下:

    class VideoProcessJob < ActiveJob::Base
      queue_as :default
    
      after_perform do |job|
        UserMailer.notify_video_processed(job.arguments.first)
      end
    
      def perform(video_id)
        Video.find(video_id).process
      end
    end
    

    因此,您不必担心直接与Sidekiq 或任何其他排队后端集成,请与ActiveJob 交谈:)

    【讨论】:

    • 谢谢,但我想通过在网页上渲染部分来通知用户
    • @mystdeim 这里的问题是哪个网页!用户可以去找任何人吗?任何可能的渲染/重定向都不是一个好主意。而是使用由ActionCable 订阅触发的一些通知模式。将尝试发布一些代码,但这应该不难。祝你好运
    【解决方案2】:

    我在这种情况下的做法是:

    1. 添加sidekiq-status,以便可以通过 ID 跟踪后台作业。
    2. 在创建后台作业的客户端调用中,返回新创建的作业的 ID。

      class MyController < ApplicationController
      
        def create
          # sidekiq-status lets us retrieve a unique job ID when
          # creating a job
          job_id = Workers::MyJob.perform_async(...)
      
          # tell the client where to find the progress of this job
          return :json => {
            :next => "/my/progress?job_id={job_id}"
          }
        end
      
      end
      
    3. 使用该作业 ID 在服务器上轮询“进度”端点。此端点获取作业的作业进度信息并将其返回给客户端。

      class MyController < ApplicationController
      
        def progress
          # fetch job status from sidekiq-status
          status = Sidekiq::Status::get_all(params[:job_id])
      
          # in practice, status can be nil if the info has expired from
          # Redis; I'm ignoring that for the purpose of this example
      
          if status["complete"]
            # job is complete; notify the client in some way
            # perhaps by sending it a rendered partial
            payload = {
              :html => render_to_string({
                :partial => "my/job_finished",
                :layout => nil
              })
            }
          else
            # tell client to check back again later
            payload = {:next => "/my/progress?job_id={params[:job_id]}"}
          end
      
          render :json => payload
        end
      
      end
      
    4. 如果客户端看到作业已经完成,它可以显示一条消息或采取任何下一步所需的步骤。

      var getProgress = function(progress_url, poll_interval) {
        $.get(progress_url).done(function(progress) {
          if(progress.html) {
            // job is complete; show HTML returned by server
            $('#my-container').html(progress.html);
          } else {
            // job is not yet complete, try again later at the URL
            // provided by the server
            setTimeout(function() {
              getProgress(progress.next, poll_interval);
            }, poll_interval);
          }
        });
      };
      $("#my-button").on('click', function(e) {
        $.post("/my").done(function(data) {
          getProgress(data.next, 5000);
        });
        e.preventDefault();
      });
      

    注意事项:该代码仅用于说明,缺少您应该注意的事项,例如错误处理、防止重复提交等。

    【讨论】:

    • 您使用过时的池化方式执行此操作,并且更喜欢使用 websocket 或 SSE 进行推送通知
    • @mystdeim 你能分享一个使用 websockets/SSE 更优雅地做到这一点的例子吗? (我正在尝试找出实现这一目标的最佳方法,但不幸的是在网上找不到很多示例)
    猜你喜欢
    • 1970-01-01
    • 2019-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-15
    • 1970-01-01
    相关资源
    最近更新 更多