1。分散ロックの概要
分散ロックは、主にプロセス全体、ホスト全体、および分散環境のネットワーク全体で共有リソースを保護し、データの一貫性を確保するための相互に排他的なアクセスを実現するために使用されます。
2。アーキテクチャの紹介
分散ロックを実装するためにZookeeperの使用を導入する前に、まず現在のシステムアーキテクチャ図をご覧ください
説明:左側の領域全体は、Zookeeperクラスターを表しています。ロッカーはZookeeperの永続的なノードであり、Node_1、node_2、およびnode_3は、ロッカーの永続的なノードの下で一時的なシーケンシャルノードです。 client_1、client_2、client_nは複数のクライアントを意味し、サービスとは相互に排他的なアクセスを必要とする共有リソースを意味します。
分散ロックの取得のためのアイデア
1。分散ロックを取得するという全体的な考え方
分散ロックを取得するときは、ロッカーノードの下に一時シーケンシャルノードを作成し、ロックを解放するときに一時ノードを削除します。クライアントは、CreateNodeメソッドを呼び出して、ロッカーの下に一時的なシーケンシャルノードを作成し、GetChildren( "Locker")を呼び出して、すべての子ノードをロッカーの下に取得します。現時点ではウォッチャーは不要であることに注意してください。クライアントがすべての子ノードパスを取得した後、以前に作成した子ノード番号が最小であることがわかった場合、クライアントはロックを取得したと見なされます。作成したノードがロッカーのすべての子供の中で最小ではないことがわかった場合、ロックを取得していないことを意味します。この時点で、クライアントはノードをそれよりも小さいノードを見つけてから、存在()メソッドを呼び出して、イベントリスナーを登録する必要があります。その後、関係しているノードが削除された場合、クライアントのウォッチャーは対応する通知を受け取ります。この時点で、作成したノードがロッカーチャイルドノードの中で最小のシリアル番号であるかどうかを再度決定します。ルガオはロックを取得しました。そうでない場合は、上記の手順を繰り返して、あなたよりも小さいノードを取得し続け、登録して登録してください。現在のプロセスでは、まだ多くの論理的判断が必要です。
2。分散ロックを取得するためのコアアルゴリズムプロセス
以下は、次のように、分散ロックを取得するための完全なアルゴリズムを分析するための同じフローチャートです。
説明:クライアントAが分散ロックを取得したい場合は、最初にロッカーの下で一時的なシーケンシャルノード(node_n)を作成し、すぐにロッカーの下ですべての(第1レベルの)子ノードを取得します。
現時点では、複数のクライアントが同時にロックを競うため、ロッカーの下の子ノードの数は1を超えます。シーケンシャルノードの場合、特性はノード名の後に数値があることです。最初に作成されたノードの数は、後で作成したノードよりも小さくなります。したがって、子供のノードは、ノード名の接尾辞の順序で小から大きいものに並べ替えることができます。このようにして、最初のものは最初に作成されたシーケンシャルノードです。この時点で、それは最初にロックに努力するクライアントを表しています!この時点で、最小のノードがクライアントAによって作成されたnode_nであるかどうかを判断します。もしそうなら、それはクライアントAがロックを取得したことを意味します。そうでない場合、それはロックが他のクライアントによって取得されたことを意味します。したがって、クライアントAは、ロックをリリースするのを待つ必要があります。つまり、ロックを取得したクライアントBは、作成したノードを削除します。
この時点で、node_n時間よりも小さいシーケンシャルノードの削除イベントを聞くことで、クライアントBがロックをリリースしたかどうかがわかります。もしそうなら、クライアントAは再びロッカーの下のすべての子供を獲得し、それ自体で作成されたnode_nがロッカーのすべての子供の中で最小のシーケンス番号になるまで、それ自体によって作成されたnode_nノードと比較します。つまり、クライアントAがロックを取得しました!
4。Zookeeperに基づいた分散ロックのコード実装
1.分散ロックインターフェイスを定義します
定義された分散ロックインターフェイスは次のとおりです。
public Interface distributedLock { / **ロックを取得します。 / ***タイムアウトまでロックを取得* @paramタイムタイムアウト時間* @paramユニットユニットタイムパラメーターユニット* @returnロックが取得されるかどうか* @スロー例外*/ public boolean actocire(long time、timeunit unit)スロー例外。 / *** lockをリリース* @throws例外*/ public void release()throws exception;}2。単純なミューテックスを定義します
Mutex Lockクラスを定義し、上記で定義されているロックインターフェイスを実装し、ベースクラスに基づいたアストリビュードロックを継承します。このベースクラスは、主にZookeeperと対話するために使用されます。これには、ロックを取得しようとする方法やリリースロックが含まれます。
/**ロックインターフェイスの特定の実装は、主に継承された親クラスに基づいたアストリビュードロックによって達成されます。親クラスは、分散ロックを実装するためにZookeeperの特定の詳細に基づいて実装されます* /Public Class SimpledistributedLockMutexは、ZookeePerの分布ロックを実装するノードを保存するために使用されるbeased -StributedLock Implments basedistributedLock Implments extends basedistributedlock emptraments extends basedistributedlock emptraments extends basedistributedlock emptramensです。このノードの下に一時的なシーケンシャルノードを作成して、分散ロック*/プライベートファイナルストリングベースパスを実装します。 /*名前のプレフィックスをロックします。たとえば、ロッカーの下で作成されたシーケンシャルノードはロックから始まります。これにより、無関係なノード*のフィルタリング*が容易になります*作成されたノードは、lock-00000001、lock-00000002*/ private staticfinal string lock_name = "lock-"に似ています。 /*ロッカーの下でクライアントによって作成された成功したシーケンシャルノードを保存するために使用されます。その後の関連操作(判断など)*/ private文字列OurlockPath。 /***ロックリソースを取得し、親クラスのロック取得方法を介してロックを取得するために使用される* @param時間ロックのタイムアウト時間を取得* @paramユニットタイムユニット時間* @returnロックが取得されます* @throws例外*/プライベートブレアンインターナロック(長い時間、タイムナイトユニット)が例外を投げます{//詳細については、TireLockの実装を参照してください。 OulLockPath = TireLock(Time、Unit); uslockpathを返します!= null; } /*** Zookeeper Client Connection Object、およびbasepath* @param Client Zookeeper Client Connection Object* @Param BasePath Basepathは永続的なノード* /public SimpledistributedLockMutex(Zkclientextクライアント、String basepath){ /*親のクラスの構成要素を呼び出します。ノード *現在のクラス属性 */スーパー(クライアント、basepath、lock_name)へのベースパスの参照を保存します。 this.basepath = basepath; } /**タイムアウトまでロックを取得し、タイムアウト後に例外がスローされます* /public void acchire()Throws Exception {//-1は、タイムアウトが設定されていないことを意味し、タイムアウトはZookeeperによって決定されます。 }} / ***タイムアウトでロックを取得* / public boolean actocire(long time、timeunit unit)throws exception {return internallock(time、unit); } / **ロックをリリース* / public void release()throws Exception {releseLock(OurlockPath); }}3。分散ロックの実装の詳細
分散ロックを取得するための重要なロジックは、Zookeeperに基づいて分散ロックを実装する詳細を実装する、Zookeeperに基づいて分散ロックを実装することを実装するための重要なロジックです。
Public Class BasedIstributeDLock {プライベートファイナルZkClientextクライアント。プライベートファイナルストリングパス。プライベートファイナルストリングベースパス。プライベート最終文字列lockname;プライベート静的最終整数max_retry_count = 10; public BeasedistributedLock(zkclientext client、string path、string lockname){this.client = client; this.basepath = path; this.path = path.concat( "/")。concat(lockname); this.lockname = lockname; } private void deleteourpath(String OurPath)スロー例外{client.delete(ourpath); } private string createelocknode(zkclient client、string path)スロー例外{return client.createephemeral seconecential(path、null); } / ** * lockを取得するためのコア方法 * @param startmillis * @param millistowait * @param ourpath * @return * / private boolean waittolock(long startmillis、long millistowait、string ourpath)スロー例boolean dodelete = false; try {while(!havethelock){//このメソッドは、ロッカーノードの下のすべてのシーケンシャルノードの取得を実装し、小さいリスト<string> children = getSortedChildren(); string sequencenodename = ourpath.substring(basepath.length()+1); //ロッカーのすべての子ノードで、クライアントによって作成されたオーダーノードの並べ替え位置を計算します。ソートが0の場合、ロックが取得されたことを意味します。 int ourindex = children.indexof(sequencenodename); /*以前に作成した[一時的な]注文ノードがGetSortedChildrenで見つからなかった場合、これはネットワークフラッシュブレークのために作成したノードが削除される可能性があることを意味します。例外をスローする必要があります。前のレベルを処理してください *前のレベルは、例外をキャッチし、指定された数の再試行を実行することです。後続のDirectLock Method*/ if(Ourindex <0){throw new zknonodeexception( "not not not:" + sequencenodename); } //現在のクライアントによって作成されたノードがロッカーチャイルドノードリストの0より大きい場合、他のクライアントがロックを取得したことを意味します。 //他のクライアントがロックをリリースしたかどうかを判断する方法チャイルドノードリストからノード自体よりも小さいノードを取得し、文字列pathtowatch = isgetthelockのリスニングセッションをセットアップしますか? null:children.get(ourindex -1); if(isgetthelock){havethelock = true; } else {//小さいノードが削除されている場合、現在のクライアントのノードが最小である必要があることを意味するため、CountDownLatchを使用して、待機文字列の前のsequencePath = basepath .concat( "/").concat(pathtowatch)を実現します。 final countdownlatch latch = new CountDownLatch(1); final izkdatalistener forterlistener = new izkdatalistener(){//小さなノード削除イベントが発生したら、CountDownLatchを終了して待機します// public void handledatedeleted(string datapath)exception {latch.countdown(); } public void hondledatachange(string datapath、object data)スロー例外{//無視}}; {//例外を試してください。ノードがclient.subscribedatachanges(以前のsequencepath、forterlistener); if(millistowait!= null){millistowait - =(system.currenttimemillis() - startmillis); startmillis = system.currenttimemillis(); if(millistowait <= 0){dodelete = true; //タイムアウト - ノードブレークを削除します。 } latch.await(millistowait、timeunit.microseconds); } else {latch.await(); }} catch(zknonodeexception e){//無視}最後に{client.unsubscribedatachanges(以前のsequencepath、forterlistener); }}}}}} catch(例外e){//例外を削除する必要がありますdodelete = true; eを投げる; }最後に{// nodeを削除する必要がある場合if(dodelete){deleteourpath(ourpath); }} havethelockを返します。 } private string getLockNodenumber(string str、string lockname){int index = str.lastindexof(lockname); if(index> = 0){index += lockname.length(); return index <= str.length()? str.substring(index): ""; } return str; } private list <string> getSortedChildren()スロー例外{try {list <string> children = client.getChildren(basepath); collections.sort(children、new Comparator <String>(){public int Compare(String LHS、String RHS){return getLockNodenumber(LHS、locKname).compareto(getLockNodenumber(RHS、lockname));}});子供を返す; } catch(zknonodeexception e){client.createpersistent(basepath、true); return getSortedChildren(); }}保護されたvoid lireaselock(string lockpath)スロー例外{deleteourpath(lockpath); }保護されたString TirmockLock(Long Time、TimeUnit Unit)スロー例外{final long startmillis = system.currenttimemillis();最終的なlong millistowait =(unit!= null)? unit.tomillis(時間):null; String OurPath = null; boolean hasthelock = false; boolean isdone = false; int retrycount = 0; //ネットフラッシュブレークには再試行が必要です(!isdone){isdone = true; try {// CreateLockNodeを使用して、クライアントがロッカー(BasePath Persistent Node)の下のロックを取得するための[一時]注文ノードを作成します。 ourpath = createLockNode(クライアント、パス); / ***このメソッドは、ロックが取得されたかどうか、つまり、自分で作成された注文ノードがロッカーのすべての子ノードの中で最小かどうかを判断するために使用されます*ロックが取得されない場合、ロックがリリースされるのを待ち、ロックが取得またはタイミングが取得されるまでもう一度試してください*/ hasthelock = waitolock(waitmillis、millistowait、millistowait、millistowait、millistowait、millistowait、millistowait、millistowait、 } catch(zknonodeexception e){if(retrycount ++ <max_retry_count){isdone = false; } else {throw e; }}}} if(hasthelock){return ourpath; } nullを返します。 }上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。