分佈式協調工具(二)--Zookeepers實現分佈式鎖

什麼是多線程

多線程爲了能夠提高應用程序的運行效率,在一個進程中有多條不同的執行路徑,同時並行執行,互不影響。

什麼是線程安全

當多個線程同時共享,同一個全局變量或靜態變量,做寫的操作時,可能會發生數據衝突問題,也就是線程安全問題。但是做讀操作是不會發生數據衝突問題。

解決辦法

 使用同步代碼塊或者Lock鎖機制,保證在多個線程共享同一個變量只能有一個線程進行操作

什麼是分佈式鎖

分佈式鎖一般用在分佈式系統或者多個應用中,用來控制同一任務是否執行或者任務的執行順序。在項目中,部署了多個tomcat應用,在執行定時任務時就會遇到同一任務可能執行多次的情況,我們可以藉助分佈式鎖,保證在同一時間只有一個tomcat應用執行了定時任務

Zookeeper實現分佈式鎖原理

使用zookeeper創建臨時序列節點來實現分佈式鎖,適用於順序執行的程序,大體思路就是創建臨時序列節點,找出最小的序列節點,獲取分佈式鎖,程序執行完成之後此序列節點消失,通過watch來監控節點的變化,從剩下的節點的找到最小的序列節點,獲取分佈式鎖,執行相應處理,依次類推……

創建Lock接口

public interface Lock {
    //獲取到鎖的資源
	public void getLock();
    // 釋放鎖
	public void unLock();
}

創建ZookeeperAbstractLock抽象類

//將重複代碼寫入子類中..
public abstract class ZookeeperAbstractLock implements Lock {
	// zk連接地址
	private static final String CONNECTSTRING = "127.0.0.1:2181";
	// 創建zk連接
	protected ZkClient zkClient = new ZkClient(CONNECTSTRING);
	protected static final String PATH = "/lock";

	public void getLock() {
		if (tryLock()) {
			System.out.println("##獲取lock鎖的資源####");
		} else {
			// 等待
			waitLock();
			// 重新獲取鎖資源
			getLock();
		}

	}

	// 獲取鎖資源
	abstract boolean tryLock();

	// 等待
	abstract void waitLock();

	public void unLock() {
		if (zkClient != null) {
			zkClient.close();
			System.out.println("釋放鎖資源...");
		}
	}

}

ZookeeperDistrbuteLock類

public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock {
	private CountDownLatch countDownLatch = null;

	@Override
	boolean tryLock() {
		try {
			zkClient.createEphemeral(PATH);
			return true;
		} catch (Exception e) {
//			e.printStackTrace();
			return false;
		}

	}

	@Override
	void waitLock() {
		IZkDataListener izkDataListener = new IZkDataListener() {

			public void handleDataDeleted(String path) throws Exception {
				// 喚醒被等待的線程
				if (countDownLatch != null) {
					countDownLatch.countDown();
				}
			}
			public void handleDataChange(String path, Object data) throws Exception {

			}
		};
		// 註冊事件
		zkClient.subscribeDataChanges(PATH, izkDataListener);
		if (zkClient.exists(PATH)) {
			countDownLatch = new CountDownLatch(1);
			try {
				countDownLatch.await();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		// 刪除監聽
		zkClient.unsubscribeDataChanges(PATH, izkDataListener);
	}

}

使用Zookeeper鎖運行效果

public class OrderService implements Runnable {
	private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
	// 使用lock鎖
	// private java.util.concurrent.locks.Lock lock = new ReentrantLock();
	private Lock lock = new ZookeeperDistrbuteLock();
	public void run() {
		getNumber();
	}
	public void getNumber() {
		try {
			lock.getLock();
			String number = orderNumGenerator.getNumber();
			System.out.println(Thread.currentThread().getName() + ",生成訂單ID:" + number);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unLock();
		}
	}
	public static void main(String[] args) {
		System.out.println("####生成唯一訂單號###");
//		OrderService orderService = new OrderService();
		for (int i = 0; i < 100; i++) {
			new Thread( new OrderService()).start();
		}
	}
}

master選舉使用場景及結構

 現在很多時候我們的服務需要7*24小時工作,假如一臺機器掛了,我們希望能有其它機器頂替它繼續工作。此類問題現在多采用master-salve模式,也就是常說的主從模式,正常情況下主機提供服務,備機負責監聽主機狀態,當主機異常時,可以自動切換到備機繼續提供服務(這裏有點兒類似於數據庫主庫跟備庫,備機正常情況下只監聽,不工作),這個切換過程中選出下一個主機的過程就是master選舉。

對於以上提到的場景,傳統的解決方式是採用一個備用節點,這個備用節點定期給當前主節點發送ping包,主節點收到ping包後會向備用節點發送應答ack,當備用節點收到應答,就認爲主節點還活着,讓它繼續提供服務,否則就認爲主節點掛掉了,自己將開始行使主節點職責。如圖1所示:

 

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