2017-06-02 55 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 
相关问题