Sidekiq:perform_async和依赖于顺序的操作

时间:2014-06-10 19:09:18

标签: ruby-on-rails asynchronous sidekiq

我的Rails应用程序中有一个控制器操作,通过短信和电子邮件与用户联系。由于我不打算进入的原因,在成功发送电子邮件之前需要完成短信。我原来有这样的事情:

控制器:

class MyController < ApplicationController
  def contact_user
    ContactUserWorker.perform_async(@user.id)
  end
end

工人:

class ContactUserWorker
  include Sidekiq::Worker

  def perform(user_id)
    SendUserTextWorker.perform_async(user_id)
    SendUserEmailWorker.perform_async(user_id)
  end
end

class SendUserTextWorker
  include Sidekiq::Worker

  def perform(user_id)
    user = User.find(user_id)
    user.send_text
  end
end

class SendUserEmailWorker
  include Sidekiq::Worker

  def perform(user_id)
    user = User.find(user_id)
    user.send_email
  end
end

这是不可靠的;有时电子邮件会失败,有时两者都会失败。我正在尝试确定perform_async是否是导致问题的原因。 async部分允许电子邮件在文本完成之前启动吗?我对perform_async究竟是如何工作有点模糊,但这听起来像是一个合理的猜测。

首先,我将ContactUserWorker重构为:

class ContactUserWorker
  include Sidekiq::Worker

  def perform(user_id)
    user = User.find(user_id)
    User.send_text

    SendUserEmailWorker.perform_async(user_id)
  end
end

最后,我只是将呼叫从send_text移出工作人员并进入控制器:

class MyController < ApplicationController
  def contact_user
    @user.send_text
    SendUserEmailWorker.perform_async(@user.id)
  end
end

这是真实代码的简化版本,但这是它的要点。它现在看起来工作正常,但我仍然想知道这个问题是否与Sidekiq相关或是否还有其他问题。

我很好奇,如果我使用perform而不是perform_async用于除电子邮件调用之外的所有通话,我的原始结构是否会有效。像这样:

class MyController < ApplicationController
  def contact_user
    ContactUserWorker.perform(@user.id)
  end
end

class ContactUserWorker
  include Sidekiq::Worker

  def perform(user_id)
    SendUserTextWorker.perform(user_id)
    SendUserEmailWorker.perform_async(user_id)
  end
end

2 个答案:

答案 0 :(得分:1)

我很好奇我的原始结构是否会起作用,如果我使用的是perform而不是perform_async用于除电子邮件调用之外的所有通话

它会有。但这并不是你实际上的东西。你真正想要的是:

class MyController < ApplicationController
  def contact_user
    ContactUserWorker.perform_async(@user.id)
  end
 end

class ContactUserWorker
  include Sidekiq::Worker
  attr_reader :user_id

 def perform(user_id)
  @user_id = user_id
  user.send_text
  user.send_email
 end

 def user
   @user ||= User.find user_id
 end 

问题确实是执行异步部分。它通过单独的sidekiq守护进程安排在后台执行的两个任务。我猜你的sidekiq配置为同时执行作业。在第一个版本中,您首先安排ContactUserWorker在当前rails请求之外的后台执行该作业。由于这个工作人员稍后开始工作,它依次启动两个单独的延迟工作,然后并行运行,因此无法确定两者中的哪一个先执行/完成。

我不会通过发送文字知道你的意思,但发送电子邮件是一个阻止过程,因此在后台执行此操作是个好主意,因为它会阻止完整的rails过程否则,直到电子邮件发送(在典型的独角兽/乘客多进程部署)。而且你实际上想要按顺序执行这两个任务并作为一个原子操作,它完全没问题,由一个sidekiq工作/工作人员执行它们。

您也不必检查send_text是否成功。如果Sidekiq的任何部分失败并且异常

,它将重试整个作业

答案 1 :(得分:1)

如果电子邮件只能在发送短信后发送,请在成功完成发送文本后发送电子邮件。

class ContactUserWorker
  include Sidekiq::Worker

  def perform(user_id)
    SendUserTextWorker.perform_async(user_id)
  end
end

class SendUserTextWorker
  include Sidekiq::Worker

  def perform(user_id)
    user = User.find(user_id)
    text_sent = user.send_text
    SendUserEmailWorker.perform_async(user_id) if text_sent
  end
end

class SendUserEmailWorker
  include Sidekiq::Worker

  def perform(user_id)
    user = User.find(user_id)
    user.send_email
  end
end

在user.send_text中,您需要处理文本或电子邮件均未发送的事实。