使用Java程式碼實現zookeeper分散式鎖
阿新 • • 發佈:2019-01-22
產生問題
例:在分散式(叢集)環境下,每臺JVM不能實現同步,比如將一個專案部署到多臺tomcat伺服器,那麼用多臺JVM在使用時間戳生成唯一的訂單號時,會出現訂單號重複問題。
解決辦法:
分散式情況下,怎麼解決訂單號生成不重複:
1. 使用分散式鎖
2. 提前生成好,訂單號,存放在redis。獲取訂單號時,直接從redis中取。
實現分散式鎖的方式
1.使用資料庫實現分散式鎖
缺點:效能差、執行緒出現異常時,容易出現死鎖
2.使用redis實現分散式鎖
缺點:鎖的失效時間難控制、容易產生死鎖、非阻塞式、不可重入
3.使用zookeeper實現分散式鎖
實現相對簡單、可靠性強、使用臨時節點,失效時間容易控制
什麼是分散式鎖?
分散式鎖一般用在分散式系統或者多個應用中,用來控制同一任務是否執行或者任務的執行順序。在專案中,部署了多個tomcat應用,在執行定時任務時就會遇到同一任務可能執行多次的情況,我們可以藉助分散式鎖,保證在同一時間只有一個tomcat應用執行了定時任務。
使用Zookeeper實現分散式鎖
Zookeeper實現分散式鎖原理
使用zookeeper建立臨時序列節點來實現分散式鎖,適用於順序執行的程式,大體思路就是建立臨時序列節點,找出最小的序列節點,獲取分散式鎖,程式執行完成之後此序列節點消失,通過watch來監控節點的變化,從剩下的節點的找到最小的序列節點,獲取分散式鎖,執行相應處理,依次類推……
程式碼實現
Maven依賴
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
建立生成唯一訂單類
public class OrderNumberGenerate {
//全域性訂單ID
private static Integer count = 0;
public String getNumber(){
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss");
return format.format(new Date())+"-"+ ++count;
}
}
建立Lock介面
public interface Lock {
//獲取鎖資源
public void getLock();
//釋放鎖資源
public void unlock();
}
建立ZookeeperAbstractLock抽象類,實現Lock介面
public abstract class ZookeeperAbstractLock implements Lock {
//zk連線地址
private static final String CONNECTSTRING = "127.0.0.1:2181";
//建立zk連線
protected ZkClient zkClient = new ZkClient(CONNECTSTRING);
//PATH
protected static final String PATH = "/lock";
/**
* 獲取鎖
*/
@Override
public void getLock() {
if(trylock()){
System.out.println("###獲取lock鎖當資源###");
} else{
//等待鎖
waitLock();
//重新獲取鎖
getLock();
}
}
/**
* 釋放鎖
*/
@Override
public void unlock() {
if(zkClient!=null){
zkClient.close();
System.out.println("###釋放所資源###");
}
}
/**
* 判斷是否獲取鎖成功,成功返回true,失敗返回false
*/
abstract boolean trylock();
/**
* 等待鎖
*/
abstract void waitLock();
}
建立ZookeeperDistributeLock類,繼承ZookeeperAbstractLock類
public class ZookeeperDistributeLock extends ZookeeperAbstractLock {
private CountDownLatch countDownLatch = null;
@Override
boolean trylock() {
try {
zkClient.createPersistent(PATH);
return true;
} catch (RuntimeException e) {
//e.printStackTrace();
return false;
}
}
@Override
void waitLock() {
IZkDataListener iZkDataListener = new IZkDataListener() {
/**
* 當節點發生改變時
* @param s
* @param o
* @throws Exception
*/
@Override
public void handleDataChange(String s, Object o) throws Exception {
}
/**
* 當節點被刪除時
* @param s
* @throws Exception
*/
@Override
public void handleDataDeleted(String s) throws Exception {
//喚醒被等待的執行緒
if (countDownLatch!=null) {
countDownLatch.countDown();
}
}
};
//註冊事件
zkClient.subscribeDataChanges(PATH,iZkDataListener);
if(zkClient.exists(PATH)){
countDownLatch = new CountDownLatch(1);
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//刪除監聽
zkClient.unsubscribeDataChanges(PATH,iZkDataListener);
}
}
建立多執行緒,使用Zookeeper鎖執行
public class OrderService implements Runnable{
OrderNumberGenerate generate = new OrderNumberGenerate();
//使用自己建立的lock鎖
private Lock lock = new ZookeeperDistributeLock();
@Override
public void run() {
try {
//獲取鎖資源
lock.getLock();
getNumber();
} catch (Exception e) {
e.printStackTrace();
} finally {
//釋放鎖資源
lock.unlock();
}
}
//生成訂單
public void getNumber(){
String number = generate.getNumber();
System.out.println(Thread.currentThread().getName()+",生成訂單ID:"+number);
}
public static void main(String[] args) {
System.out.println("####生成唯一訂單號###");
for (int i=0;i<10;i++) {
new Thread(new OrderService()).start();
}
}
}
執行結果
###獲取lock鎖當資源###
Thread-2,生成訂單ID:2018-09-05-04-45-03-1
###釋放所資源###
###獲取lock鎖當資源###
Thread-4,生成訂單ID:2018-09-05-04-45-11-2
###釋放所資源###
###獲取lock鎖當資源###
Thread-12,生成訂單ID:2018-09-05-04-45-29-3
###釋放所資源###
###獲取lock鎖當資源###
Thread-16,生成訂單ID:2018-09-05-04-45-50-4
###釋放所資源###
###獲取lock鎖當資源###
Thread-10,生成訂單ID:2018-09-05-04-46-02-5
###釋放所資源###
###獲取lock鎖當資源###
Thread-8,生成訂單ID:2018-09-05-04-46-04-6
###釋放所資源###
Thread-18,生成訂單ID:2018-09-05-04-46-05-7
###釋放所資源###
###獲取lock鎖當資源###
Thread-14,生成訂單ID:2018-09-05-04-46-15-8
###釋放所資源###
###獲取lock鎖當資源###
Thread-20,生成訂單ID:2018-09-05-04-46-16-9
###釋放所資源###
###獲取lock鎖當資源###
Thread-6,生成訂單ID:2018-09-05-04-46-22-10
###釋放所資源###