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


10

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

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

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

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

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です。
  • キューやデータベースなどの中間技術はありません
  • 中央マスターノードはありません。この場合、ロックの配置は、ロックのイニシエーターと、その動作を管理するために何らかの形式のタイムアウトで要求を受け入れるパートナーによって定義されます。

誰か提案はありますか?


ロックは通常、ほとんどのシステムの標準機能です。C#にもあると思います。(Google検索結果:albahari.com/threading/part2.aspx)基本的なミューテックスやセマフォを超えて何かを達成しようとしていますか?
Dipan Mehta

2
@DipanMehta申し訳ありませんが、私はこれをより明確に取り上げるべきでした。ノード私が言及したが、ネットワーク上のマシンです。Mutexとセマフォについての私の理解は、それらはマシン全体のロック(たとえば、クロスプロセス)であり、ネットワーク上のマシン間で拡張できるロックではないということです。
JoeGeeky

@JoeGeekyあなたの質問はここで話題になっているので、Stack Overflowにとって理論的すぎる可能性があります。そこでもう一度質問したい場合はできますが、よりコードに焦点を当てた表現が必要です。
アダムリア

回答:


4

説明をありがとう。

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

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

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


以前の回答:

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

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

SOユーザーatlantisは、この投稿で、いくつかの詳細についてJeff Keyの投稿を提案しています。


おかげで、私は中央のマスター、データベース、またはキューを持っていないので、これらのソリューションは適切ではありません。これらの詳細のいくつかを明確にするために、いくつかの追加の詳細で質問を更新しました。
JoeGeeky

ノード間のすべての通信に使用する必要がある明確に定義されたプロトコルがすでにあるため、これらの製品を直接使用することはできませんが、ChubbyとPaxosには、明確に定義されたパターンがある可能性があります。見てみます。
JoeGeeky

@JoeGeekyはい、Paxosリンクにはシーケンス図があり、好みの通信リンクを使用してそれを実装できる場合があります。
Peter K.

直接的な回答ではありませんが、ChubbyとPaxosのすべてを読むことで、自分のソリューションを定義することができました。これらのツールは使用しませんでしたが、いくつかの概念に基づいて合理的なパターンを定義することができました。ありがとう。
JoeGeeky

@JoeGeeky:少なくとも、助けになったと聞いてよかった。ダニをありがとう。
Peter K.

4

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

  • 通信(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呼び出しが受信者が処理できる情報のパッケージを保持し、ロックの必要性を取り除くことができますか?


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

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


1
タイムアウトセマンティクスは、ネットワークから消えたノードを処理するため、またはロックスタックの大きなバックログを処理するために主に存在します...これにより、ロックの取得を待機している間にブロックされる時間を制限し、ロックを要求するユーザーに機会を提供します予期しない遅延、障害などの最中に他のプロセスを開始する...さらに、これは、何かが失敗した場合に何かが永久にロックされることを防ぎます。この時点で、私は最終的に何かが失敗することを指定した任意の選択肢が表示されていないが、私はあなたの懸念に感謝
JoeGeeky

他のコメントの一部を述べるために、FIFOパターンを使用してロックがスタックおよび解放されることを期待していますが、キュー自体は(非同期通信の意味で)使用していません。これが必要な要求/応答パターンの点でどのように機能するかは、何らかの方法でブロックしてより大きなハンドシェイクの一部になる必要があることを除いて、私は十分に調整していません。現時点では、1つのノード内でスタックロックメカニズムを使用して作業しており、次に分散シナリオでどのように機能するかを考えています。あなたが示唆したように私はもう少し読んでいきます。ありがとう
JoeGeeky

@JoeGeeky-FIFOはキューです。キューに注意してください。その側を非常に注意深く検討してください。何かをすぐに使えるようにするのではなく、問題と解決策を注意深く検討する必要があるように思えます。
quick_now

わかりました...非同期プロセスで使用されるFIFOキュー(たとえば、1つのプロセスがエンキューし、次に別のデキューする)の違いを明確にしようとしていました。この場合、物事を順番に管理する必要がありますが、キューに入るプロセスは、(a)ロックを取得する、(b)ロックが拒否される、または(c)タイムアウトしてラインを離れるまで終了しません。ATMに並んでいるような感じです。これは、成功した場合のFIFOパターンのように動作しますが、プロセスがラインの先頭に到達する前に順不同のままになる場合があります。既製は?いいえ、ただしこれは新しい問題ではありません
JoeGeeky

0

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/

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.