采用Zookeeper实现分布式锁

一.Curator简介

Curator 是 Netflix 公司开源的一个  Zookeeper 客户端 ,Curator 框架在 Zookeeper 原生 API 接口上进行了包装,解决了很多 zooKeeper 客户端非常底层的细节开发。提供 ZooKeeper 各种应用场景,如:分布式锁、集群领导选举、共享计数器、缓存机制、分布式队列等的抽象封装,是最好用,最流行的 Zookeeper 的客户端。

下面将使用 Curator 客户端来实现 Java 版的 Zookeeper 分布式锁。

二.Spring Boot 整合 Curator

1.配置文件

zookeeper:
  curator:
    # 集群连接信息
    ip: 192.168.3.101:2181,192.168.3.102:2181,192.168.3.103:2181
    # 会话超时时长
    sessionTimeOut: 60000
    # 重试间隔时长
    sleepMsBetweenRetries: 1000
    # 重试次数
    maxRetries: 3
    # 连接超时时长
    connectionTimeoutMs: 5000

2.Curator的配置类

package com.learn.blog.demo;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "zookeeper.curator")
public class CuratorConfig {
    private String ip;
    private int sessionTimeOut;
    private int sleepMsBetweenRetries;
    private int maxRetries;
    private int connectionTimeoutMs;

    @Bean("curatorFramework")
    public CuratorFramework curatorClient() {
        CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
                .connectString(ip)
                .sessionTimeoutMs(sessionTimeOut)
                .connectionTimeoutMs(connectionTimeoutMs)
                .retryPolicy(new RetryNTimes(maxRetries, sleepMsBetweenRetries))
                .build();
        curatorFramework.start();

        return curatorFramework;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public int getSessionTimeOut() {
        return sessionTimeOut;
    }

    public void setSessionTimeOut(int sessionTimeOut) {
        this.sessionTimeOut = sessionTimeOut;
    }

    public int getSleepMsBetweenRetries() {
        return sleepMsBetweenRetries;
    }

    public void setSleepMsBetweenRetries(int sleepMsBetweenRetries) {
        this.sleepMsBetweenRetries = sleepMsBetweenRetries;
    }

    public int getMaxRetries() {
        return maxRetries;
    }

    public void setMaxRetries(int maxRetries) {
        this.maxRetries = maxRetries;
    }

    public int getConnectionTimeoutMs() {
        return connectionTimeoutMs;
    }

    public void setConnectionTimeoutMs(int connectionTimeoutMs) {
        this.connectionTimeoutMs = connectionTimeoutMs;
    }

}

三.分布式锁的实现

Curator 的 acquire方法和 release 方法的工作流程讲解

acquire方法是 Curator 客户端中获取分布式锁

release方法是 Curator 客户端中释放分布式锁

 源码分析

acquire方法的源码解析

public void acquire() throws Exception {
    if (!this.internalLock(-1L, (TimeUnit)null)) {
        throw new IOException("Lost connection while trying to acquire lock: " + this.basePath);
    }
}

private boolean internalLock(long time, TimeUnit unit) throws Exception {
    // 获取当前线程
    Thread currentThread = Thread.currentThread();
    // 从 ConcurrentMap 中获取当前线程的 lockData
    InterProcessMutex.LockData lockData = (InterProcessMutex.LockData)this.threadData.get(currentThread);
    // 如果 lockData 不为null,说明该线程之前已经获取到了锁
    if (lockData != null) {
        // 可重入数量加一
        lockData.lockCount.incrementAndGet();
        // 返回加锁成功
        return true;
    } else {
        // 向 zookeeper 添加节点
        String lockPath = this.internals.attemptLock(time, unit, this.getLockNodeBytes());
    // 如果添加成功
    if (lockPath != null) {
        InterProcessMutex.LockData newLockData = new InterProcessMutex.LockData(currentThread, lockPath);
        // 把当前线程的 lockData 存入 ConcurrentMap
        this.threadData.put(currentThread, newLockData);
        return true;
     } else {
        return false;
     }
}

// ConcurrentMap 中 key 是 Thread,value 是 LockData
private static class LockData {
    // 线程
    Thread owningThread;
    // 锁的路径
    final String lockPath;
    // 锁的重入数
    final AtomicInteger lockCount;

    private LockData(Thread owningThread, String lockPath) {
        this.lockCount = new AtomicInteger(1);
        this.owningThread = owningThread;
        this.lockPath = lockPath;
    }
}

 release方法的源码解析

public void release() throws Exception {
    // 获取当前的线程
    Thread currentThread = Thread.currentThread();
    // 从 ConcurrentMap 中获取当前线程的 lockData
    InterProcessMutex.LockData lockData = (InterProcessMutex.LockData)this.threadData.get(currentThread);
    // 如果 lockData 为 null,说明该线程释放的锁不是自己的锁,抛出异常
    if (lockData == null) {
        throw new IllegalMonitorStateException("You do not own the lock: " + this.basePath);
    } else {
         // 判断可重入的数量
         int newLockCount = lockData.lockCount.decrementAndGet();
         if (newLockCount <= 0) {
            // 小于 0 直接抛出异常
            if (newLockCount < 0) {
                throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + this.basePath);
            } else {
                try {
                    // 等于 0,删除 zookeeper 上的节点
                    this.internals.releaseLock(lockData.lockPath);
                } finally {
                    // 移除该线程在 ConcurrentMap 的记录
                    this.threadData.remove(currentThread);
                }

            }
        }
    }
}

 分布式锁代码实现

package com.learn.blog.demo;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.springframework.beans.factory.annotation.Autowired;

public class ZKLock {

    @Autowired
    private CuratorFramework curatorFramework;

    private InterProcessMutex interProcessMutex;

    public ZKLock() {
         interProcessMutex = new InterProcessMutex(curatorFramework, "/lock");
    }

    public void lock() {
        try {
            interProcessMutex.acquire();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void release() {
        try {
            interProcessMutex.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值