2013-07-25 50 views
3

Redis的基于互斥我想实现一个基于内存的多进程共享互斥体,它支持超时,使用Redis的。红宝石 - 与到期实现

我需要该互斥锁是非阻塞的,这意味着我只需要能够知道我是否能够获取互斥锁,如果没有,只需继续执行后备代码即可。

东西沿着这些路线:

if lock('my_lock_key', timeout: 1.minute) 
    # Do some job 
else 
    # exit 
end 

未到期互斥可以采用以下方式实现的Redis的setnx mutex 1

if redis.setnx('#{mutex}', '1') 
    # Do some job 
    redis.delete('#{mutex}') 
else 
    # exit 
end 

但是如果我需要一个超时机制互斥(在为了避免在命令redis.delete之前ruby代码失败的情况,导致该互斥锁永远被锁定,例如,但不仅仅是这个原因)。

做这样的事情显然是行不通的:

redis.multi do 
    redis.setnx('#{mutex}', '1') 
    redis.expire('#{mutex}', key_timeout) 
end 

因为我的到期重新设置为互斥即使我无法设置互斥(setnx返回0)。

当然,我会预料会有类似于setnxex的东西,它会以到期时间自动设置密钥的值,但前提是密钥不存在。不幸的是,据我所知,Redis并不支持这一点。

我也不过,找到renamenx key otherkey,它可以让你重新命名一个关键的一些其他关键,只有在其他关键已不存在。

我想出了这样的事情(用于演示目的,我把它写下来单片,并没有打破它的方法):

result = redis.multi do 
    dummy_key = "mutex:dummy:#{Time.now.to_f}#{key}" 
    redis.setex dummy_key, key_timeout, 0 
    redis.renamenx dummy_key, key 
end 
if result.length > 1 && result.second == 1 
    # do some job 
    redis.delete key 
else 
    # exit 
end 

在这里,我设置了过期假钥匙,并尝试将其重命名为真钥匙(在一次交易中)。

如果renamenx操作失败,那么我们无法获取互斥锁,但是没有造成任何损害:虚拟关键字将到期(可以通过添加一行代码立即删除),并且真正的密钥到期时间将保持不变。

如果renamenx操作成功,那么我们就能够获得互斥体,和互斥将获得所需的到期时间。

任何人都可以看到与上述解决方案的任何缺陷?这个问题是否有更加标准的解决方案?我真的很讨厌使用一个外部的宝石,以解决这个问题...

回答

3

如果你使用Redis的2.6+,你可以用Lua脚本引擎简单地做这更多。该Redis documentation说:

一个Redis的脚本是定义事务性的,所以一切都可以用Redis的交易做的,你也可以做一个脚本,通常脚本会更简单,更快速。

实现实在是小巫见大巫:

LUA_ACQUIRE = "return redis.call('setnx', KEYS[1], 1) == 1 and redis.call('expire', KEYS[1], KEYS[2]) and 1 or 0" 
def lock(key, timeout = 3600) 
    if redis.eval(LUA_ACQUIRE, key, timeout) == 1 
    begin 
     yield 
    ensure 
     r.del key 
    end 
    end 
end 

用法:

lock("somejob") { do_exclusive_job } 
0

redis 2.6.12开始,你可以这样做:redis.set(key, 1, nx: true, ex: 3600)这实际上是SET key 1 NX EX 3600

我被简单的启发,无论克里斯的与米奇的解决方案,创造宝石 - simple_redis_lock与此代码(和某些功能和RSpec):

def lock(key, timeout) 
    if @redis.set(key, Time.now, nx: true, px: timeout) 
    begin 
     yield 
    ensure 
     release key 
    end 
    end 
end 

我探讨一些其他的真棒选择:

  1. mlanett/redis-lock
  2. PatrickTulskie/redis-lock
  3. leandromoreira/redlock-rb
  4. dv/redis-semaphore

但他们阻止收购锁定太多的功能,并没有使用这种单原子SET KEY 1 NX EX 3600 Redis的声明。