数据模型

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. Django REST Framework(一) Serializer和ModelSerializer

    REST Framework概述 Django REST framework是一套基于Django的REST框架,是一个用于构建Web API的功能强大且灵活的工具包. 1.RESTful 简述Rep ...

  2. Python3-算法-递归

    递归 递归算法是指一般通过函数或子进程来实现,在函数或子进程的内部,直接或间接地调用自己的算法,说白了就是自己调自己 注: 1.在使用递归的时候,必须要有一个递归出口,即一个明确的结束条件,否则就是死 ...

  3. Java基础:变量、常量、作用域

    JAVA基础:变量.常量.作用域 变量:可以变化的量.Java是强类型语言,每个变量都必须声明类型. Java变量是程序中最基本的存储单元,要素包括变量名,类型和作用域. //类变量 static s ...

  4. MyEclipse中项目利用不同版本JDK编译注意点

    一.发现问题本地用jdk.1.6编译,可服务器上市jdk1.5,其他服务器甚至是jdk1.4,不少语法和特性不一样.该服务器的jdk版本一般不现实因为是还有许多其他项目只有将本地安装上jdk1.5. ...

  5. 【部分】ASP.NET MVC5 - 地址栏传参两种方法

    地址栏传参两种方法 1-  Home/Index/88       (后台控制器读取需要一样的参数名称) 2-  Home/Index?id1=88?id2=99    (Request三种接受方法) ...

  6. JavaScript基础JavaScript的常用编码惯例(007)

    采用一定的编码惯例,可以使得项目中的代码提到较高的一致性,可读性和可预测性. 1.缩进缩 进可以提高代码的可读性.不过错误的缩进也可能导致代码的误读.有人认为缩进应该使用tab,另外的一些人主张采用4 ...

  7. 第五天.权限批量录入/更新、信号、Django日志配置

    1. 角色.菜单.权限的增删该查 1. ModelForm增删改查 2. 增加和编辑使用同一个视图函数 Form() 1. 生成获取用户输入的标签 2. 对用户输入的内容做校验 3. 保留输入的内容同 ...

  8. scrapy框架携带cookie访问淘宝购物车

    我们知道,有的网页必须要登录才能访问其内容.scrapy登录的实现一般就三种方式. 1.在第一次请求中直接携带用户名和密码. 2.必须要访问一次目标地址,服务器返回一些参数,例如验证码,一些特定的加密 ...

  9. postman 进阶技巧

    cookie 清除缓存 code 生成接口自动化测试脚本 响应部分 pretty 响应以json或xml显示 raw 响应以文本显示 preview 以HTML网页行驶显示 断言 断言:用于判断接口请 ...

  10. 还能这么玩?用VsCode画类图、流程图、时序图、状态图...不要太爽!

    文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 软件设计中,有好几种图需要画,比如流程图.类图.组件图等,我知道大部分 ...