如何让 Sidekiq 尊重 Retry-After 响应 header?

How can I make Sidekiq respect a Retry-After response header?

我正在使用 Sidekiq 尝试通过 Firebase 云消息传递 (FCM) 发送推送通知。 FCM 要求使用它的服务器提供 2 个东西:

  1. 指数退避
  2. 在 500 条回复中表彰 Retry-After header

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

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

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

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

不要使用 Sidekiq 的内置重试。安排与当前作业相同的新作业到 运行 秒数。

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