天天看點

微服務: 6 - 分布式服務注冊中心zookeeper之進階實戰

分布式服務注冊中心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;
        }

    }


}