web-dev-qa-db-ja.com

分散ロックパターンを探す

C#の分散システム用のカスタム再帰オブジェクトロックメカニズム\パターンを考え出す必要があります。基本的に、私はマルチノードシステムを使用しています。各ノードには排他的書き込みn-number個の状態に対する権限があります。同じ状態が読み取り専用フォームでも利用できます少なくとも1つの他のノード。一部の書き込み/更新はすべてのノードでアトミックである必要がありますが、他の更新はバックグラウンドのレプリケーションプロセス、キューなどを通じて最終的に整合性が取れます...

アトミック更新では、オブジェクトをロック済みとして効率的にマークできるパターンまたはサンプルを探していますwrites配布、コミット、ロールバックなどができます...システムが高レベルの同時実行性、つまり、ロックがスタックされると、タイムアウトになるか、ロックが解放されるとアンロールされるようになる必要があると想定しています。

トランザクションまたはメッセージングの部分はこの質問の焦点では​​ありませんが、いくつかの追加のコンテキストのためにそれらを提供しました。そうは言っても、必要に応じてどのようなメッセージが必要だと思うかを自由に説明してください。

これは、私が想像していたものの漠然としたサンプルですが、まったく新しい製品を実装することを除いて、新しいアイデアを受け入れることができます。

thing.AquireLock(LockLevel.Write);

//Do work

thing.ReleaseLock();

次のような拡張メソッドの使用を考えていました

public static void AquireLock(this IThing instance, TupleLockLevel lockLevel)
{ 
    //TODO: Add aquisition wait, retry, recursion count, timeout support, etc...  
    //TODO: Disallow read lock requests if the 'thing' is already write locked
    //TODO: Throw exception when aquisition fails
    instance.Lock = lockLevel;
}

public static void ReleaseLock(this IThing instance)
{
    instance.Lock = TupleLockLevel.None;
}

いくつかの詳細を明確にするために...

  • すべての通信は、バイナリ要求/応答プロトコルを使用するTCP/IPです。
  • キューやデータベースなどの中間技術はありません
  • 中央マスターノードはありません。この場合、ロックの配置は、ロックのイニシエーターと、その動作を管理する何らかの形式のタイムアウトで要求を受け入れるパートナーによって定義されます。

誰か提案がありますか?

10
JoeGeeky

説明をありがとう。

その場合は、パブリッシュ/サブスクライブモデルを使用することをお勧めします。 Googleの Chubby分散ロックプロトコル (- Paxos の実装)

私はPaxos(またはChubby)を使用したことがありませんが、オープンソースの実装があるようです here

それが機能しない場合は、たとえば、メッセージングライブラリに関する通常の容疑者の1つを使用して、Paxosの独自のバージョンを実装できます。 zero message queue libraryRabbitMQ 、または ActiveMQ


以前の回答:

SO( [A][B] )に関するほとんどの提案は、マシン間でメッセージキューを使用するためのものですロック。

AcquireLockメソッドは、ロックオブジェクトを識別するものをキューにプッシュし、成功する前にロックの以前のインスタンスをチェックします。 ReleaseLockメソッドは、ロックオブジェクトをキューから削除します。

SOユーザー atlantis が示唆、 この投稿の場合Jeff Keyの投稿 の詳細の一部。

4
Peter K.

あなたはここにいくつかの混合技術を持っているように私には思えます:

  • 通信(100%信頼できるものとして本質的に依存している...これは致命的となる可能性があります)

  • ロック/相互排除

  • タイムアウト(目的は何ですか)?

警告の言葉:分散システムでのタイムアウトは危険と困難を伴います。タイムアウトを無差別に使用しても問題は解決されないため、使用する場合は非常に慎重に設定して使用する必要があります。 (タイムアウトの使用方法を確認したい場合する必要があります、HDLC通信プロトコルのドキュメントを読んで理解してください。これは、適切な賢い使用法の良い例であり、賢いビットコーディングシステムと組み合わせて、 IDLEラインなどの検出)。

しばらくの間、通信リンク(TCPではなく、他のもの)を使用して接続されたマルチプロセッサ分散システムで作業しました。私が学んだことの1つは、大まかな一般化として、いくつかの危険なマルチプログラミングの場所があるということです。

  • キューへの依存は通常、涙で終わります(キューがいっぱいになると、問題が発生します。決していっぱいにならないキューサイズを計算できる場合は、キューなしのソリューションを使用できます)。

  • ロックへの依存は苦痛です。別の方法があるかどうかを試してみてください(ロックを使用する必要がある場合は、文献を参照してください。マルチプロセッサ分散ロックは、過去20〜30年間の多くの学術論文の主題でした)。

ロックを使用して続行する必要があります:

最後の手段を回復する手段としてのみ、つまり、基礎となる通信システムの障害を検出するためにのみ、タイムアウトを使用すると仮定します。さらに、TCP/IP通信システムが高帯域幅であり、低レイテンシ(理想的にはゼロですが、これが発生することはない)と考えることができると想定します。

私が提案することは、すべてのノードが接続できる他のノードの接続リストを持っているということです。 (ノードは接続がどこから来るかは気にしません。)ノードが接続できるノードのテーブルの母集団は、整理するために別のものとして残されます。また、接続がノードに到達するIPポート番号の割り当てなども無視されます-単一のポートまたは複数のポートで要求を受け入れるのには十分な理由があります。これは慎重に検討する必要があります。要素には、暗黙的なキューイング、順序付け、リソースの使用、オペレーティングシステムのタイプと機能が含まれます。

ノードは、接続先を知ったら、そのノードにロック要求を送信でき、そのリモートノードからのロック応答から受信する必要があります。これら2つの操作をラッパーにパックして、アトミックに見えるようにすることができます。これの効果は、ロックを取得したいノードが次のような呼び出しを行うことです:

if (get_lock(remote_node) == timeout) then
  {
    take some failure action - the comms network is down
  }

/* Lock is now acquired - do work here */

if (release_lock(remote_node) == timeout) then
  {
    take some failure action - the comms network is down
  }

get_lockおよびrelease_lockの呼び出しは、(原則として)次のようになります。

send_to_remote_node(lock_request)
get_from_remote_node_or_timeout(lock_reply, time)
if (result was timeout) then
  return timeout
else
  return ok

分散ロックシステムでは、多くのリモートノードがロックを取得するために待機している可能性があるため、ロックが保持されている間に実行される作業単位が小さくて高速になるように、十分に注意する必要があります。これは事実上、停止して待機するマルチプロセッサ/通信システムであり、堅牢ですが、最高のパフォーマンスは得られません。

まったく異なるアプローチを取ることをお勧めします。リモートプロシージャコールを使用できますが、各RPCコールは、受信者が処理でき、ロックの必要性を排除する情報のパッケージを運びますか?


質問を再読すると、物事の通信面に本当に関心があるのではなく、ロックの問題を解決したいだけのようです。

したがって、私の回答は少し話題から外れているように見えるかもしれませんが、その下にある部品も正しくないと、ロックの問題を解決できないと思います。類推:悪い土台の上に家を建てると、家が倒れる...最終的には。

4
quickly_now

NCacheのような分散キャッシュを使用して、質問を簡単に実装できます。必要なのは、オブジェクトを使用してロックを取得できる悲観的ロックメカニズムです。次に、タスクと操作を実行し、他のアプリケーションが後で使用できるようにロックを解放します。

次のコードを見てください。

ここで、特定のキーのロックを取得し、タスク(1つ以上の操作からの範囲)を実行し、最後にロックを解放します。

// Instance of the object used to lock and unlock cache items in NCache
LockHandle lockHandle = new LockHandle();

// Specify time span of 10 sec for which the item remains locked
// NCache will auto release the lock after 10 seconds.
TimeSpan lockSpan = new TimeSpan(0, 0, 10); 

try
{
    // If item fetch is successful, lockHandle object will be populated
    // The lockHandle object will be used to unlock the cache item
    // acquireLock should be true if you want to acquire to the lock.
    // If item does not exists, account will be null
    BankAccount account = cache.Get(key, lockSpan, 
    ref lockHandle, acquireLock) as BankAccount;
    // Lock acquired otherwise it will throw LockingException exception

    if(account != null && account.IsActive)
    {
        // Withdraw money or Deposit
        account.Balance += withdrawAmount;
        // account.Balance -= depositAmount;

        // Insert the data in the cache and release the lock simultaneously 
        // LockHandle initially used to lock the item must be provided
        // releaseLock should be true to release the lock, otherwise false
        cache.Insert("Key", account, lockHandle, releaseLock); 
        //For your case you should use cache.Unlock("Key", lockHandle);
    }
    else
    {
        // Either does not exist or unable to cast
        // Explicitly release the lock in case of errors
        cache.Unlock("Key", lockHandle);
    } 
}
catch(LockingException lockException)
{
    // Lock couldn't be acquired
    // Wait and try again
}

リンクから取得: http://blogs.alachisoft.com/ncache/distributed-locking/

0
Basit Anwer