分布式服務注冊中心zookeeper之進階實戰
-
- 分布式鎖作用以及原理
- 基于zk實作分布式鎖的多種方式
分布式鎖作用以及原理
- 為什麼要分布式鎖
- 分布式環境中,如果各個服務節點去競争資源,沒辦法使用單機多線程中JDK自帶的鎖,此時需要分布式鎖來協調
- 企業有哪些常見的手段實作分布式鎖
zookeeper 、redis、memcache
- 分布式鎖的原理
- zookeeper : 建立相應的節點,建立成功,則表示擷取到相應的鎖;釋放鎖時,則删除節點;建立失敗,表示擷取鎖失敗
-
redis、memcache : 對應的去設定一個值作為鎖的标志,每次擷取鎖的時候,判斷對應的值是否存在;
存在,則無法擷取;不存在,則設定相應的鎖,表示擷取到鎖。(redis使用setnx,memcache使用add)
基于zk實作分布式鎖的多種方式
- 注意事項
- 建立節點的時候,一定要建立臨時節點,避免應用擷取到鎖後當機,導緻鎖一直被持有。
- 實作方式
-
單一節點不斷重試
程式連上zk後,嘗試建立節點。如果建立成,則擷取鎖成功;如果鎖已經存在,則擷取失敗,程式進入短暫的休眠,之後重試。釋放鎖的時候,删除節點即可。
缺點:消耗資源,擷取不到會不斷重試
-
使用zk的watcher機制
缺點:引發羊群效應;一旦釋放鎖,導緻大量等待這個鎖的程式争搶資源;
- 建立有序節點
-
每個線程搶占鎖之前,先搶号建立自己的ZNode。同樣,釋放鎖的時候,就需要删除搶号的Znode。搶号成功後,如果不是排号最小的節點,就處于等待通知的狀态。
等誰的通知呢?不需要其他人,隻需要等前一個Znode 的通知就可以了。目前一個Znode 删除的時候,就是輪到了自己占有鎖的時候。
第一個通知第二個、第二個通知第三個,擊鼓傳花似的依次向後。
參考:https://blog.csdn.net/crazymakercircle/article/details/85956246
3 執行個體
- ZkLockExample Zk分布式鎖代碼執行個體
package net.view.lock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Zk分布式鎖代碼執行個體
*/
public class ZkLockExample {
private static int num = 0;
private static CountDownLatch countDownLatch = new CountDownLatch(10);
private static ZkLock lock = ZkLock.getInstance();
/**y
* 每次調用對num進行++操作
*/
public static void inCreate() {
lock.lock(1);
num++;
System.out.println(num);
lock.unLock(1);
}
public static void test() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
inCreate();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//每個線程執行完成之後,調用countdownLatch
countDownLatch.countDown();
}).start();
}
while (true) {
if (countDownLatch.getCount() == 0) {
System.out.println(num);
break;
}
}
}
}
- ZkLock 分布式鎖實作
package net.view.lock;
import org.apache.zookeeper.*;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import static org.apache.zookeeper.CreateMode.EPHEMERAL;
import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;
/**
* 基于zk實作分布式鎖
*/
public class ZkLock {
private ZooKeeper zooKeeper;
private static CountDownLatch countDownLatch = new CountDownLatch(1);
private final String connectString = "192.168.250.130:2181,192.168.250.130:12181";
private final String zkNodeStr = "/dx-lock-";
private ZkLock() {
try {
zooKeeper = new ZooKeeper(connectString, 5000, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("接收到監聽事件=====》"+event);
if (Event.KeeperState.SyncConnected == event.getState()) {
countDownLatch.countDown();
}
}
});
System.out.println("等待連接配接----"+zooKeeper.getState());
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("與zk建立連接配接=====>"+zooKeeper.getState());
} catch (IOException e) {
e.printStackTrace();
}
}
public void lock(Integer id) {
String path = zkNodeStr + id;
//建立臨時節點,如果建立成功的話,就表示擷取鎖,如果失敗,則不斷嘗試
try {
zooKeeper.create(path,"".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println("成功擷取到鎖");
} catch (Exception e) {
while (true) {
try {
Thread.sleep(500L);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
zooKeeper.create(path,"".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
} catch (Exception e1) {
continue;
}
break;
}
}
}
/**
* 釋放鎖,直接删除zk節點
* @param id
*/
public void unLock(Integer id) {
String path = zkNodeStr + id;
try {
zooKeeper.delete(path,-1);
System.out.println("釋放鎖----");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
public static ZkLock getInstance() {
return Singleton.getInstance();
}
private static class Singleton {
private static ZkLock instance;
static {
instance = new ZkLock();
}
private static ZkLock getInstance() {
return instance;
}
}
}