zookeeper(4)--zookeeper分布式锁原理

目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。

在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。在单机环境中,Java中其实提供了很多并发处理相关的API,但是这些API在分布式场景中就无能为力了。也就是说单纯的Java Api并不能提供分布式锁的能力。所以针对分布式锁的实现目前有多种方案。

针对分布式锁的实现,目前比较常用的有以下几种方案:

  基于数据库实现分布式锁 基于缓存(redis,memcached,tair)实现分布式锁 基于Zookeeper实现分布式锁

 一、zookeeper中分布式锁实现原理

(1)、普通节点思路

现在模拟一个使用Zookeeper实现分布式锁,假设有A,B,C三台客户端去访问资源,调用zookeeper获得锁。客户端三个在zookeeper的 /locks节点下创建一个/lock节点,由于节点是唯一性的特性,只有一个人会创建成功,其余两个创建失败,会进入监听/locks节点的变化,如果/locks下子节点/lock节点发生变化,其余两个可以去拿锁,这样是否好呢? 这样子会导致惊群效应。就是一个触发使得在短时间呢会触发大量的watcher事件,但是只有一个客户端能拿到锁

  --众人抢,大量watcher事件

(2)、有序节点思路

1、在获取分布式锁的时候在locker节点下创建临时顺序节点,释放锁的时候删除该临时节点。

2、客户端调用createNode方法在locks下创建临时顺序节点,然后调用getChildren(“locks”)来获取locks下面的所有子节点,注意此时不用设置任何Watcher。

3、客户端获取到所有的子节点path之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。

4、如果发现自己创建的节点并非locks所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,然后对其调用exist()方法,同时对其注册事件监听器。

5、之后,让这个被关注的节点删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是locks子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。

  

二、代码实现

package com.lf.zookeeper.lock;

import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/*
 *实现分布式锁
 */
public class DestributeLock implements Lock,Watcher{

	private ZooKeeper zk = null;
	private String ROOT_LOCK ="/locks";//定义根节点
	private String CURRENT_LOCK;//当前锁
	private String WAIT_LOCK ;//等待前一个对象释放锁

	private CountDownLatch countDownLatch;

	public DestributeLock() {
		try {
			zk= new ZooKeeper("192.168.25.129:2181,192.168.25.130:2181,192.168.25.131:2181", 4000, this);
			//判断根节点是否存在
			Stat stat = zk.exists(ROOT_LOCK, false);
			if(stat==null){
				zk.create(ROOT_LOCK, "1".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE , CreateMode.PERSISTENT);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void process(WatchedEvent event) {

		if(countDownLatch != null){
			this.countDownLatch.countDown();
		}
	}

	@Override
	public void lock() {
		if(tryLock()){
			System.out.println(Thread.currentThread().getName()+"->"+CURRENT_LOCK+",获取锁成功!");
			return;
		}
		try {
			waitForLock(WAIT_LOCK);//如果没有获得锁,继续等待
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private boolean waitForLock(String prev) throws Exception, InterruptedException {
		Stat stat = zk.exists(prev, true);
		if(stat!=null){
			System.out.println(Thread.currentThread().getName()+"->等待"+ROOT_LOCK+prev+"释放锁");
			countDownLatch = new CountDownLatch(1);
			countDownLatch.await();
			System.out.println(Thread.currentThread().getName()+"->"+"获得锁成功!");
		}
		return true;
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
		// TODO Auto-generated method stub

	}

	@Override
	public Condition newCondition() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean tryLock() {
		// TODO Auto-generated method stub
		try {

			CURRENT_LOCK= zk.create(ROOT_LOCK+"/", "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
			System.out.println(Thread.currentThread().getName()+"->"+CURRENT_LOCK+",尝试竞争锁!");
			//获取根节点下的所有子节点
			List<String> childrens = zk.getChildren(ROOT_LOCK, false);
			SortedSet<String> sortedSet = new TreeSet();//定义一个有序的集合进行排序
			for (String children : childrens) {
				sortedSet.add(ROOT_LOCK+"/"+children);
			}
			//获取最小的子节点
			String firstNode = sortedSet.first();
			SortedSet<String> lessthanMe = sortedSet.headSet(CURRENT_LOCK);
			if(CURRENT_LOCK.equals(firstNode)){//当前节点和最小锁比较,如果相同,则获取锁成功
				return true;
			}
			if(!lessthanMe.isEmpty()){
				WAIT_LOCK = lessthanMe.last();//获取比当前节点更小的最后一个节点,设置给WAIT_LOCK
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		return false;
	}

	@Override
	public boolean tryLock(long arg0, TimeUnit arg1) throws InterruptedException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void unlock() {
		System.out.println(Thread.currentThread().getName()+"->"+CURRENT_LOCK+"释放锁");
		try {
			zk.delete(CURRENT_LOCK, -1);
			CURRENT_LOCK = null;
			zk.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

}

测试类

package com.lf.zookeeper.lock;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

public class LockDemo {

	public static void main(String[] args) throws IOException {
		CountDownLatch countDownLatch = new CountDownLatch(10);
		for (int i = 1; i <= 10; i++) {
			new Thread(()->{
				try {
					countDownLatch.await();
					DestributeLock destributeLock = new DestributeLock();
					destributeLock.lock();
				} catch (Exception e) {
					e.printStackTrace();
				}
			},"Thread-"+i).start();
			countDownLatch.countDown();
		}
		System.in.read();
	}
}

运行结果

  

Thread-4->/locks/0000000072,尝试竞争锁!
Thread-5->/locks/0000000073,尝试竞争锁!
Thread-9->/locks/0000000074,尝试竞争锁!
Thread-8->/locks/0000000075,尝试竞争锁!
Thread-10->/locks/0000000076,尝试竞争锁!
Thread-2->/locks/0000000077,尝试竞争锁!
Thread-6->/locks/0000000078,尝试竞争锁!
Thread-3->/locks/0000000079,尝试竞争锁!
Thread-7->/locks/0000000080,尝试竞争锁!
Thread-1->/locks/0000000071,获取锁成功!
Thread-4->等待/locks/locks/0000000071释放锁
Thread-5->等待/locks/locks/0000000072释放锁
Thread-9->等待/locks/locks/0000000073释放锁
Thread-8->等待/locks/locks/0000000074释放锁
Thread-10->等待/locks/locks/0000000075释放锁
Thread-2->等待/locks/locks/0000000076释放锁
Thread-6->等待/locks/locks/0000000077释放锁
Thread-3->等待/locks/locks/0000000078释放锁
Thread-7->等待/locks/locks/0000000079释放锁

手动触发watcher事件,释放锁,delete /locks/0000000071

出现     Thread-4->/locks/0000000072,获取锁成功!

三、基于curator的实现分布式锁

  代码

  

package com.lf.zookeeper.lock;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;

public class CuratorLockDemo {

	public static void main(String[] args) {
		CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().build();
		InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, "/locks");//关注节点
		try {
			interProcessMutex.acquire();//获取锁
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

原文地址:https://www.cnblogs.com/flgb/p/10549215.html

时间: 2024-08-30 03:50:13

zookeeper(4)--zookeeper分布式锁原理的相关文章

关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁

首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在jdk java.util 并发包中已经为我们提供了这些方法去加锁, 比如synchronized 关键字 或者Lock 锁,都可以处理. 但是我们现在的应用程序如果只部署一台服务器,那并发量是很差的,如果同时有上万的请求那么很有可能造成服务器压力过大,而瘫痪. 想想双十一 和 三十晚上十点分支付宝红

redis分布式锁原理与实现

分布式锁原理 分布式锁,是控制分布式系统之间同步访问共享资源的一种方式.在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁. 使用setnx.getset.expire.del这4个redis命令实现 setnx 是『SET if Not eXists』(如果不存在,则 SET)的简写. 命令格式:SETNX key value:使用:只在键 k

zookeeper 分布式锁原理:

1 大家也许都很熟悉了多个线程或者多个进程间的共享锁的实现方式了,但是在分布式场景中我们会面临多个Server之间的锁的问题,实现的复杂度比较高.利用基于google chubby原理开发的开源的zookeeper,可以使得这个问题变得简单很多.下面介绍几种可能的实现方式,并且对比每种实现方式的优缺点. 1. 利用节点名称的唯一性来实现共享锁 ZooKeeper抽象出来的节点结构是一个和unix文件系统类似的小型的树状的目录结构.ZooKeeper机制规定:同一个目录下只能有一个唯一的文件名.例

基于zookeeper简单实现分布式锁

这里利用zookeeper的EPHEMERAL_SEQUENTIAL类型节点及watcher机制.来简单实现分布式锁. 主要思想: 1.开启10个线程.在disLocks节点下各自创建名为sub的EPHEMERAL_SEQUENTIAL节点. 2.获取disLocks节点下全部子节点,排序,假设自己的节点编号最小,则获取锁: 3.否则watch排在自己前面的节点,监听到其删除后,进入第2步(又一次检測排序是防止监听的节点发生连接失效.导致的节点删除情况): 4.删除自身sub节点,释放连接: 这

利用多写Redis实现分布式锁原理与实现分析

在我写这篇文章的时候,其实我还是挺纠结的,因为我这个方案本身也是雕虫小技拿出来显眼肯定会被贻笑大方,但是我最终还是拿出来与大家分享,我本着学习的态度和精神,希望大家能够给与我指导和改进方案. 一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. 场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次.在这种情况下,如果无法保证该接口的幂

Redis实现分布式锁原理与实现分析

一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. 场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次.在这种情况下,如果无法保证该接口的幂等性,那么将会出现重复下单问题. 在接收消息的时候,消息推送重复.如果处理消息的接口无法保证幂等,那么重复消费消息产生的影响可能会非常大. 类似这种场景,我们有很多种方法,可以使用幂等操作,也

redisson实现分布式锁原理

Redisson分布式锁 之前的基于注解的锁有一种锁是基本redis的分布式锁,锁的实现我是基于redisson组件提供的RLock,这篇来看看redisson是如何实现锁的. 不同版本实现锁的机制并不相同 引用的redisson最近发布的版本3.2.3,不同的版本可能实现锁的机制并不相同,早期版本好像是采用简单的setnx,getset等常规命令来配置完成,而后期由于redis支持了脚本Lua变更了实现原理. <dependency> <groupId>org.redisson&

redis实现分布式锁原理解析

目录 1.什么是分布式锁? 2.redis实现的分布式锁 3.内部实现解析 3.1.redis中的数据变化 3.2.redisson的实现方式 1.什么是分布式锁? 分布式锁,是控制分布式系统之间同步访问共享资源的一种方式.在分布式系统中,常常需要协调各个系统之间的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁. 2.redis实现的分布式锁 使用的是redisson框架操作

redis分布式锁原理

举例子:秒杀方式看医生,一个人看5min 1.第一次只允许一个人直接进行来,X10:00:00进去了,setNX(roomid,now+5min),出来时间假如是10:05:00 2.时间到了10:05:00,3个人同时进来,需要去看墙上的钟表,一个人一个的看 A进入病房看到,看到钟表时间10:05:00,得到时间10:05:00,看完时候的时间是10:05:01 B进入病房看到,看到钟表时间10:05:01,得到时间10:05:01,看完时候的时间是10:05:02 C进入病房看到,看到钟表时