【问题标题】:How can I make Sidekiq respect a Retry-After response header?如何让 Sidekiq 尊重 Retry-After 响应标头?
【发布时间】:2017-09-23 17:39:41
【问题描述】:

我正在使用 Sidekiq 尝试通过 Firebase Cloud Messaging (FCM) 发送推送通知。 FCM 需要来自使用它的服务器的两件事:

  1. 指数退避
  2. 在 500 响应中尊重 Retry-After 标头

https://firebase.google.com/docs/cloud-messaging/http-server-ref#table4

我们通过 Sidekiq 免费获得指数退避,但我不确定如何告诉它至少延迟重试直到标题中指定的时间。

我在下面有一个 hacky 解决方案,我在 sleep 之前创建了工人 raise,但似乎重试逻辑应该存在于 Sidekiq 中。无论如何,我是否可以将 Retry-After 标头值告诉 Sidekiq,以便它可以做正确的事情?

class SendPushNotification
  include Sidekiq::Worker
  sidekiq_options queue: :low

  def perform(user_id)
    notification = { body: 'hello world' }
    user = User.find(user_id)
    fcm_token = user.try(:fcm_token) || ENV['FCM_TOKEN']

    uri = URI('https://fcm.googleapis.com/fcm/send')

    http = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true)

    req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json',
                                   'Authorization' => "key=#{ENV['FCM_API_KEY']}")

    req.body = {
      badge: 0,                      # badge is removed when set to zero
      click_action: 'click',         # string
      collapse_key: 'Handshake',     # collapse multiple messages under this header
      content_available: true,       # wake inactive app
      data: nil,
      registration_ids: [fcm_token], # fcm device tokens
      notification: notification,
      mutable_content: true,         # iOS 10 feature
      priority: 'high',              # high or low (corresponds to 5 and 10 in apns)
      subtitle: nil,
      time_to_live: 2419200,         # max and default for FCM
      dry_run: false                # message is not sent when set to true
    }.to_json

    res =  http.request(req)

    case res
    when Net::HTTPOK
      true
    when Net::HTTPUnauthorized
      raise 'Unauthorized'
    when Net::HTTPBadRequest
      raise "BadRequest: #{res.body}"
    else
      if res.header['Retry-After']
        # this seems like a hacky solution
        sleep res.header['Retry-After']
      end
      raise 'Remote Server Error'
    end
  end
end

【问题讨论】:

    标签: firebase-cloud-messaging sidekiq


    【解决方案1】:

    不要使用 Sidekiq 的内置重试。安排一个与当前作业相同的新作业,使其在那么多秒内运行。

     self.class.perform_in(res.header['Retry-After'].to_i, user_id)
    

    【讨论】:

    • 感谢您的回答和出色的工作平台 :) 此解决方案具有很好的副作用,即不会用 Google 的 Internet 端的错误填充 BugSnag。
    • 没错。重试是针对特殊情况和处理代码中的错误。这是一个正常的代码路径,在这个意义上并不例外。
    • 你确实有这个“循环”可能永远执行的缺点,这里的重试没有限制 afaik。我不确定如何优雅地处理这种边缘情况。
    • 非常真实的回复:无限循环。如果 Google 不发回标头,我将使用 Sidekiq 的重试策略。如果他们确实一直发送它:¯_(ツ)_/¯
    猜你喜欢
    • 2012-03-14
    • 1970-01-01
    • 2011-04-15
    • 2016-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-10
    • 2010-10-16
    相关资源
    最近更新 更多