ZooKeeper 实现分布式锁的方法示例

 更新时间:2020年6月25日 10:36  点击:1477

ZooKeeper 是一个典型的分布式数据一致性解决方案,分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅、负载均衡、分布式协调/通知、集群管理、Master 选举、分布式锁等功能。

节点

在介绍 ZooKeeper 分布式锁前需要先了解一下 ZooKeeper 中节点(Znode),ZooKeeper 的数据存储数据模型是一棵树(Znode Tree),由斜杠(/)的进行分割的路径,就是一个 Znode(如 /locks/my_lock)。每个 Znode 上都会保存自己的数据内容,同时还会保存一系列属性信息。

Znode 又分为以下四种类型:

类型 描述
持久节点 节点创建后,会一直存在,不会因客户端会话失效而删除
持久顺序节点 基本特性与持久节点一致,创建节点的过程中,ZooKeeper 会在其名字后自动追加一个单调增长的数字后缀,作为新的节点名
临时节点 客户端会话失效或连接关闭后,该节点会被自动删除
临时顺序节点 基本特性与临时节点一致,创建节点的过程中,ZooKeeper 会在其名字后自动追加一个单调增长的数字后缀,作为新的节点名

锁原理

ZooKeeper 分布式锁是基于 临时顺序节点 来实现的,锁可理解为 ZooKeeper 上的一个节点,当需要获取锁时,就在这个锁节点下创建一个临时顺序节点。当存在多个客户端同时来获取锁,就按顺序依次创建多个临时顺序节点,但只有排列序号是第一的那个节点能获取锁成功,其他节点则按顺序分别监听前一个节点的变化,当被监听者释放锁时,监听者就可以马上获得锁。

而且用临时顺序节点的另外一个用意是如果某个客户端创建临时顺序节点后,自己意外宕机了也没关系,ZooKeeper 感知到某个客户端宕机后会自动删除对应的临时顺序节点,相当于自动释放锁。

如上图:ClientA 和 ClientB 同时想获取锁,所以都在 locks 节点下创建了一个临时节点 1 和 2,而 1 是当前 locks 节点下排列序号第一的节点,所以 ClientA 获取锁成功,而 ClientB 处于等待状态,这时 ZooKeeper 中的 2 节点会监听 1 节点,当 1节点锁释放(节点被删除)时,2 就变成了 locks 节点下排列序号第一的节点,这样 ClientB 就获取锁成功了。

代码测试

请确保 ZooKeeper 服务已启动,ZooKeeper 的搭建可参考Kafka 集群 中的 ZooKeeper 集群部分

以下是基于 C# 的测试,Java 可使用 Curator 框架,实现原理和上面描述是一致的,有兴趣可以看看源码,应该也不难理解。

创建 .NET Core 控制台程序 Nuget

安装 ZooKeeperNetEx.Recipes

创建 ZooKeeper Client

private const int CONNECTION_TIMEOUT = 50000;
private const string CONNECTION_STRING = "127.0.0.1:2181";
private ZooKeeper CreateClient()
{
	var zooKeeper = new ZooKeeper(CONNECTION_STRING, CONNECTION_TIMEOUT, NullWatcher.Instance);
	Stopwatch sw = new Stopwatch();
	sw.Start();
	while (sw.ElapsedMilliseconds < CONNECTION_TIMEOUT)
	{
		var state = zooKeeper.getState();
		if (state == ZooKeeper.States.CONNECTED || state == ZooKeeper.States.CONNECTING)
		{
			break;
		}
	}
	sw.Stop();
	return zooKeeper;
}

class NullWatcher : Watcher
  {
    public static readonly NullWatcher Instance = new NullWatcher();
    private NullWatcher() { }
    public override Task process(WatchedEvent @event)
    {
      return Task.CompletedTask;
    }
  }

添加 Lock 方法

/// <summary>
/// 加锁
/// </summary>
/// <param name="key">加锁的节点名</param>
/// <param name="lockAcquiredAction">加锁成功后需要执行的逻辑</param>
/// <param name="lockReleasedAction">锁释放后需要执行的逻辑,可为空</param>
/// <returns></returns>
public async Task Lock(string key, Action lockAcquiredAction, Action lockReleasedAction = null)
{
	// 获取 ZooKeeper Client
	ZooKeeper keeper = CreateClient();
	// 指定锁节点
	WriteLock writeLock = new WriteLock(keeper, $"/{key}", null);

	var lockCallback = new LockCallback(() =>
	{
		lockAcquiredAction.Invoke();
		writeLock.unlock();
	}, lockReleasedAction);
	// 绑定锁获取和释放的监听对象
	writeLock.setLockListener(lockCallback);
	// 获取锁(获取失败时会监听上一个临时节点)
	await writeLock.Lock();
}

class LockCallback : LockListener
{
	private readonly Action _lockAcquiredAction;
	private readonly Action _lockReleasedAction;

	public LockCallback(Action lockAcquiredAction, Action lockReleasedAction)
	{
		_lockAcquiredAction = lockAcquiredAction;
		_lockReleasedAction = lockReleasedAction;
	}

	/// <summary>
	/// 获取锁成功回调
	/// </summary>
	/// <returns></returns>
	public Task lockAcquired()
	{
		_lockAcquiredAction?.Invoke();
		return Task.FromResult(0);
	}

	/// <summary>
	/// 释放锁成功回调
	/// </summary>
	/// <returns></returns>
	public Task lockReleased()
	{
		_lockReleasedAction?.Invoke();
		return Task.FromResult(0);
	}
}

多线程模拟测试

static async Task RunAsync()
{
	Parallel.For(1, 10, async (i) =>
	{
		await new ZooKeeprDistributedLock().Lock("locks", () =>
		{
			Console.WriteLine($"第{i}个请求,获取锁成功:{DateTime.Now},线程Id:{Thread.CurrentThread.ManagedThreadId}");
			Thread.Sleep(1000); // 业务逻辑...
		}, () =>
		{
			Console.WriteLine($"第{i}个请求,释放锁成功:{DateTime.Now},线程Id:{Thread.CurrentThread.ManagedThreadId}");
			Console.WriteLine("-------------------------------");
		});
	});
	await Task.CompletedTask;
}

虽然模拟的是多线程并行执行,但最终都会依赖锁的获取和释放而串行执行实际业务逻辑。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持猪先飞。

[!--infotagslink--]

相关文章

  • 史上最便捷搭建Zookeeper服务器的方法(推荐)

    由于 ZooKeeper 便捷的使用方式、卓越的性能和良好的稳定性,被广泛地应用于诸如 Hadoop、HBase、Kafka 和 Dubbo 等大型分布式系统中。这篇文章主要介绍了史上最便捷搭建Zookeeper服务器的方法,需要的朋友可以参考下...2021-05-07
  • SpringBoot集成redis实现分布式锁的示例代码

    这篇文章主要介绍了SpringBoot集成redis实现分布式锁的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-25
  • ZooKeeper的安装及部署教程

    Zookeeper是一个针对大型分布式系统的可靠协调系统,提供的功能包括:配置维护、名字服务、分布式同步、组服务等,这篇文章主要介绍了ZooKeeper的安装及部署,需要的朋友可以参考下...2020-06-25
  • redission分布式锁防止重复初始化问题

    这篇文章主要介绍了redission分布式锁防止重复初始化问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-15
  • 详解redis分布式锁(优化redis分布式锁的过程及Redisson使用)

    在分布式的开发中,以电商库存的更新功能进行讲解,在实际的应用中相同功能的消费者是有多个的,这篇文章主要介绍了redis分布式锁详解(优化redis分布式锁的过程及Redisson使用),需要的朋友可以参考下...2021-11-12
  • 在Java中操作Zookeeper的示例代码详解

    这篇文章主要介绍了在Java中操作Zookeeper的示例代码详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-07-20
  • 关于idea+centos7+zookeeper报错connectionloss,timeout问题

    这篇文章主要介绍了idea+centos7+zookeeper报错connectionloss,timeout问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-19
  • SpringBoot使用Redis实现分布式锁

    这篇文章主要为大家详细介绍了SpringBoot使用Redis实现分布式锁,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-05-17
  • 谈谈Redis分布式锁的正确实现方法

    这篇文章主要给大家介绍了关于Redis分布式锁的正确实现方法,文中通过示例代码介绍的非常详细,对大家学习或者使用Redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2021-01-15
  • 分析ZooKeeper分布式锁的实现

    在分布式的情况下,sychornized 和 Lock 已经不能满足我们的要求了,那么就需要使用第三方的锁了,这里我们就使用 ZooKeeper 来实现一个分布式锁...2021-07-01
  • SpringBoot系列教程之dubbo和Zookeeper集成方法

    这篇文章主要介绍了SpringBoot系列教程之dubbo和Zookeeper集成方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-09-05
  • Redis构建分布式锁

    本文主要介绍了Redis构建分布式锁的相关知识。具有很好的参考价值,下面跟着小编一起来看下吧...2017-04-03
  • 解决java连接zookeeper很慢的问题

    这篇文章主要介绍了解决java连接zookeeper很慢的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-11-11
  • redis分布式锁及会出现的问题解决

    这篇文章主要给大家介绍了关于redis分布式锁及会出现问题的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-15
  • Redis分布式锁升级版RedLock及SpringBoot实现方法

    这篇文章主要介绍了Redis分布式锁升级版RedLock及SpringBoot实现,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-02-01
  • 详解RedisTemplate下Redis分布式锁引发的系列问题

    这篇文章主要介绍了详解RedisTemplate下Redis分布式锁引发的系列问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-24
  • Java工作中常见的并发问题处理方法总结

    这篇文章主要介绍了Java工作中常见的并发问题处理方法总结,文章内容讲解的很清晰,有不太懂得同学可以跟着学习下...2021-02-10
  • Redis分布式锁的使用和实现原理详解

    这篇文章主要给大家介绍了关于Redis分布式锁的使用和实现原理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-15
  • Redis分布式锁的正确实现方法总结

    在本篇文章里小编给大家整理的是关于Redis分布式锁的正确实现方式介绍,有兴趣的朋友们可以学习下。...2021-01-15
  • 如何利用Redis分布式锁实现控制并发操作

    这篇文章主要介绍了如何利用Redis分布式锁实现控制并发操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-15