代码之家  ›  专栏  ›  技术社区  ›  Hendrik

同步不同Sidekiq线程的方法并等待

  •  0
  • Hendrik  · 技术社区  · 11 年前

    问题: 我有几个sidekiq线程和一个只能从任何线程调用一次的函数。

    原因: 我们正在查询AdWords API以获取一些数据。当涉及到利率限制时,它们是非常严格的。一次只能有一个线程调用该函数来获取数据。

    现在一些代码:

    # Public: Get estimates for a set of keywords. If there is an error, retry
    # several times. If not successful, raise an error
    #
    # keywords: The keyword objects to get estimates for.
    # save: Boolean to indicate whether the keyword objects should be saved to
    # the database
    #
    def repeatedly_try_get_estimates(keywords: [], save: true, sleep_delay: 150)
      return keywords if keywords.empty?
      func = -> { get_estimates(keywords, !save) }
      retry_operation(function: func, max_tries: 15, sleep_delay: sleep_delay)
    end
    
    • 正如你所看到的,现在我有一个巨大的 sleep_delay 解决问题。
    • 代码调用 retry_operation 函数 get_estimates 函数作为参数。然后将重试 获取估计值(_E) 函数多次,直到出现API 例外

    这个 retry_function :

    # Private: Retry a function X times and wait X seconds. If it does not work X times,
    # raise an error. If successful return the functions results.
    #
    # - max_tries: The maximum tries to repeat the function
    # - sleep_delay: The seconds to wait between each iteration.
    # - function: The lambda function to call each iteration
    #
    def retry_operation(max_tries: 5, sleep_delay: 30, function: nil, current_try: 0, result: nil)
    
      # Can't call, no function
      if function.nil?
        return
      end
    
      # Abort, tried too frequently.
      if current_try > max_tries
        raise "Failed function too often"
      end
    
      # Check if there is an exception
      exception = true
      begin
        result = function.call
        exception = false
      rescue => e
        Rails.logger.info "Received error when repeatedly calling function #{e.message.to_s}"
      end
    
      if exception
        sleep sleep_delay if sleep_delay > 0
        retry_operation(max_tries: max_tries, sleep_delay: sleep_delay, function: function, current_try: current_try + 1)
      else
        result
      end
    end
    

    这个 get_estimates_function 在这里: https://gist.github.com/a14868d939ef0e34ef9f 。太长了,以防万一。

    我想我需要做到以下几点:

    1. 调整中的代码 repeatedly_try_get_estimates 作用
    2. 在类中使用互斥锁。
    3. 如果互斥体正在使用,则挽救异常。
    4. 仅当互斥锁空闲时,运行 rety_operation ,否则睡一会儿

    感谢您的帮助:)

    1 回复  |  直到 11 年前
        1
  •  0
  •   Hendrik    11 年前

    我们来了,开始工作了:

    # Public: Get estimates for a set of keywords. If there is an error, retry
    # several times. If not successful, raise an error
    #
    # keywords: The keyword objects to get estimates for.
    # save: Boolean to indicate whether the keyword objects should be saved to
    # the database
    #
    def repeatedly_try_get_estimates(keywords: [], save: true, sleep_delay: 40)
      return keywords if keywords.empty?
      func = -> { get_estimates(keywords, save_keywords: true) }
      exception = nil
      result = nil
      initial_sleep = 0
    
      estimates_mutex.synchronize do
        since_last_request = Time.now.to_i - last_adwords_api_request
        if since_last_request <= 30
          Rails.logger.info "AdWords: Last request was only few seconds ago - sleeping #{since_last_request}."
          initial_sleep = since_last_request
        end
        begin
          result = retry_operation(function: func, max_tries: 15, sleep_delay: sleep_delay, initial_sleep: initial_sleep)
        rescue => e
          exception = e
        end
        @@last_adwords_api_request = Time.now.to_i
      end
      if exception
        raise exception
      end
      result
    end