Ruby on rails 在Ruby和Redis中匹配实时玩家的最佳策略?
我使用这种相当简单的方法来匹配两名现场玩家:Ruby on rails 在Ruby和Redis中匹配实时玩家的最佳策略?,ruby-on-rails,ruby,redis,actioncable,Ruby On Rails,Ruby,Redis,Actioncable,我使用这种相当简单的方法来匹配两名现场玩家: 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
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
文件是否可以用更好的方式来防止这种情况。您的问题是SPOP
和SADD
之间存在竞争条件。您应该在事务中运行这两个命令。使用Redis,您可以通过实现这一点,从而确保整个脚本在服务器端以原子方式运行
-- 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
我希望你已经监控了你的日志。除了使用事务,您还可以使用自旋锁来处理redis中的竞争条件。有关更多详细信息,请参阅本文:。但通常情况下,这是您为解决手头的问题而对代码建模的方式
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
使用如何?