zookeeper的分佈式鎖的實現

分佈式鎖能夠在一組進程之間提供互斥機制,使得在任何時刻都只有一個進程可以持有鎖,分佈式鎖可以用在大型分佈式系統中實現領導者選舉,在任何時間點,持有鎖的那個進程就是系統的領導者

分佈式鎖的思路:首先需要指定一個groupZnode作爲鎖,通常用它來描述被鎖定的實體,稱爲/lock;然後希望獲得鎖的客戶端創建一些短暫順序znode,作爲鎖znode的子節點。作爲子節點,在任何時刻,順序號最小的客戶端持有鎖。例如:兩個客戶端同時創建znode,分別爲/lock/sub_01,/lock/sub_02,那麼創建/lock/sub_01的客戶端將會持有鎖,因爲他的znode順序號最小。zookeeper服務是順序的仲裁者,因爲他負責分配順序號。

通過刪除znode  /lock/sub_01即可簡單地將鎖釋放;另外,如果客戶端進程死亡,對應的短暫znode也會被刪除。接下來,創建/lock/sub_02的客戶端持有鎖,因爲他的順序號緊跟前一個。通過在創建客戶端的時候添加觀察,可以使得客戶端在獲得鎖的時候得到通知。

下面是實現分佈式鎖的樣例代碼:

emptypackage com.wangl.hadoop.zookeeper.learn;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

public abstract class DistributeLock {
	private static final int SESSION_TIMEOUT = 5000;
	private static final String HOSTS = "host1:2181,host2:2181,host3:2181";
	protected String groupNode = "lock";
	private String subNode = "sub";
	protected ZooKeeper zk;
	protected String thisPath;
	private String waitPath;
	private CountDownLatch connectedSignal = new CountDownLatch(1);
	
	public void connect() throws IOException, InterruptedException, KeeperException{
		zk = new ZooKeeper(HOSTS, SESSION_TIMEOUT, new Watcher(){

			public void process(WatchedEvent event) {
				if(event.getState() == KeeperState.SyncConnected){
					connectedSignal.countDown();
				}
				if(event.getType() == EventType.NodeDeleted && event.getPath().equals(waitPath)){
				    //如果發生非人爲的異常而導致客戶端中斷連接從而使得其下一個客戶端會過早的獲得鎖
					//爲了防止這種情況的發生,在節點刪除事件被觸發的時候,在進行一次檢測,如果該節點的角標不爲0,則重新設置waitPath
					//這樣就避免了這種衝突的發生。
					try {
						List children = zk.getChildren("/"+groupNode, false);
						if(children.isEmpty()){
							System.out.println(groupNode + "is empty");
							return;
						}
						Collections.sort(children);
						String thisNode = thisPath.substring(("/"+groupNode+"/").length());
						int index = children.indexOf(thisNode);
						if(index == 0){
							doSomething();
						}
						else{
							//如果角標不爲0,說明其上個節點發生了異常導致觸發該事件,則重新設置waitPath
							waitPath = "/" + groupNode +"/" + children.get(index - 1);
							//對新設置的waitPath添加觀察
							Stat stat = zk.exists(waitPath, true);
							if(stat == null){
								doSomething();
							}
						}
					} catch (KeeperException e) {
						e.printStackTrace();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			}
			
		});
		
		connectedSignal.await();
		
		thisPath = zk.create("/"+groupNode+"/"+subNode, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
		List children = zk.getChildren("/"+groupNode, false);
		Collections.sort(children);
		String thisNode = thisPath.substring(("/"+groupNode+"/").length());
		int index = children.indexOf(thisNode);
		if(index == -1){}
		if(index == 0){
			doSomething();
		}else{
			waitPath = "/" + groupNode +"/" + children.get(index - 1);
			zk.exists(waitPath, true);
		}
		
	}
	//可以定義成爲抽象方法,子類可以設置客戶端要做的事
	protected abstract void doSomething();
	
	public static void main(String args[]) throws InterruptedException{
		for(int i=0;i<10;i++){
			new Thread(new Runnable(){

				public void run() {
					try {
						DistributeLock lock = new SetDataDistributeLock();
						lock.connect();
					} catch (Exception e) {
						e.printStackTrace();
					}
					
				}
				
			}).start();
		}
		Thread.sleep(Long.MAX_VALUE);
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章