前書き
ZooKeeperは、分散型のオープンソース分散型アプリケーション調整サービスであり、GoogleのChubbyのオープンソース実装であり、HadoopとHbaseの重要なコンポーネントです。分散アプリケーションに一貫したサービスを提供するソフトウェアです。提供される機能には、構成の保守、ドメイン名サービス、分散同期、グループサービスなどがあります。
ZooKeeperのアーキテクチャは、冗長サービスを通じて高可用性を実現します。したがって、最初に応答がない場合、クライアントは別のZooKeeperホストに問い合わせることができます。ZooKeeperノードは、ファイルシステムやプレフィックスツリー構造と非常によく似た階層的な名前空間にデータを格納します。クライアントはノード上で読み取りと書き込みを行うことができるため、このように共有構成サービスを利用できます。更新は全順序で行われます。
ZooKeeper分散ロックの原則に基づく
で飼育係node_nノードを指定されたノードの一時的な順序を作成する(ロック)
すべての子供をロック下に置く
ノードの自己増加シーケンス番号に従って、子ノードを小さいものから大きいものへとソートします。
ノードが最初の子ノードであるかどうかを判別し、そうである場合はロックを取得します。そうでない場合は、ノードよりも小さいノードの削除イベントをリッスンします。
監視イベントが有効になった場合は、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(); } } }