1.9 分布式协调服务-Zookeeper(二)
zoo.cfg配置文件分析
tickTime=2000 zookeeper中最小的时间单位长度 (ms)
initLimit=10 follower节点启动后与leader节点完成数据同步的时间
syncLimit=5 leader节点和follower节点进行心跳检测的最大延时时间
dataDir=/tmp/zookeeper 表示zookeeper服务器存储快照文件的目录
dataLogDir 表示配置 zookeeper事务日志的存储路径,默认指定在dataDir目录下
clientPort 表示客户端和服务端建立连接的端口号: 2181
zookeeper中的一些概念
数据模型
zookeeper的数据模型和文件系统类似,每一个节点称为:znode. 是zookeeper中的最小数据单元。每一个znode上都可以
保存数据和挂载子节点。 从而构成一个层次化的属性结构
节点特性
持久化节点 : 节点创建后会一直存在zookeeper服务器上,直到主动删除
持久化有序节点 :每个节点都会为它的一级子节点维护一个顺序
临时节点 : 临时节点的生命周期和客户端的会话保持一致。当客户端会话失效,该节点自动清理
临时有序节点 : 在临时节点上多勒一个顺序性特性
会话
客户端与服务器的一次连接
Watcher
zookeeper提供了分布式数据发布/订阅,zookeeper允许客户端向服务器注册一个watcher监听。当服务器端的节点触发指定事件的时候
会触发watcher。服务端会向客户端发送一个事件通知
watcher的通知是一次性,一旦触发一次通知后,该watcher就失效
ACL
zookeeper提供控制节点访问权限的功能,用于有效的保证zookeeper中数据的安全性。避免误操作而导致系统出现重大事故。
CREATE /READ/WRITE/DELETE/ADMIN
zookeeper的命令操作
1. create [-s] [-e] path data acl
-s 表示节点是否有序
-e 表示是否为临时节点
默认情况下,是持久化节点
2. get path [watch]
获得指定 path的信息
3.set path data [version]
修改节点 path对应的data
乐观锁的概念
数据库里面有一个 version 字段去控制数据行的版本号
4.delete path [version]
删除节点
stat信息
cversion = 0 子节点的版本号
aclVersion = 0 表示acl的版本号,修改节点权限
dataVersion = 1 表示的是当前节点数据的版本号
czxid 节点被创建时的事务ID
mzxid 节点最后一次被更新的事务ID
pzxid 当前节点下的子节点最后一次被修改时的事务ID
ctime = Sat Aug 05 20:48:26 CST 2017
mtime = Sat Aug 05 20:48:50 CST 2017
cZxid = 0x500000015
ctime = Sat Aug 05 20:48:26 CST 2017
mZxid = 0x500000016
mtime = Sat Aug 05 20:48:50 CST 2017
pZxid = 0x500000015
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0 创建临时节点的时候,会有一个sessionId 。 该值存储的就是这个sessionid
dataLength = 3 数据值长度
numChildren = 0 子节点数
java API的使用
1.导入java包
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
</dependency>
权限控制模式
schema:授权对象
ip : 192.168.1.1
Digest : username:password
world : 开放式的权限控制模式,数据节点的访问权限对所有用户开放。 world:anyone
super :超级用户,可以对zookeeper上的数据节点进行操作
连接状态
KeeperStat.Expired 在一定时间内客户端没有收到服务器的通知, 则认为当前的会话已经过期了。
KeeperStat.Disconnected 断开连接的状态
KeeperStat.SyncConnected 客户端和服务器端在某一个节点上建立连接,并且完成一次version、zxid同步
KeeperStat.authFailed 授权失败
事件类型
NodeCreated 当节点被创建的时候,触发
NodeChildrenChanged 表示子节点被创建、被删除、子节点数据发生变化
NodeDataChanged 节点数据发生变化
NodeDeleted 节点被删除
None 客户端和服务器端连接状态发生变化的时候,事件类型就是None
zookeeper的实际应用场景
zookeeper能够实现哪些场景
订阅发布
watcher机制
统一配置管理(disconf)
分布式锁
1.应用:redis ,zookeeper数据库
2.zookeeper分布式锁实现原理
客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推;
客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听刚好在自己之前一位的子节点删除消息,获得子节点变更通知后重复此步骤直至获得锁;
执行业务代码;
完成业务流程后,删除对应的子节点释放锁。
负载均衡
ID生成器
分布式队列
统一命名服务
master选举
封装的zookeeper——curator
curator
Curator本身是Netflix公司开源的zookeeper客户端;
curator提供了各种应用场景的实现封装
curator-framework 提供了fluent风格api
curator-replice 提供了实现封装
curator连接的重试策略
ExponentialBackoffRetry() 衰减重试
RetryNTimes 指定最大重试次数
RetryOneTime 仅重试一次
RetryUnitilElapsed 一直重试知道规定的时间
curator使用分布式锁
虽然zookeeper原生客户端暴露的API已经非常简洁了,但是实现一个分布式锁还是比较麻烦的。
1.引入包:
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
</dependency>
2.使用分布式锁代码
public static void main(String[] args) throws Exception {
//创建zookeeper的客户端
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.newClient("10.21.41.181:2181,10.21.42.47:2181,10.21.49.252:2181", retryPolicy);
client.start();
//创建分布式锁, 锁空间的根节点路径为/curator/lock
InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock");
//获取锁
mutex.acquire();
//进行业务流程
System.out.println("Enter mutex");
//完成业务流程, 释放锁
mutex.release();
//关闭客户端
client.close(); }
锁的获取和释放使用 acquire()和release()方法即可
下面来分析下获取锁的源码实现。
acquire的方法如下:
/*
* 获取锁,当锁被占用时会阻塞等待,这个操作支持同线程的可重入(也就是重复获取锁),acquire的次数需要与release的次数相同。
* @throws Exception ZK errors, connection interruptions
*/ @Override
public void acquire() throws Exception
{
if ( !internalLock(-1, null) )
{
throw new IOException("Lost connection while trying to acquire lock: " + basePath);
}
}
这里有个地方需要注意,当与zookeeper通信存在异常时,acquire会直接抛出异常,需要使用者自身做重试策略。代码中调用了internalLock(-1, null),参数表明在锁被占用时永久阻塞等待。internalLock的代码如下:
private boolean internalLock(long time, TimeUnit unit) throws Exception
{
//这里处理同线程的可重入性,如果已经获得锁,那么只是在对应的数据结构中增加acquire的次数统计,直接返回成功
Thread currentThread = Thread.currentThread();
LockData lockData = threadData.get(currentThread);
if ( lockData != null )
{
// re-entering
lockData.lockCount.incrementAndGet();
return true;
}
//这里才真正去zookeeper中获取锁
String lockPath = internals.attemptLock(time, unit, getLockNodeBytes());
if ( lockPath != null )
{
//获得锁之后,记录当前的线程获得锁的信息,在重入时只需在LockData中增加次数统计即可
LockData newLockData = new LockData(currentThread, lockPath);
threadData.put(currentThread, newLockData);
return true;
}
//在阻塞返回时仍然获取不到锁,这里上下文的处理隐含的意思为zookeeper通信异常
return false;
}
zookeeper获取锁的具体实现:
String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception
{
//参数初始化,此处省略
//...
//自旋获取锁
while ( !isDone )
{
isDone = true;
try
{
//在锁空间下创建临时且有序的子节点
ourPath = driver.createsTheLock(client, path, localLockNodeBytes);
//判断是否获得锁(子节点序号最小),获得锁则直接返回,否则阻塞等待前一个子节点删除通知
hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath);
}catch ( KeeperException.NoNodeException e ){
//对于NoNodeException,代码中确保了只有发生session过期才会在这里抛出NoNodeException,因此这里根据重试策略进行重试
if ( client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper()) )
{
isDone = false;
}else{
throw e;
}
}
}
//如果获得锁则返回该子节点的路径
if ( hasTheLock )
{
return ourPath;
}
return null;
}
上面代码中主要有两步操作:
driver.createsTheLock:创建临时且有序的子节点,里面实现比较简单不做展开,主要关注几种节点的模式:
1)PERSISTENT(永久);
2)PERSISTENT_SEQUENTIAL(永久且有序);
3)EPHEMERAL(临时);
4)EPHEMERAL_SEQUENTIAL(临时且有序)。
internalLockLoop:阻塞等待直到获得锁。
internalLockLoop是怎么判断锁以及阻塞等待的,这里删除了一些无关代码,只保留主流程:
//自旋直至获得锁
while ( (client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock )
{
//获取所有的子节点列表,并且按序号从小到大排序
List<String> children = getSortedChildren();
//根据序号判断当前子节点是否为最小子节点
String sequenceNodeName = ourPath.substring(basePath.length() + 1); // +1 to include the slash
PredicateResults predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases);
if ( predicateResults.getsTheLock() )
{
//如果为最小子节点则认为获得锁
haveTheLock = true;
}else{
//否则获取前一个子节点
String previousSequencePath = basePath + "/" + predicateResults.getPathToWatch();
//这里使用对象监视器做线程同步,当获取不到锁时监听前一个子节点删除消息并且进行wait(),当前一个子节点删除(也就是锁释放)时,回调会通过notifyAll唤醒此线程,此线程继续自旋判断是否获得锁
synchronized(this){
try{
//这里使用getData()接口而不是checkExists()是因为,如果前一个子节点已经被删除了那么会抛出异常而且不会设置事件监听器,而checkExists虽然也可以获取到节点是否存在的信息但是同时设置了监听器,这个监听器其实永远不会触发,对于zookeeper来说属于资源泄露
client.getData().usingWatcher(watcher).forPath(previousSequencePath);
//如果设置了阻塞等待的时间
if ( millisToWait != null )
{
millisToWait -= (System.currentTimeMillis() - startMillis);
startMillis = System.currentTimeMillis();
if ( millisToWait <= 0 )
{
doDelete = true; // 等待时间到达,删除对应的子节点
break;
}
//等待相应的时间
wait(millisToWait);
}else{
//永远等待
wait();
}
}catch ( KeeperException.NoNodeException e ){
//上面使用getData来设置监听器时,如果前一个子节点已经被删除那么会抛出NoNodeException,只需要自旋一次即可,无需额外处理
}
}
}
}
1.9 分布式协调服务-Zookeeper(二)的更多相关文章
- 分布式协调服务Zookeeper集群之ACL篇
分布式协调服务Zookeeper集群之ACL篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.zookeeper ACL相关知识概览 1>.zookeeper官方文档(h ...
- 分布式协调服务Zookeeper集群监控JMX和ZkWeb应用对比
分布式协调服务Zookeeper集群监控JMX和ZkWeb应用对比 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. JMX是用来远程监控Java应用的框架,这个也可以用来监控其他的J ...
- 分布式协调服务Zookeeper集群搭建
分布式协调服务Zookeeper集群搭建 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.安装jdk环境 1>.操作环境 [root@node101.yinzhengjie ...
- 分布式协调服务Zookeeper扫盲篇
分布式协调服务Zookeeper扫盲篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 身为运维工程师对kubernetes(k8s)可能比较熟,那么etcd(go语言实现)分布式协 ...
- 搞懂分布式技术3:初探分布式协调服务zookeeper
搞懂分布式技术3:初探分布式协调服务zookeeper 1.Zookeepr是什么 Zookeeper是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它实现诸如数据发布/订阅,负载均衡, ...
- 分布式协调服务ZooKeeper工作原理
分布式协调服务ZooKeeper工作原理 原创 2016-02-19 杜亦舒 性能与架构 性能与架构 性能与架构 微信号 yogoup 功能介绍 网站性能提升与架构设计 大数据处理框架Hadoop.R ...
- 中小型研发团队架构实践八:分布式协调服务ZooKeeper
一.ZooKeeper 是什么 Apache ZooKeeper 由 Apache Hadoop 的子项目发展而来,于 2010 年 11 月正式成为了 Apache 的顶级项目. ZooKeeper ...
- 中小型研发团队架构实践:分布式协调服务ZooKeeper
一.ZooKeeper 是什么 Apache ZooKeeper 由 Apache Hadoop 的子项目发展而来,于 2010 年 11 月正式成为了 Apache 的顶级项目. 相关厂商内容 优秀 ...
- 分布式协调服务-Zookeeper
什么是 zookeeper? Zookeeper 是google的chubby一个开源实现,是hadoop的分布式协调服务 它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名 ...
随机推荐
- java:数据结构(二)栈的应用(进制转换全收集)
说到进制转换,java已经封装了基本的方法,在竞赛中使用封装的方法自然能节省大量时间 另一位仁兄介绍的封装好的方法: https://blog.csdn.net/m0_37961948/article ...
- quartz定时任务cron表达式
cron 表达式格式:秒 分 时 日 月 周 年(可选) 子表达式范围 字段名 字段解释 值范围 特殊字符 Seconds 秒 0~59 , - * / Minutes 分 0~59 , - * / ...
- PMM Client 安装异常报错
1.PMM架构 如下图所示 2.Client主要组件 PMM Client是安装在你要监视的MySQL或MongoDB主机上的一组代理组件.组件收集关于一般系统和数据库性能的各种数据,并将该数据发送到 ...
- 该用Python还是SQL?4个案例教你节省时间
在数据分析行业,对数据提出的每一个问题都可以用多种潜在的语言和工具包来回答.每种语言都有其优势,它们之间也存在着不同的区别.不能否认的是,有些操作用Python执行起来要比SQL更加高效.这篇文章分享 ...
- Postgresql数据库部署之:Postgresql 存在session 会话不能删除数据库
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='数据库名' AND pid<>pg_backen ...
- 事务的四大特性(ACID):
事务的四大特性(ACID): 1.原子性(Atomicity): 事务中所有操作是不可再分割的原子单元.事务中所有操作要么都执行成功,要么都执行失败. 2.一致性(Consistency): 事 ...
- 基于gdal的格网插值
格网插值就是使用离散的数据点创建一个栅格图像的过程.通常情况下,有一系列研究区域的离散点,如果我们想将这些点转换为规则的网格数据来进行进一步的处理,或者和其他网格数据进行合并 等处理,就需要使用格网插 ...
- 安利一下workflowy和Dynalist
最近有比较幕布这款笔记记录软件,很是喜欢那种可以记录笔记的方式,不过我还追溯了一下这种笔记模式,发现了workflowy和Dynalist这两款风格一样的软件.大家可以自行注册试试.其中Dynalis ...
- dicom错误解决
https://github.com/pydicom/pydicom/issues/331 sudo apt-get install python-gdcm
- Node.js如何执行cmd
最近正好因业务的一个需求需要研究如何根据vscode的插件名来下载对应的插件以解决之前将插件打包上传到服务器上面导致的延迟问题(插件体积小还好说,如果体积过大,即便是压缩打成zip包,如果同一时刻很多 ...