数据模型

Zookeeper的数据模型与文件系统非常相似,唯一不同的它的每个节点(ZNode)都可以存放数据,无论父节点还是子节点。

事务ID

即前面提到的ZXID。对每个事务请求,Zookeeper都会分配一个ZXID,保证操作的全局顺序。

节点类型

  1. 持久节点:创建后一直存在,直到被删除
  2. 临时节点:当会话结束或超时就会消失
  3. 有序节点:在给定的节点名后面加上一个有序的数字后缀,这个后缀的上限是整型的最大值

节点的状态

节点的状态信息定于为Stat类,基本属性如下:

版本号-----保证分布式数据的原子操作

上面节点状态属性中的version、cversion、aversion就是Zookeeper利用乐观锁机制来保证原子操作的属性。

Zookeeper服务器的PrepRequestProcessor处理器类中,处理每个数据更新请求(setDataRequest)时,进行如下操作:

            zks.sessionTracker.checkSession(request.sessionId, request.getOwner());
SetDataRequest setDataRequest = (SetDataRequest)record;
if(deserialize)
ByteBufferInputStream.byteBuffer2Record(request.request, setDataRequest);
path = setDataRequest.getPath();
validatePath(path, request.sessionId);
nodeRecord = getRecordForPath(path);
checkACL(zks, nodeRecord.acl, ZooDefs.Perms.WRITE,
request.authInfo);
//使用乐观锁检查version
version = setDataRequest.getVersion();
int currentVersion = nodeRecord.stat.getVersion();
if (version != -1 && version != currentVersion) {
throw new KeeperException.BadVersionException(path);
}
version = currentVersion + 1;
request.txn = new SetDataTxn(path, setDataRequest.getData(), version);
nodeRecord = nodeRecord.duplicate(request.hdr.getZxid());
nodeRecord.stat.setVersion(version);
addChangeRecord(nodeRecord);

ACL-----保证数据安全

权限模式(Scheme):

  1. iP:"ip:192.168.0.12"表示针对这个ip进行权限控制,"ip:192.168.0.1/24"表示对192.168.0.*这个网段控制
  2. Digest:以"username:password"来标识,Zookeeper会对其进行两次编码----SHA-1和BASE64
  3. World:对所有用户开放
  4. Super:超级管理员,可以对任何数据操作,启动时配置-Dzookeeper.DigestAuthenticationProvider.superDigest=super:password,password需要经过编码

授权对象(ID):

权限(Permission):

  1. CREATE:子节点的创建权限
  2. DELETE:子节点的删除权限
  3. READ:读取权限
  4. WRITE:更新权限
  5. ADMIN:ACL操作权限

watcher机制

总体概况为:客户端注册watcher、服务端处理watcher、客户端回调watcher。

1.客户端注册watcher

以getData为例:

1.标记request,封装watcher到WatcherRegister

public byte[] getData(String path, Watcher watcher, Stat stat) throws KeeperException, InterruptedException {
....
ZooKeeper.WatchRegistration wcb = null;
if (watcher != null) {
wcb = new ZooKeeper.DataWatchRegistration(watcher, path);
}
....
request.setWatch(watcher != null);
GetDataResponse response = new GetDataResponse();
ReplyHeader r = this.cnxn.submitRequest(h, request, response, wcb);
....
}

2.将request封装为Packet(通讯的最小单元)放入发送队列发送,等待服务端响应

public ReplyHeader submitRequest(RequestHeader h, Record request, Record response, WatchRegistration watchRegistration, WatchDeregistration watchDeregistration) throws InterruptedException {
ReplyHeader r = new ReplyHeader();
ClientCnxn.Packet packet = this.queuePacket(h, r, request, response, (AsyncCallback)null, (String)null, (String)null, (Object)null, watchRegistration, watchDeregistration);
synchronized(packet) {
while(!packet.finished) {
packet.wait();
} return r;
}
}

3.客户端的sendThread的readResqponse()负责接收响应,finishPacket方法将watcher注册到ZKWatcherManager中

private void finishPacket(ClientCnxn.Packet p) {
int err = p.replyHeader.getErr();
if (p.watchRegistration != null) {
p.watchRegistration.register(err);
}
......
}

2.服务端处理watcher

服务端处理分为ServerCnxn(与客户端的连接)存储和watcher触发

2.1ServerCnxn存储

1.FinalRequestProcessor的processRequest会判断是否要注册watcher

		case OpCode.getData: {
lastOp = "GETD";
GetDataRequest getDataRequest = new GetDataRequest();
ByteBufferInputStream.byteBuffer2Record(request.request,
getDataRequest);
DataNode n = zks.getZKDatabase().getNode(getDataRequest.getPath());
if (n == null) {
throw new KeeperException.NoNodeException();
}
PrepRequestProcessor.checkACL(zks, zks.getZKDatabase().aclForNode(n),
ZooDefs.Perms.READ,
request.authInfo);
Stat stat = new Stat();
byte b[] = zks.getZKDatabase().getData(getDataRequest.getPath(), stat,
getDataRequest.getWatch() ? cnxn : null);
rsp = new GetDataResponse(b, stat);
break;
}

2.getDataRequest.getWatch()为true会将ServerCnxn存储到WatcherManager中

watchManager是Zk服务器端Watcher的管理者,从两个维度维护watcher:

  1. watchTable从数据节点的粒度来维护
  2. watch2Paths从watcher的粒度来维护

2.2watcher触发

当节点数据改变时将调用watcherManager的triggerWatch方法向客户端发送通知

public Set<Watcher> triggerWatch(String path, EventType type, Set<Watcher> supress) {
//1.封装watchedEvent
WatchedEvent e = new WatchedEvent(type,
KeeperState.SyncConnected, path);
HashSet<Watcher> watchers;
//2.查询watcher
synchronized (this) {
watchers = watchTable.remove(path);
if (watchers == null || watchers.isEmpty()) {
if (LOG.isTraceEnabled()) {
ZooTrace.logTraceMessage(LOG,
ZooTrace.EVENT_DELIVERY_TRACE_MASK,
"No watchers for " + path);
}
return null;
}
for (Watcher w : watchers) {
HashSet<String> paths = watch2Paths.get(w);
if (paths != null) {
paths.remove(path);
}
}
}
for (Watcher w : watchers) {
if (supress != null && supress.contains(w)) {
continue;
}
//3.获取ServerCnxn,向客户端发送通知
w.process(e);
}
return watchers;
}

3.客户端回调watcher

1.SendThread接收通知

		else if (replyHdr.getXid() == -1) {//-1代表这是通知
if (ClientCnxn.LOG.isDebugEnabled()) {
ClientCnxn.LOG.debug("Got notification sessionid:0x" + Long.toHexString(ClientCnxn.this.sessionId));
}
//1.反序列化
WatcherEvent event = new WatcherEvent();
event.deserialize(bbia, "response");
//2.相对路径处理
if (ClientCnxn.this.chrootPath != null) {
String serverPath = event.getPath();
if (serverPath.compareTo(ClientCnxn.this.chrootPath) == 0) {
event.setPath("/");
} else if (serverPath.length() > ClientCnxn.this.chrootPath.length()) {
event.setPath(serverPath.substring(ClientCnxn.this.chrootPath.length()));
} else {
ClientCnxn.LOG.warn("Got server path " + event.getPath() + " which is too short for chroot path " + ClientCnxn.this.chrootPath);
}
}
//3.还原watchedEvent
WatchedEvent we = new WatchedEvent(event);
if (ClientCnxn.LOG.isDebugEnabled()) {
ClientCnxn.LOG.debug("Got " + we + " for sessionid 0x" + Long.toHexString(ClientCnxn.this.sessionId));
}
//4.交给eventThread回调watcher
ClientCnxn.this.eventThread.queueEvent(we);
}

2.调用EventThread的queueEvent方法从ZKWatcherManager获取watcher入队

	private void queueEvent(WatchedEvent event, Set<Watcher> materializedWatchers) {
if (event.getType() != EventType.None || this.sessionState != event.getState()) {
this.sessionState = event.getState();
Object watchers;
if (materializedWatchers == null) {
//从ZKWatcherManager获取watcher
watchers = ClientCnxn.this.watcher.materialize(event.getState(), event.getType(), event.getPath());
} else {
watchers = new HashSet();
((Set)watchers).addAll(materializedWatchers);
} ClientCnxn.WatcherSetEventPair pair = new ClientCnxn.WatcherSetEventPair((Set)watchers, event);
//入队等待run方法处理
this.waitingEvents.add(pair);
}
}

3.EventThread的run方法串行调用队列中的事件包含的watcher的process方法

public void run() {
try {
this.isRunning = true; while(true) {
Object event = this.waitingEvents.take();
if (event == ClientCnxn.this.eventOfDeath) {
this.wasKilled = true;
} else {
this.processEvent(event);
} if (this.wasKilled) {
LinkedBlockingQueue var2 = this.waitingEvents;
synchronized(this.waitingEvents) {
if (this.waitingEvents.isEmpty()) {
this.isRunning = false;
break;
}
}
}
}
} catch (InterruptedException var5) {
ClientCnxn.LOG.error("Event thread exiting due to interruption", var5);
} ClientCnxn.LOG.info("EventThread shut down for session: 0x{}", Long.toHexString(ClientCnxn.this.getSessionId()));
} private void processEvent(Object event) {
try {
if (event instanceof ClientCnxn.WatcherSetEventPair) {
ClientCnxn.WatcherSetEventPair pair = (ClientCnxn.WatcherSetEventPair)event;
Iterator i$ = pair.watchers.iterator(); while(i$.hasNext()) {
Watcher watcher = (Watcher)i$.next(); try {
watcher.process(pair.event);
} catch (Throwable var11) {
ClientCnxn.LOG.error("Error while calling watcher ", var11);
}
}
}
...... }

4.watcher特性

  1. 一次性:客户端和服务端都清除watcher
  2. 客户端串行执行
  3. 轻量:只告诉发生什么事件,不告诉变化的数据

参考资料

从 Paxos 到 Zookeeper——分布式一致性原理和实践

Zookeeper ----- 系统模型的更多相关文章

  1. 【分布式】Zookeeper系统模型

    一.前言 前面已经讲解了Zookeeper的一些应用场景,但是并没有深入到Zookeeper内部进行分析,本篇将讲解其系统模型. 二.系统模型 2.1 数据模型 Zookeeper的数据节点称为ZNo ...

  2. zookeeper系列(六)zookeeper的系统模型(数据树)

    作者:leesf    掌控之中,才会成功:掌控之外,注定失败. 出处:http://www.cnblogs.com/leesf456/p/6072597.html尊重作者原创,奇文共欣赏,大家共同学 ...

  3. Apache-Shiro+Zookeeper系统集群安全解决方案之缓存管理

    上篇[Apache-Shiro+Zookeeper系统集群安全解决方案之会话管理],解决了Shiro在系统集群开发时安全的会话共享问题,系统在使用过程中会有大量的权限检查和用户身份检验动作,为了不频繁 ...

  4. 《Cortex-M0权威指南》之体系结构---系统模型

    转载请注明来源:cuixiaolei的技术博客 Cortex-M0体系结构包括:系统模型.存储器映射.异常中断.这篇文章主要讲解Cortex-M0的系统模型. 操作模式和状态 如上图所示,Cortex ...

  5. 为什么要引入zookeeper系统

    为什么要引入zookeeper系统?这篇文章将说明几个引入zookeeper的原因,首先,先对zookeeper做一个简单的介绍. zookeeper是hadoop下的一个子项目,它是一个针对大型分布 ...

  6. Zookeeper内部实现分布式数据一致性(底层系统模型)(一)

    Zookeeper的几个概念:(接下来将从这几个概念书写Zookeeper的内部工作流程) 数据模型 节点特性 版本 Watcher ACL <1> 数据模型: Zookeeper的视图很 ...

  7. Apache-Shiro+Zookeeper系统集群安全解决方案之会话管理

    如今的系统多不是孤军奋战,在多结点会话共享管理方面有着各自的解决办法,比如Session粘连,基于Web容器的各种处理等或者类似本文说的完全接管Web容器的Session管理,只是做法不尽相同. 而本 ...

  8. 【嵌入式开发】写入开发板Linux系统-模型S3C6410

    笔者 : 万境绝尘 转载请著名出处 最终拿到板子了, 嵌入式开发正式开启. 板子型号 : 三星 S3C6410 基于ARM11, 指令集基于arm6指令集; 为毛不是 Cortext A9的板子; 烧 ...

  9. PLECS_直流电机基本系统模型

    1.模型图 2.模型仿真结果 (1)Step阶跃t=1s,R=20Ω,V_dc = 120V,那么此时 电源电压波形: 电机电枢电流波形: 电机电磁转矩: 电机转速波形: (2)其他参数不变将R=30 ...

随机推荐

  1. 在maven项目中使用Junit进行单元测试(一)

    https://blog.csdn.net/ai_xue_xi/article/details/51819729 这篇文章相当的经典,最好使用的maven生成单元测试报告,不要在使用ant脚本生成单元 ...

  2. disruptor架构三 使用场景 使用WorkHandler和BatchEventProcessor辅助创建消费者

    在helloWorld的实例中,我们创建Disruptor实例,然后调用getRingBuffer方法去获取RingBuffer,其实在很多时候,我们可以直接使用RingBuffer,以及其他的API ...

  3. React实战教程之从零开始手把手教你使用 React 最新特性Hooks API 打造一款计算机知识测验App

    项目演示地址 项目演示地址 项目代码结构 前言 React 框架的优雅不言而喻,组件化的编程思想使得React框架开发的项目代码简洁,易懂,但早期 React 类组件的写法略显繁琐.React Hoo ...

  4. Python 简明教程 --- 8,Python 字符串函数

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 好代码本身就是最好的文档.当你需要添加一个注释时,你应该考虑如何修改代码才能不需要注释. -- St ...

  5. 深度学习“四大名著”发布!Python、TensorFlow、机器学习、深度学习四件套!

    Python 程序员深度学习的"四大名著": 这四本书着实很不错!我们都知道现在机器学习.深度学习的资料太多了,面对海量资源,往往陷入到"无从下手"的困惑出境. ...

  6. opencv+python实现图像锐化

    突然发现网上都是些太繁琐的方法,我就找opencv锐化函数咋这么墨迹. 直接上代码: kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], ...

  7. PID各环节的意义和功能,自带PID的matlab编程实例

    这是PID的标准形式包括比例/积分/微分三部分,e为偏差 下面我们分析三个环节的作用,设:当前系统状态A,目标状态B, e=B-A,初始状态e>0 (以下是个人的理解,欢迎读者评论) 1 比例环 ...

  8. mybatis源码配置文件解析之五:解析mappers标签

    在上篇博客中分析了plugins标签,<mybatis源码配置文件解析之四:解析plugins标签>,了解了其使用方式及背后的原理.现在来分析<mappers>标签. 一.概述 ...

  9. 前端走进机器学习生态,在 Node.js 中使用 Python

    这次给大家带来一个好东西,它的主要用途就是能让大家在 Node.js 中使用 Python 的接口和函数.可能你看到这里会好奇,会疑惑,会不解,我 Node.js 大法那么好,干嘛要用 Python ...

  10. 使用Visual Studio 开发SharePoint项目时的快捷键

    组合键:ctrl+c,alt+c,Shift+ctrl+c,可以快速的将文件拷贝到对应的部署目录下.