<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ZooKeeper分布式鎖的實(shí)現(xiàn)

          共 17163字,需瀏覽 35分鐘

           ·

          2021-07-12 07:07

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

            作者 |  IT王小二

          來(lái)源 |  urlify.cn/EvQ7Fv

          在分布式的情況下,sychornized 和 Lock 已經(jīng)不能滿(mǎn)足我們的要求了,那么就需要使用第三方的鎖了,這里我們就使用 ZooKeeper 來(lái)實(shí)現(xiàn)一個(gè)分布式鎖。

          一、分布式鎖方案比較

          方案實(shí)現(xiàn)思路優(yōu)點(diǎn)缺點(diǎn)
          利用 MySQL 的實(shí)現(xiàn)方案利用數(shù)據(jù)庫(kù)自身提供的鎖機(jī)制實(shí)現(xiàn),要求數(shù)據(jù)庫(kù)支持行級(jí)鎖實(shí)現(xiàn)簡(jiǎn)單性能差,無(wú)法適應(yīng)高并發(fā)場(chǎng)景;容易出現(xiàn)死鎖的情況;無(wú)法優(yōu)雅的實(shí)現(xiàn)阻塞式鎖
          利用 Redis 的實(shí)現(xiàn)方案使用 Setnx 和 lua 腳本機(jī)制實(shí)現(xiàn),保證對(duì)緩存操作序列的原子性性能好實(shí)現(xiàn)相對(duì)復(fù)雜,有可能出現(xiàn)死鎖;無(wú)法優(yōu)雅的實(shí)現(xiàn)阻塞式鎖
          利用 ZooKeeper 的實(shí)現(xiàn)方案基于 ZooKeeper 節(jié)點(diǎn)特性及 watch 機(jī)制實(shí)現(xiàn)性能好,穩(wěn)定可靠性高,能較好地實(shí)現(xiàn)阻塞式鎖實(shí)現(xiàn)相對(duì)復(fù)雜

          二、ZooKeeper實(shí)現(xiàn)分布式鎖

          這里使用 ZooKeeper 來(lái)實(shí)現(xiàn)分布式鎖,以50個(gè)并發(fā)請(qǐng)求來(lái)獲取訂單編號(hào)為例,描述兩種方案,第一種為基礎(chǔ)實(shí)現(xiàn),第二種在第一種基礎(chǔ)上進(jìn)行了優(yōu)化。

          1. 方案一

          流程描述:

          具體代碼:

          OrderNumGenerator:

          /**
           * @Author SunnyBear
           * @Description 生成隨機(jī)訂單號(hào)
           */
          public class OrderNumGenerator {

              private static long count = 0;

              /**
               * 使用日期加數(shù)值拼接成訂單號(hào)
               */
              public String getOrderNumber() throws Exception {
                  String date = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
                  String number = new DecimalFormat("000000").format(count++);
                  return date + number;
              }
          }

          Lock:

          /**
           * @Author SunnyBear
           * @Description 自定義鎖接口
           */
          public interface Lock {

              /**
               * 獲取鎖
               */
              public void getLock();

              /**
               * 釋放鎖
               */
              public void unLock();
          }

          AbstractLock:

          /**
           * @Author SunnyBear
           * @Description 定義一個(gè)模板,具體的方法由子類(lèi)來(lái)實(shí)現(xiàn)
           */
          public abstract class AbstractLock implements Lock {

              /**
               * 獲取鎖
               */
              @Override
              public void getLock() {

                  if (tryLock()) {
                      System.out.println("--------獲取到了自定義Lock鎖的資源--------");
                  } else {
                      // 沒(méi)拿到鎖則阻塞,等待拿鎖
                      waitLock();
                      getLock();
                  }

              }

              /**
               * 嘗試獲取鎖,如果拿到了鎖返回true,沒(méi)有拿到則返回false
               */
              public abstract boolean tryLock();

              /**
               * 阻塞,等待獲取鎖
               */
              public abstract void waitLock();
          }

          ZooKeeperAbstractLock:

          /**
           * @Author SunnyBear
           * @Description 定義需要的服務(wù)連接
           */
          public abstract class ZooKeeperAbstractLock extends AbstractLock {

              private static final String SERVER_ADDR = "192.168.182.130:2181,192.168.182.131:2181,192.168.182.132:2181";

              protected ZkClient zkClient = new ZkClient(SERVER_ADDR);

              protected static final String PATH = "/lock";
          }

          ZooKeeperDistrbuteLock:

          /**
           * @Author SunnyBear
           * @Description 真正實(shí)現(xiàn)鎖的細(xì)節(jié)
           */
          public class ZooKeeperDistrbuteLock extends ZooKeeperAbstractLock {
              private CountDownLatch countDownLatch = null;

              /**
               * 嘗試拿鎖
               */
              @Override
              public boolean tryLock() {
                  try {
                      // 創(chuàng)建臨時(shí)節(jié)點(diǎn)
                      zkClient.createEphemeral(PATH);
                      return true;
                  } catch (Exception e) {
                      // 創(chuàng)建失敗報(bào)異常
                      return false;
                  }
              }

              /**
               * 阻塞,等待獲取鎖
               */
              @Override
              public void waitLock() {
                  // 創(chuàng)建監(jiān)聽(tīng)
                  IZkDataListener iZkDataListener = new IZkDataListener() {
                      @Override
                      public void handleDataChange(String s, Object o) throws Exception {

                      }

                      @Override
                      public void handleDataDeleted(String s) throws Exception {
                          // 釋放鎖,刪除節(jié)點(diǎn)時(shí)喚醒等待的線程
                          if (countDownLatch != null) {
                              countDownLatch.countDown();
                          }
                      }
                  };

                  // 注冊(cè)監(jiān)聽(tīng)
                  zkClient.subscribeDataChanges(PATH, iZkDataListener);

                  // 節(jié)點(diǎn)存在時(shí),等待節(jié)點(diǎn)刪除喚醒
                  if (zkClient.exists(PATH)) {
                      countDownLatch = new CountDownLatch(1);
                      try {
                          countDownLatch.await();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }

                  // 刪除監(jiān)聽(tīng)
                  zkClient.unsubscribeDataChanges(PATH, iZkDataListener);
              }

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

          測(cè)試效果:使用50個(gè)線程來(lái)并發(fā)測(cè)試ZooKeeper實(shí)現(xiàn)的分布式鎖

          /**
           * @Author SunnyBear
           * @Description 使用50個(gè)線程來(lái)并發(fā)測(cè)試ZooKeeper實(shí)現(xiàn)的分布式鎖
           */
          public class OrderService {

              private static class OrderNumGeneratorService implements Runnable {

                  private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();;
                  private Lock lock = new ZooKeeperDistrbuteLock();

                  @Override
                  public void run() {
                      lock.getLock();
                      try {
                          System.out.println(Thread.currentThread().getName() + ", 生成訂單編號(hào):"  + orderNumGenerator.getOrderNumber());
                      } catch (Exception e) {
                          e.printStackTrace();
                      } finally {
                          lock.unLock();
                      }
                  }
              }

              public static void main(String[] args) {
                  System.out.println("----------生成唯一訂單號(hào)----------");
                  for (int i = 0; i < 50; i++) {
                      new Thread(new OrderNumGeneratorService()).start();
                  }
              }
          }

          2. 方案二

          方案二在方案一的基礎(chǔ)上進(jìn)行優(yōu)化,避免產(chǎn)生“羊群效應(yīng)”,方案一一旦臨時(shí)節(jié)點(diǎn)刪除,釋放鎖,那么其他在監(jiān)聽(tīng)這個(gè)節(jié)點(diǎn)變化的線程,就會(huì)去競(jìng)爭(zhēng)鎖,同時(shí)訪問(wèn) ZooKeeper,那么怎么更好的避免各線程的競(jìng)爭(zhēng)現(xiàn)象呢,就是使用臨時(shí)順序節(jié)點(diǎn),臨時(shí)順序節(jié)點(diǎn)排序,每個(gè)臨時(shí)順序節(jié)點(diǎn)只監(jiān)聽(tīng)它本身的前一個(gè)節(jié)點(diǎn)變化。

          流程描述:

          具體代碼

          具體只需要將方案一中的 ZooKeeperDistrbuteLock 改變,增加一個(gè) ZooKeeperDistrbuteLock2,測(cè)試代碼中使用 ZooKeeperDistrbuteLock2 即可測(cè)試,其他代碼都不需要改變。

          /**
           * @Author SunnyBear
           * @Description 真正實(shí)現(xiàn)鎖的細(xì)節(jié)
           */
          public class ZooKeeperDistrbuteLock2 extends ZooKeeperAbstractLock {

              private CountDownLatch countDownLatch = null;
              /**
               * 當(dāng)前請(qǐng)求節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)
               */
              private String beforePath;
              /**
               * 當(dāng)前請(qǐng)求的節(jié)點(diǎn)
               */
              private String currentPath;

              public ZooKeeperDistrbuteLock2() {
                  if (!zkClient.exists(PATH)) {
                      // 創(chuàng)建持久節(jié)點(diǎn),保存臨時(shí)順序節(jié)點(diǎn)
                      zkClient.createPersistent(PATH);
                  }
              }

              @Override
              public boolean tryLock() {
                  // 如果currentPath為空則為第一次嘗試拿鎖,第一次拿鎖賦值currentPath
                  if (currentPath == null || currentPath.length() == 0) {
                      // 在指定的持久節(jié)點(diǎn)下創(chuàng)建臨時(shí)順序節(jié)點(diǎn)
                      currentPath = zkClient.createEphemeralSequential(PATH + "/""lock");
                  }
                  // 獲取所有臨時(shí)節(jié)點(diǎn)并排序,例如:000044
                  List<String> childrenList = zkClient.getChildren(PATH);
                  Collections.sort(childrenList);

                  if (currentPath.equals(PATH + "/" + childrenList.get(0))) {
                      // 如果當(dāng)前節(jié)點(diǎn)在所有節(jié)點(diǎn)中排名第一則獲取鎖成功
                      return true;
                  } else {
                      int wz = Collections.binarySearch(childrenList, currentPath.substring(6));
                      beforePath = PATH + "/" + childrenList.get(wz - 1);
                  }
                  return false;
              }

              @Override
              public void waitLock() {
                  // 創(chuàng)建監(jiān)聽(tīng)
                  IZkDataListener iZkDataListener = new IZkDataListener() {
                      @Override
                      public void handleDataChange(String s, Object o) throws Exception {

                      }

                      @Override
                      public void handleDataDeleted(String s) throws Exception {
                          // 釋放鎖,刪除節(jié)點(diǎn)時(shí)喚醒等待的線程
                          if (countDownLatch != null) {
                              countDownLatch.countDown();
                          }
                      }
                  };

                  // 注冊(cè)監(jiān)聽(tīng),這里是給排在當(dāng)前節(jié)點(diǎn)前面的節(jié)點(diǎn)增加(刪除數(shù)據(jù)的)監(jiān)聽(tīng),本質(zhì)是啟動(dòng)另外一個(gè)線程去監(jiān)聽(tīng)前置節(jié)點(diǎn)
                  zkClient.subscribeDataChanges(beforePath, iZkDataListener);

                  // 前置節(jié)點(diǎn)存在時(shí),等待前置節(jié)點(diǎn)刪除喚醒
                  if (zkClient.exists(beforePath)) {
                      countDownLatch = new CountDownLatch(1);
                      try {
                          countDownLatch.await();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }

                  // 刪除對(duì)前置節(jié)點(diǎn)的監(jiān)聽(tīng)
                  zkClient.unsubscribeDataChanges(beforePath, iZkDataListener);
              }

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


          瀏覽 46
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉亚洲 | 真实国产亂伦免费看 | 激情国产精品 | 成人亚洲A片Ⅴ一区二区三区动漫 | 日韩A级电影在线 |