2017-06-02 12 views
1

私は2人のライブのプレイヤーと一致するように、このかなり簡単な手段を使用しています:私のゲームの開始時に 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-synchronize)の使用はどうですか? [同期メソッドのための同時実行のルビ](https://stackoverflow.com/questions/14090731/synchronized-method-for-concurrency-in-ruby) –

答えて

1

問題は、SPOPSADDの間に競合状態が存在することです。トランザクションでこれらの2つのコマンドを実行する必要があります。 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

あなたのログを監視したいと考えています。トランザクションを使用する以外にも、スピンロックを使用してレディ状態の競合状態を処理することもできます。詳細は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 
関連する問題