Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 如何防止许多sidekiq作业超过API调用限制_Ruby On Rails_Redis_Synchronization_Sidekiq_Distributed System - Fatal编程技术网

Ruby on rails 如何防止许多sidekiq作业超过API调用限制

Ruby on rails 如何防止许多sidekiq作业超过API调用限制,ruby-on-rails,redis,synchronization,sidekiq,distributed-system,Ruby On Rails,Redis,Synchronization,Sidekiq,Distributed System,我正在开发一个RubyonRails应用程序。我们有很多sidekiq员工,他们可以同时处理多个工作。每个作业都将调用Shopify API,Shopify设置的调用限制为每秒调用2次。我想同步它,这样在给定的时间内只有两个作业可以调用API。 我现在的做法是: # frozen_string_literal: true class Synchronizer attr_reader :shop_id, :queue_name, :limit, :wait_time def initi

我正在开发一个RubyonRails应用程序。我们有很多sidekiq员工,他们可以同时处理多个工作。每个作业都将调用Shopify API,Shopify设置的调用限制为每秒调用2次。我想同步它,这样在给定的时间内只有两个作业可以调用API。 我现在的做法是:

# frozen_string_literal: true
class Synchronizer

  attr_reader :shop_id, :queue_name, :limit, :wait_time

  def initialize(shop_id:, queue_name:, limit: nil, wait_time: 1)
    @shop_id = shop_id
    @queue_name = queue_name.to_s
    @limit = limit
    @wait_time = wait_time
  end

  # This method should be called for each api call
  def synchronize_api_call
    raise "a block is required." unless block_given?
    get_api_call
    time_to_wait = calculate_time_to_wait
    sleep(time_to_wait) unless Rails.env.test? || time_to_wait.zero?
    yield
  ensure
    return_api_call
  end

  def set_api_calls
    redis.del(api_calls_list)
    redis.rpush(api_calls_list, calls_list)
  end

  private

  def get_api_call
    logger.log_message(synchronizer: 'Waiting for api call', color: :yellow)
    @api_call_timestamp = redis.brpop(api_calls_list)[1].to_i
    logger.log_message(synchronizer: 'Got api call.', color: :yellow)
  end

  def return_api_call
    redis_timestamp = redis.time[0]
    redis.rpush(api_calls_list, redis_timestamp)
  ensure
    redis.ltrim(api_calls_list, 0, limit - 1)
  end

  def last_call_timestamp
    @api_call_timestamp
  end

  def calculate_time_to_wait
    current_time = redis.time[0]
    time_passed = current_time - last_call_timestamp.to_i
    time_to_wait = wait_time - time_passed
    time_to_wait > 0 ? time_to_wait : 0
  end

  def reset_api_calls
    redis.multi do |r|
      r.del(api_calls_list)
    end
  end

  def calls_list
    redis_timestamp = redis.time[0]
    limit.times.map do |i|
      redis_timestamp
    end
  end

  def api_calls_list
    @api_calls_list ||= "api-calls:shop:#{shop_id}:list"
  end

  def redis
    Thread.current[:redis] ||= Redis.new(db: $redis_db_number)
  end

end
我使用它的方式是这样的

synchronizer = Synchronizer.new(shop_id: shop_id, queue_name: 'shopify_queue', limit: 2, wait_time: 1)
# this is called once the process started, i.e. it's not called by the jobs themselves but by the App from where the process is kicked off.
syncrhonizer.set_api_calls # this will populate the api_calls_list with 2 timestamps, those timestamps will be used to know when the last api call has been sent.
然后当一个工作想要打电话的时候

syncrhonizer.synchronize_api_call do
   # make the call  
end
问题 这样做的问题是,如果某个作业
由于某种原因无法返回到api\u调用列表,那么它所执行的api\u调用
,这将使该作业和其他作业永远卡住,或者直到我们注意到并且
我们再次调用set\u api\u调用
。这个问题不仅会影响到那个特定的商店,还会影响到其他商店,因为sidekiq的工作人员在使用我们的应用程序的所有商店之间共享。有时我们会注意到,直到有用户打电话给我们,我们才发现它被卡住了好几个小时,而它应该在几分钟内完成

问题
我最近才意识到Redis并不是共享锁定的最佳工具。所以我在问,
还有其他适合这项工作的好工具吗???
如果不是在Ruby世界,我也想向其他人学习。我对技术和工具都感兴趣。因此,每一点都有帮助。

您可能希望重新构造代码并创建一个微服务来处理API调用,它将使用本地锁定机制并强制工作人员等待套接字。它还增加了维护微服务的复杂性。但是,如果您赶时间,那么Ent速率限制看起来也很酷。

Sidekiq Enterprise有一个速率限制API,正是为了解决这个问题而设计的。