2013-07-25 51 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的聲明。