IT老哥

zookeeper实现分布式锁

正确的实现分布式锁方式
这里用的是顺序临时节点。

锁原理

多个客户端来竞争锁,各自创建自己的节点,按照顺序创建,谁排在第一个,谁就成功的获取了锁。

就像排队买东西一样,谁排在第一个,谁就先买。

创建锁的过程

在这里插入图片描述

A、B、C、D四个客户端来抢锁

A先来了,他创建了000001的临时顺序节点,他发现自己是最小的节点,那么就成功的获取到了锁

然后B来获取锁,他按照顺序创建了000001的临时顺序节点,发现前面有一个比他小的节点,那么就获取锁失败。他开始监听A客户端,看他什么时候能释放锁

同理C和D。

释放锁的过程

在这里插入图片描述

A客户端执行完任务后,断开了和zookeeper的会话,这时候临时顺序节点自动删除了,也就释放了锁

B客户端一直在虎视眈眈的watch监听着A,发现他释放了锁,立马就判断自己是不是最小的节点,如果是就获取锁成功

C监听着B,D监听着C。

合理性分析

A释放锁会唤醒B,B获取到锁,对C和D是没有影响的,因为B的节点并没有发生变化。

同时B释放锁,唤醒C,C获取锁,对D是没有影响的,因为C的节点没有变化。

同理D。。。。

释放锁的操作,只会唤醒下一个客户端,不会唤醒所有的客户端。所以这种方案不存在惊群现象。

ps:创建临时节点 = 创建锁,删除临时节点 = 释放锁。

代码实现

我们这里直接用封装好的工具类,因为如果你自己写的话,如果测试不到位,一旦线上出现问题,那就是大问题。

这里我们用curator这个工具类,他这里把分布式锁已经都给我们实现好了,我们使用起来就像ReentrantLock这些锁一样,非常简单。

如果大家有兴趣,可以去阅读一下curator的源码。
pom文件配置

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.0.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.10</version>
</dependency>
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.RetryNTimes;
import java.util.concurrent.TimeUnit;

/**
 * curator分布式锁测试
 */
public class CuratorDistrLockTest implements Runnable {
    
    

  //zookeeper的地址
  private static final String ZK_ADDRESS = "127.0.0.1:2181";

  private static final String ZK_LOCK_PATH = "/zkLock";

  static CuratorFramework client = null;

  static {
    
    
      // 连接ZK,如果连接失败,设置每5000毫秒重试一次,最多重试10次
      client = CuratorFrameworkFactory.newClient(ZK_ADDRESS,
              new RetryNTimes(10, 5000));
      client.start();
  }

  private static void curatorLockTest() {
    
    

      InterProcessMutex lock = new InterProcessMutex(client, ZK_LOCK_PATH);
      try {
    
    
          if (lock.acquire(6 * 1000, TimeUnit.SECONDS)) {
    
    
              System.out.println("====== " + Thread.currentThread().getName() + " 抢到了锁 ======");
              //执行业务逻辑
              Thread.sleep(15000);
              System.out.println(Thread.currentThread().getName() + "任务执行完毕");
          }
      } catch (Exception e) {
    
    
          System.out.println("业务异常");
      } finally {
    
    
          try {
    
    
              lock.release();
          } catch (Exception e) {
    
    
              System.out.println("锁释放异常");
          }
      }
  }

  public static void main(String[] args) {
    
    
      // 用两个线程,模拟两个客户端
      // 每个线程创建各自的zookeeper连接对象
      new Thread(new CuratorDistrLockTest()).start();

      new Thread(new CuratorDistrLockTest()).start();
  }

  @Override
  public void run() {
    
    
      curatorLockTest();
  }
}

猜你喜欢

转载自blog.csdn.net/yangshengwei230612/article/details/113990906