ZKに基づく分散ロックを実現

前書き

ZooKeeperは、分散型のオープンソース分散型アプリケーション調整サービスであり、GoogleのChubbyのオープンソース実装であり、HadoopとHbaseの重要なコンポーネントです。分散アプリケーションに一貫したサービスを提供するソフトウェアです。提供される機能には、構成の保守、ドメイン名サービス、分散同期、グループサービスなどがあります。

ZooKeeperのアーキテクチャは、冗長サービスを通じて高可用性を実現します。したがって、最初に応答がない場合、クライアントは別のZooKeeperホストに問い合わせることができます。ZooKeeperノードは、ファイルシステムやプレフィックスツリー構造と非常によく似た階層的な名前空間にデータを格納します。クライアントはノード上で読み取りと書き込みを行うことができるため、このように共有構成サービスを利用できます。更新は全順序で行われます。

ZooKeeper分散ロックの原則に基づく

  1. 飼育係node_nノードを指定されたノードの一時的な順序を作成する(ロック)

  2. すべての子供をロック下に置く

  3. ノードの自己増加シーケンス番号に従って、子ノードを小さいものから大きいものへとソートします。

  4. ノードが最初の子ノードであるかどうかを判別し、そうである場合はロックを取得します。そうでない場合は、ノードよりも小さいノードの削除イベントをリッスンします。

  5. 監視イベントが有効になった場合は、2番目のステップに戻って、ロックが取得されるまで再判断します。

実装

以下では、特にjavaとzookeeperを使用して分散ロックを実装し、zookeeper操作はapacheが提供するzookeeperパッケージを使用します。

  • Watchインターフェースを実装し、監視を実装するプロセス(WatchedEventイベント)メソッドを実装することにより、CountDownLatchに監視を完了させ、CountDownLatchを使用してロックを待機するときにカウントし、countDownが実行されるまで待機し、待機を停止して、実行を継続します。

  • 次の全体的なプロセスは、基本的に上記のプロセスと同じですが、監視時にCountDownLatchを使用して前のノードを監視する点が異なります。

パッケージorg.java.base.zookeeper; 
インポートjava.io.IOException; 
インポートjava.util.ArrayList; 
java.util.Collectionsをインポートします。
インポートjava.util.List; 
インポートjava.util.concurrent.CountDownLatch; 
import java.util.concurrent.TimeUnit; 
インポートjava.util.concurrent.locks.Condition; 
インポートjava.util.concurrent.locks.Lock; 

import org.apache.zookeeper.CreateMode; 
import org.apache.zookeeper.KeeperException; 
import org.apache.zookeeper.WatchedEvent; 
import org.apache.zookeeper.Watcher; 
import org.apache.zookeeper.ZooDefs; 
import org.apache.zookeeper.ZooKeeper; 
import org.apache.zookeeper.data.Stat; 
import org.codehaus.jettison.json.JSONString;

import com.fasterxml.jackson.databind.util.JSONPObject; 
import com.google.gson.JsonObject; 


public class DistributedLockはLock、Watcherを実装し
 ます{ private ZooKeeper zk = null; 
 //ルートノード
 privateString ROOT_LOCK = "/ locks"; 
 / /競合するリソース
 privateString lockName; 
 //前のロック待機
 privateString WAIT_LOCK; 
 //現在のロックprivateString 
 CURRENT_LOCK; 
 //カウンター
 privateCountDownLatch countDownLatch; 
 private int sessionTimeout = 30000; 
 private List <Exception> exceptionList = new ArrayList <Exception >(); 

 / 
 ***
 分散ロックを構成する* @ paramconfig
 接続されたURL * @paramlockName競合するリソース
 * /
 public DistributedLock(String config、String lockName){ 
 this.lockName = lockName; 
 try { 
 //動物園管理者に接続zk 
 = new ZooKeeper(config、sessionTimeout、this); 
 Stat stat = zk.exists(ROOT_LOCK、false); 
 if(stat = = null){ 
 //ルートノードが存在しない場合は、ルートノードを作成します
 zk.create(ROOT_LOCK、new byte [0]、ZooDefs.Ids.OPEN_ACL_UNSAFE、CreateMode.PERSISTENT); 
 } 
 } catch(IOException e){ 
 e.printStackTrace(); 
 } catch(InterruptedException e){ 
 e.printStackTrace(); 
 } catch(KeeperException e){ 
 e.printStackTrace(); 
 } 
 } 

 //ノードモニター
 publicvoid process(WatchedEvent event){
 if(this.countDownLatch!= null){  
 this。 countDownLatch。countDown();
 } 
 } 

 public void lock(){ 
 if(exceptionList.size()> 0){ 
 throw new LockException(exceptionList.get(0)); 
 } 
 try { 
 if(this.tryLock()){ 
 System.out.println(Thread.currentThread()。getName()+ "" + lockName + "获得了锁"); 
 戻る; 
 } else { 
 //等待锁
 waitForLock(WAIT_LOCK、sessionTimeout); 
 } 
 } catch(InterruptedException e){ 
 e.printStackTrace(); 
 } catch(KeeperException e){ 
 e.printStackTrace(); 
 } 
 } 

 public boolean tryLock(){ 
 try { 
 String splitStr = "_lock_"; 
 if(lockName.contains(splitStr)){ 
 throw new LockException( "锁名有误"); 
 }
 //一時的に順序付けられたノードを作成します
 CURRENT_LOCK = zk.create(ROOT_LOCK + "/" + lockName + splitStr、new byte [0]、
 ZooDefs.Ids.OPEN_ACL_UNSAFE、CreateMode.EPHEMERAL_SEQUENTIAL); 
 System.out.println(CURRENT_LOCK + "already Create "); 
 //すべての子ノードを
 取得List <String> subNodes = zk.getChildren(ROOT_LOCK、false); 
 //すべてのlockNameロックを取得
 List <String> lockObjects = new ArrayList <String>(); 
 for(String node: subNodes){ 
 String _node = node.split(splitStr)[0]; 
 if(_node.equals(lockName)){ 
 lockObjects.add(node); 
 } 
 } 
 Collections.sort(lockObjects);  
 System.out.println(Thread.currentThread()。getName()+ "ロックは"+ CURRENT_LOCK);
 //現在のノードが最小のノード、ロックを正常に取得する
 if(CURRENT_LOCK.equals(ROOT_LOCK + "/" + lockObjects.get(0))){ 
 return true;
 }

 //若不是アプローチ节点、则找到自己的前一节点
 文字列prevNode = CURRENT_LOCK.substring(CURRENT_LOCK.lastIndexOf( "/")+ 1); 
 WAIT_LOCK = lockObjects.get(Collections.binarySearch(lockObjects、prevNode)-1); 
 } catch(InterruptedException e){ 
 e.printStackTrace(); 
 } catch(KeeperException e){ 
 e.printStackTrace(); 
 } 
 falseを返します。
 } 

 public boolean tryLock(long timeout、TimeUnit unit){ 
 try { 
 if(this.tryLock()){ 
 return true; 
 } 
 return waitForLock(WAIT_LOCK、timeout); 
 } catch(Exception e){ 
 e.printStackTrace(); 
 } 
 falseを返します。
 }
 
 //ロックを待つ
 private boolean waitForLock(String prev、long waitTime)throws KeeperException、InterruptedException { 
 Stat stat = zk.exists(ROOT_LOCK + "/" + prev、true); 

 if(stat!= null){ 
 System.out.println(Thread.currentThread ().getName()+ "Waiting for lock" + ROOT_LOCK + "/" + prev); 
 this.countDownLatch = new CountDownLatch(1); 
 //前のノードが消えた場合はカウントして待機し、次にカウントダウンを実行して停止します待って、ロックを取得します
 this.countDownLatch.await(waitTime、TimeUnit.MILLISECONDS); 
 this.countDownLatch = null; 
 System.out.println(Thread.currentThread()。getName()+ "ロックまで待つ"); 
 } 
 return true; 
 } 

 public voidunlock(){ 
 try { 
 System.out.println( "release lock" + CURRENT_LOCK); 
 zk。delete(CURRENT_LOCK、-1);
 CURRENT_LOCK = null; 
 zk.close(); 
 } catch(InterruptedException e){ 
 e.printStackTrace(); 
 } catch(KeeperException e){ 
 e.printStackTrace(); 
 } 
 } 

 public Condition newCondition(){ 
 nullを返す; 
 } 

 public void lockInterruptibly()throws InterruptedException { 
 this.lock(); 
 } 


 public class LockException extends RuntimeException { 
 private static final long serialVersionUID = 1L; 
 public LockException(String e){ 
 super(e); 
 } 
 public LockException(Exception e){ 
 super(e); 
 } 
 } 
}

テストクラス

package org.java.base.zookeeper;
/**
 * 这段代码有一个问题,看大家能否发现?
 * @author Liuhaihua
 *
 */
public class Test {
 static int n = 500;
 
 public static void secskill() {
 System.out.println(--n);
 }

 public static void main(String[] args) {
 
 Runnable runnable = new Runnable() {
 public void run() {
 DistributedLock lock = null;
 try {
 lock = new DistributedLock("10.152.17.73:2181", "test1");
 lock.lock();
 secskill();
 System.out.println(Thread.currentThread().getName() + "正在运行");
 } finally {
 if (lock != null) {
 lock.unlock();
 }
 }
 }
 };

 for (int i = 0; i < 10; i++) {
 Thread t = new Thread(runnable);
 t.start();
 }
 }
}


おすすめ

転載: blog.51cto.com/15082402/2644329