redisを使用して簡単な分散ロックを書く


1つのプロセスにとって、内部のリソースロックには多くの実装方式があり、効率的であり、同じマシンの異なるプロセスに対して同期を行うと、多くの方法で実装され、異なるマシンに配備されたプログラムにとって、同期を行うのは面倒である.
最も簡単な方法はredisを使用することである可能性があり、redisの実装はすべての操作が単一スレッドに組み込まれて処理されるため、同時要求に対しては前後順があり、分散ロックの実装には優れた優位性がある.
実は分散ロックとは、ある共有ノードにリソースのタグを付け、タグが存在しない場合、他のリクエストがこのリソースを操作していないことを示し、そのリソースを取得して操作することができ、残りのリクエストがこのタグが存在することを発見した場合、前のリクエストがこのタグを明らかにするまで待つ.プロセス全体が通じました
redisのSETコマンドにはNXサポートがあり、存在しないときにこの操作が成功するので、この点を利用して上記の考えを実現することができます.
ロックを取得しようとするリクエストがある場合、
//  to generate the unique key
u, err := uuid.NewUUID()
if nil != err {
    return err
}
s.lockValue = u.String()
s.lockKey = keyName

//  try to lock
rpl, err := redis.String(conn.Do("SET", keyName, s.lockValue, "NX", "PX", timeout))
if nil != err {
    return err
}
if rpl != "OK" {
    return ErrSingleLockOperationFailed
}

return nil

各lockerにuuidを割り当て,SET NXコマンドを使用してロックを取得し,成功するとロックが取得される.
ロック解除も簡単です
//  try to unlock
//  avoid to unlock a lock not belongs to the locker
lockValue, err := redis.String(conn.Do("GET", s.lockKey))
if nil != err {
    return err
}
if lockValue != s.lockValue {
    return ErrSingleLockInvalidLockValue
}

rpl, err := redis.Int(conn.Do("DEL", s.lockKey))
if nil != err {
    return err
}

if rpl != 1 {
    return ErrSingleLockLockIsUnlocked
}

return nil

これらが大まかなコード実装である.
しかし、実現は簡単ですが、実際には問題を避けるために小さな仕事をしています.
あるlockerがロックを取得したが、timeoutの時間内にDELがロックされていない場合、このロックは期限切れになって自動的に解放され、つまり他のリクエストがこのロックを取得できるという問題が発生し、2番目に取得したロックがまだ解放されていないと仮定し、1番目のリクエストがtimeoutのイベント後にロックを解放すると、3番目のリクエストは新しいロックを取得することができる.これは大きな問題で、このように3つの接続がロックされているのは大きな問題です.
そこで、上記のコードでは、各lockerにuuidがあり、uuidが等しい場合にロックを削除し、ロックが別のlockerによって解放される問題を回避する.
github