Zookeeper--分布式锁和消息队列
在java并发包中提供了若干锁的实现,它们是用于单个java虚拟机进程中的;而分布式锁能够在一组进程之间提供互斥机制,保证在任何时刻只有一个进程可以持有锁。
分布式环境中多个进程的锁则可以使用Zookeeper来实现。
下面这种方法是使用顺序节点实现共享锁,流程如下:
对于lock()操作,首先让所有参与争锁的客户端都在/_locks目录下创建临时顺序节点,然后获取该路径下的所有节点,如果客户端创建的节点序列号最小则获得锁。否则开始监视它前一个节点并进入等待状态。
对于unlock()操作,将自身创建的节点删除。此时后一个节点的监控将被触发,对应的客户端退出等待状态,取得锁。
下面是一个简单的示例:
---
/**
* 分布式锁
*/
public class DisLock implements Watcher { public static final String LOCK_ROOT = "/__locks__"; private ZooKeeper zk; //锁名称,标识竞争的是哪个锁
private String lockName; //当前创建的节点路径
private String path; //前一个节点的路径
private String prePath; //是否获取锁
private boolean acquired; //构造函数,连接zk,检查父节点存在
public DisLock(String lockName) throws KeeperException, InterruptedException, IOException {
this.lockName = "/" + lockName;
this.zk = new ZooKeeper("localhost:2181", 30000, this);
Assert.notNull(zk, "zookeeper is null");
if (zk.exists(LOCK_ROOT, false) == null) {
zk.create(LOCK_ROOT, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
if (zk.exists(LOCK_ROOT + this.lockName, false) == null) {
zk.create(LOCK_ROOT + this.lockName, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
} public void lock() {
if (tryLock()) {
return;
} else {
waitLock(); }
} //尝试获取锁
public boolean tryLock() {
if (acquired) {
return true;
}
try {
//创建临时节点,自动编号
path = zk.create(LOCK_ROOT + lockName + "/", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL); List<String> ls = zk.getChildren(LOCK_ROOT + lockName, false);
Collections.sort(ls);
if (path.equals(LOCK_ROOT + lockName + "/" + ls.get(0))) {
acquired = true;
return true;
} for (int i = 0; i < ls.size(); i++) {
if (path.equals(LOCK_ROOT + lockName + "/" + ls.get(i))) {
prePath = LOCK_ROOT + lockName + "/" + ls.get(i - 1);
break;
}
}
return false;
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
} //释放锁
public void unlock() {
if (!acquired) {
return;
}
try {
zk.delete(path, -1);
acquired = false;
//System.out.println(Thread.currentThread().getName() + " free lock");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
} //设置监控并等待锁,前一个节点被删除后退出等待,得到锁
private synchronized void waitLock() {
try {
Stat s = zk.exists(prePath, true);
if (s == null) {
//等到锁,返回
acquired = true;
return;
}
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
waitLock();
return;
} //监视节点变化,被监控节点被删除时激活等待锁的线程
@Override
public synchronized void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.NodeDeleted) {
//System.out.println("触发:"+watchedEvent.getPath());
this.notify();
}
}
}
---
其中waitLock和process方法需要加synchronized关键字,以便使用wait和notify方法。
测试方法:
Zookeeper的另一种应用场景是处理FIFO消息队列,利用zk的自动编号,存储数据和数据一致性能力,模拟生产者-消费者模型。
创建3个消费者从zk中获取数据,此时需要使用分布式锁,加锁获取数据的部分。出于模拟生产者和消费者都在不同的进程,所有不共享zk等对象。
示例代码如下:
生产者:
public class Producer extends Thread { private ZooKeeper zk; private Random ran = new Random(); private static AtomicInteger count = new AtomicInteger(0); Producer() throws IOException {
this.zk = new ZooKeeper("localhost:2181", 30000, null);
} void produce(String str) throws KeeperException, InterruptedException {
String name = zk.create(ZkQueue.root + "/element", str.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT_SEQUENTIAL);
//System.out.println(getName() + " create: " + str);
} @Override
public void run() {
try {
while (true) {
String msg = "msg" + count.getAndIncrement();
produce(msg);
Thread.sleep(ran.nextInt(1000));
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
}
}
---循环向zk中写入递增的信息,中间延迟随机毫秒
消费者:
public class Consumer extends Thread { private ZooKeeper zk; private DisLock lock; Consumer() throws IOException, KeeperException, InterruptedException {
lock = new DisLock("queue");
this.zk = new ZooKeeper("localhost:2181", 30000, null);
} private boolean consume() throws KeeperException, InterruptedException {
lock.lock();
try {
List<String> list = zk.getChildren(root, true);
if (list.isEmpty()) {
return true;
}
Collections.sort(list);
String first = list.get(0);
byte[] b = zk.getData(root + "/" + first, false, null);
zk.delete(root + "/" + first, -1);
String str = new String(b);
System.out.println(getName() + " get:" + str);
} finally {
lock.unlock();
}
return false;
} @Override
public void run() {
try {
while (true) {
if (consume()) {
Thread.sleep(1000);
}
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
}
}
---循环从zk中取节点然后删除节点,对整个过程加锁
启动类:
public class ZkQueue implements Watcher{ @Override
public void process(WatchedEvent watchedEvent) {
System.out.printf("---->%s %s\n",watchedEvent.getPath(),watchedEvent.getType());
} private static ZooKeeper zk; public static final String root = "/zkqueue"; public static void main(String[] args) throws IOException, KeeperException, InterruptedException { zk = new ZooKeeper("localhost:2181", 30000, null); ZkUtils.delete(zk, "/__locks__/queue");//此处需保证该路径下没有子节点,否则可能获取到为最小 ZkUtils.delete(zk, root);
zk.create(root, "queue".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); //模拟2个生产者
new Producer().start();
new Producer().start(); //模拟3个消费者
new Consumer().start();
new Consumer().start();
new Consumer().start(); Scanner s = new Scanner(System.in);
s.nextLine(); ZkUtils.delete(zk, root); // 关闭连接
zk.close();
} }
---
输出结果:
Thread-2 get lock
Thread-2 get:msg0
Thread-2 free lock
Thread-3 get lock
Thread-3 get:msg2
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg3
Thread-3 get lock
Thread-2 free lock
Thread-3 get:msg4
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg5
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg6
Thread-4 free lock
Thread-3 get lock
Thread-3 get:msg7
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg8
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg1
Thread-4 free lock
Thread-3 get lock
Thread-3 get:msg9
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg10
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg11
Thread-4 free lock
Thread-3 get lock
Thread-3 get:msg12
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg13
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg14
Thread-4 free lock
Thread-3 get lock
Thread-3 get:msg15
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg16
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg17
Thread-4 free lock
Thread-3 get lock
Thread-3 get:msg18
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg19
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg20
Thread-4 free lock
Thread-3 get lock
Thread-3 get:msg21
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg22
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg23
Thread-4 free lock
Thread-3 get lock
Thread-3 get:msg24
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg25
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg26
Thread-4 free lock
Thread-3 get lock
Thread-3 get:msg27
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg28
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg29
Thread-4 free lock
Thread-3 get lock
Thread-3 get:msg30
Thread-3 free lock
Thread-2 get lock
Thread-2 free lock
Thread-4 get lock
Thread-4 free lock
Thread-3 get lock
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg31
Thread-2 free lock
Thread-2 get lock
Thread-2 get:msg32
Thread-2 free lock
Thread-2 get lock
Thread-2 get:msg33
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg34
Thread-4 free lock
Thread-3 get lock
Thread-3 get:msg35
Thread-3 free lock
Thread-2 get lock
Thread-2 get:msg36
Thread-2 free lock
Thread-4 get lock
Thread-4 free lock
Thread-3 get lock
Thread-3 free lock
Thread-2 get lock
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg37
Thread-4 free lock
Thread-2 get lock
Thread-2 get:msg38
Thread-2 free lock
Thread-3 get lock
Thread-3 get:msg39
Thread-3 free lock
Thread-4 get lock
Thread-4 get:msg40
Thread-4 free lock
Thread-2 get lock
Thread-2 get:msg41
Thread-2 free lock
Thread-3 get lock
Thread-3 free lock
Thread-4 get lock
Thread-4 free lock
Thread-2 get lock
Thread-2 free lock
Thread-4 get lock
Thread-4 get:msg42
Thread-4 free lock
Thread-2 get lock
Thread-2 get:msg43
Thread-2 free lock
Thread-3 get lock
Thread-3 get:msg44
Thread-3 free lock
Thread-4 get lock
Thread-4 free lock
Thread-2 get lock
Thread-2 free lock
Thread-3 get lock
Thread-3 free lock
Thread-4 get lock
Thread-4 get:msg45
Thread-4 free lock
....
---
可见3个消费者线程随机获取到锁,数据在锁中被递增取出,没有重复和遗漏
end
Zookeeper--分布式锁和消息队列的更多相关文章
- redis分布式锁和消息队列
最近博主在看redis的时候发现了两种redis使用方式,与之前redis作为缓存不同,利用的是redis可设置key的有效时间和redis的BRPOP命令. 分布式锁 由于目前一些编程语言,如PHP ...
- java-spring基于redis单机版(redisTemplate)实现的分布式锁+redis消息队列,可用于秒杀,定时器,高并发,抢购
此教程不涉及整合spring整合redis,可另行查阅资料教程. 代码: RedisLock package com.cashloan.analytics.utils; import org.slf4 ...
- 分布式缓存重建并发冲突和zookeeper分布式锁解决方案
如果缓存服务在本地的ehcache中都读取不到数据. 这个时候就意味着,需要重新到源头的服务中去拉去数据,拉取到数据之后,赶紧先给nginx的请求返回,同时将数据写入ehcache和redis中 分布 ...
- Zookeeper 分布式锁 (图解+秒懂+史上最全)
文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...
- ZooKeeper 分布式锁实现
1 场景描述 在分布式应用, 往往存在多个进程提供同一服务. 这些进程有可能在相同的机器上, 也有可能分布在不同的机器上. 如果这些进程共享了一些资源, 可能就需要分布式锁来锁定对这些资源的访问. 2 ...
- ZooKeeper分布式锁浅谈(一)
一.概述 清明节的时候写了一篇分布式锁概述,里面介绍了分布式锁实现的几种方式,其实那时候我一直沉迷于使用redis的悲观锁和乐观锁来实现分布式锁,直到一个血案的引发才让我重新认识了redis分布式锁的 ...
- 分布式锁(一) Zookeeper分布式锁
什么是Zookeeper? Zookeeper(业界简称zk)是一种提供配置管理.分布式协同以及命名的中心化服务,这些提供的功能都是分布式系统中非常底层且必不可少的基本功能,但是如果自己实现这些功能而 ...
- Curator Zookeeper分布式锁
Curator Zookeeper分布式锁 pom.xml中添加如下配置 <!-- https://mvnrepository.com/artifact/org.apache.curator/c ...
- [转载] zookeeper 分布式锁服务
转载自http://www.cnblogs.com/shanyou/archive/2012/09/22/2697818.html 分布式锁服务在大家的项目中或许用的不多,因为大家都把排他放在数据库那 ...
- 跟着大神学zookeeper分布式锁实现-----来自Ruthless
前几天分享了@Ruthless大神的Redis锁,发现和大家都学习了很多东西.因为分布式锁里面,最好的实现是zookeeper的分布式锁.所以在这里把实现方式和大家分享一下. zookeeper分布式 ...
随机推荐
- .Net使用Redis详解之ServiceStack.Redis(7)
Net使用Redis详解之ServiceStack.Redis(七) 序言 本篇从.Net如何接入Reis开始,直至.Net对Redis的各种操作,为了方便学习与做为文档的查看,我做一遍注释展现,其中 ...
- Prism 4 文档 ---第8章 导航
作为同用户具有丰富的交互的客户端应用程序,它的用户界面(UI)将会持续不断的更新来反映用户工作的当前的任务和数据.用户界面可以进行一段时间相当大的变化作为用户交互的应用程序中完成各种任务.通过 ...
- maven_01_简介及安装
一.简介 Maven主要服务于基于Java平台的项目构建.依赖管理和项目信息管理 何为构建 除了编写源代码,我们每天有相当一部分时间花在了编译.运行单元测试.生成文档.打包和部署等烦琐且不起眼的工作上 ...
- 【css】响应式布局入门【转】
最近研究响应式设计框架的时候,发现网上很多相关的属性介绍,却很少有系统的入门级使用的文章,我自己整理了一篇入门知识,并没有什么高深的理论,也不牵扯到框架. 目前已经越来越多的站点以及wap站点使用响应 ...
- qt 音乐播放器
https://blog.csdn.net/zyx_0604/article/details/66974048?fps=1&locationNum=14 https://blog.csdn.n ...
- Linux(CentOS 7) 新增或修改 SSH默认端口
通过ssh连接到服务器,登录root用户 执行命令编辑sshd配置文件 vi /etc/ssh/sshd_config 找到这一行 # Port 去除#号,修改22 为你想要的端口 重启sshd服务 ...
- cookie注入原理及注入检测
通常我们的开发人员在开发过程中会特别注意到防止恶意用户进行恶意的注入操作,因此会对传入的参数进行适当的过滤,但是很多时候,由于个人对安全技术了解的不同,有些开发人员只会对get,post这种方式提交的 ...
- Linux0.11信号处理详解
之前在看操作系统信号这一章的时候,一直是云里雾里的,不知道信号到底是个啥玩意儿..比如在看<Unix环境高级编程>时,就感觉信号是个挺神奇的东西.比如看到下面这段代码: #include& ...
- vue 问题集合 |
vue做类似选项卡 点击改变curIndex , 选项内容显示用 v-show="$inde ...
- pgpool安装配置整理
安装PostgreSQL并配置三节点流复制环境,就不仔细说了,大致步骤如下: 1.下载源码 2.解压安装,如果在./configure --prefix=/usr/pgsql-10执行时提示要--wi ...