2017-06-02 24 views
1

我使用這個非常簡單的手段來匹配兩個在場玩家:在Ruby和Redis中匹配現場玩家的最佳策略是什麼?

class Seek 
    def self.create(uuid) 
    if opponent = REDIS.spop("seeks") 
     Game.start(uuid, opponent) 
    else 
     REDIS.sadd("seeks", uuid) 
    end 
    end 

    def self.remove(uuid) 
    REDIS.srem("seeks", uuid) 
    end 
end 

然後我的遊戲開始的時候,我根本就Seek.create(uuid)

我得到的地方很少有小問題,有時兩個人在同一時間尋找尋找。我猜Redis.spop("seeks")對於兩名球員都會返回nil,然後轉換將它們加到REDIS.sadd("seeks", uuid)。然後他們都無限期地等待(除非有其他玩家出現)。

我的情況看起來像一個相當罕見的情況,但我很好奇,如果我的seek.rb文件可以用更好的方式寫入,以防止這種情況。

+0

如何使用[Mutex#synchronize](https://ruby-doc.org/core-2.2.0/Mutex.html#method-i-synchronize)? [同步​​法換併發中旁註](https://stackoverflow.com/questions/14090731/synchronized-method-for-concurrency-in-ruby) –

回答

1

您的問題是SPOPSADD之間存在競態條件。您應該在事務中運行這兩個命令。藉助Redis,您可以通過Lua scripting實現該目標,從而確保整個腳本在服務器端以原子方式運行。

-- transaction.lua 
redis.replicate_commands() -- see https://redis.io/commands/eval#replicating-commands-instead-of-scripts for details 

local uuid = ARGV[1] -- pass uuid by script's arguments 
local member = redis.call('SPOP', 'seeks') 
if (member) then 
    return member -- get an exist uuid 
else 
    redis.call('SADD', 'seeks', uuid) -- add it to the set 
end -- the whole script runs in a transaction 
1

我希望你已經監視你的日誌。除了使用交易,您還可以使用旋轉鎖來處理redis中的競爭條件。你可以參考這篇文章進一步的細節:http://hoyvinglavin.com/2012/04/29/redis-as-a-mutex-service/。但通常情況下,這就是你如何模擬你的代碼來解決手頭的問題。

class Seek 

    def self.create(uuid) 

    if opponent = REDIS.spop("seeks") 
     Game.start(uuid, opponent) 
    else 
     #Check if key(lock) exists in redis. If not then proceed ahead 
     #Set key(acquire lock). Raise exception in case redis set key does not return true 
     REDIS.sadd("seeks", uuid) #Perform your operation 
     #Delete key(release lock) 
    end 
    end 

    def self.remove(uuid) 
    REDIS.srem("seeks", uuid) 
    end 
end 
相關問題