如何将Resque作业锁定到一台服务器

时间:2012-10-24 23:44:26

标签: ruby-on-rails ruby redis resque

我的基础架构中有一个Resque服务器的“集群”。它们都具有相同的确切工作优先级等。我根据有多少待处理作业以及服务器上用于处理所述作业的可用资源,自动扩展Resque服务器的数量。我总是至少有两个Resque服务器。

我的问题是,当我快速完成一项工作时,有时两台服务器都会处理这项工作。这很糟糕。

我尝试使用以下内容为我的工作添加一个锁:

require 'resque-lock-timeout'

class ExampleJob
  extend Resque::Plugins::LockTimeout

  def self.perform
   # some code
  end
end

此插件适用于较长时间运行的作业。然而,对于这些超级微小的一次性工作,处理立即发生。 Resque服务器都看不到其姊妹服务器设置的锁,无论是设置锁定,还是处理作业,解锁,都已完成。

我不完全确定此时该做什么或者除了让一个专用服务器处理这种类型的工作之外还有什么解决方案。这将是一个严重的配置和扩展的痛苦。我真的希望两个服务器都能够处理它,但是一旦其中一个服务器从队列中抓取它,确保另一个服务器不运行它。

有人能提出一些可行的解决方案吗?

2 个答案:

答案 0 :(得分:2)

将你的锁解释器写入等待T毫秒,然后才能查找一个唯一的小于其锁定值的锁

这将决定谁赢了比赛,输家将自行终止。

T是给定队列池中所有N服务器之间的并行延迟。您可以通过从1000毫秒缩小来确定此heuristically,直到再次发现作业重复发生。为延迟变化提供填充。

这称为Busy-Wait解决方案,用于互斥线程安全。考虑到必须解决Mutex的各种场景(例如锁定等),它被认为是可接受的权衡之一

移动时我会发布一些链接。关于互斥体的维基百科条目应该解释所有这些。

这对你不起作用,那么: 1.使用调度程序控制复制。 2.将短时间运行的作业分类到设计为串行运行的队列。

TL; DR没有完美的解决方案,只能根据您的条件进行良好的权衡。

答案 1 :(得分:1)

两名工作人员不应该获得相同的“有效负载”,因为项目是使用BLPOP出列的。 Redis只会将排队的项目发送到第一个调用BLPOP的客户端。听起来你不止一次将这项工作入队,因此两名工人能够使用相同的参数获得不同的有效载荷。 'resque-lock-timeout'的目的是确保具有相同方法和参数的有效负载不会同时运行;但是,如果第一个作业在第二个作业尝试获取之前释放锁定,则它不会停止第二个有效负载的工作。

这只会发生在短期工作中。以下是可能发生的事情:

payload 1 is enqueued
payload 2 is enqueued
payload 1 is locked 
payload 1 is worked
payload 1 is unlocked
payload 2 is locked
payload 2 is worked
payload 2 is unlocked

在长期工作的情况下,可能会发生以下情况:

payload 1 is enqueued
payload 2 is enqueued
payload 1 is locked
payload 1 is worked 
payload 2 is fails to get lock
payload 1 is unlocked

尝试关闭Resque并将您的作业排入队列。在列表中查看redis队列中的redis(或使用redis-cli monitor监视Redis)。查看Resque是否排队了多个有效负载。如果您仍然只看到一个有效负载,则监视列表以查看另一个resque worker是否在失败的作业上调用recreate

如果您想要'resque-lock-timeout'保持锁定的时间超过处理作业所需的时间,您可以覆盖release_lock!方法来设置锁定的到期时间,而不是仅仅删除它

module Resque
  module Plugins
    module LockTimeout  
      def release_lock!(*args)
        lock_redis.expire(redis_lock_key(*args), 60) # expire lock after 60 seconds
      end
    end
  end
end

https://github.com/lantins/resque-lock-timeout/blob/master/lib/resque/plugins/lock_timeout.rb#l153-155