1. 程式人生 > >使用Java程式碼實現zookeeper分散式鎖

使用Java程式碼實現zookeeper分散式鎖

產生問題

例:在分散式(叢集)環境下,每臺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
###釋放所資源###