ZooKeeper-分布式小文件存储系统--构建精致的应用

什么是ZooKeeper

ZooKeeper的目录结构

ZooKeeper-分布式小文件存储系统--构建精致的应用_第1张图片

基本操作

ZooKeeper-分布式小文件存储系统--构建精致的应用_第2张图片
命令:
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit

参考:
Zookeeper 环境搭建&zk命令详解


ZooKeeper实现目标&单机版ZooKeeper能实现什么

  • 元数据的存储(小数据 1M内数据的存储)
  • 分布式、高可用
  • 读多写少、高性能读
  • 有序访问

ZooKeeper设计

单机层面

节点数据结构的选取

  • 树结构,每个节点是一个ZNode,数据结构选取树结构,更均衡快速的查找、删除(ZNode保存)
  • 数据保存在内存中,快速读(ZKDatabase)

ZNode结构:
ZooKeeper-分布式小文件存储系统--构建精致的应用_第3张图片

优点:高效读写

为什么ZK不擅长存储大的数据?
需要多次访问磁盘块,IO耗时严重

单机高效写磁盘

乐观锁
version 数据节点内容版本号
cversion 数据节点子节点版本号
aversion ACL变更版本号

高效写磁盘的两种方式:

  • 顺序写磁盘
  • 预分配磁盘空间

ZK每次写磁盘,先申请固定大小的磁盘空间,之后再写磁盘,大大提升写入性能。写的时候预先申请固定大小的磁盘空间(64M),这样能高效顺序写磁盘,再把数据序列化后写入磁盘。可以先写数据到磁盘缓冲区oscache中,再定期刷到磁盘中。

乐观锁保证原子操作,并发写只有一个能成功,且能支持顺序生成一个数据,如ID。写的时候,先写事务日志,在写ZKDatabase,保证数据不丢失。

顺序写数据

每次写入操作,ZooKeeper会附加一个数字标签,表明ZooKeeper中的事务顺序

高可用、宕机可恢复

快照+事务日志
什么时候记录事务日志?事务操作时,先记录事务日志,再操作内存ZKDatabase。
如何快照?新起线程,不影响主流程

内存数据 ZKDatabase DataTree DataNode nodes

事务日志 写入时申请磁盘64M 写入文件流后 配置是否刷盘

数据快照 可配置多少次事务日志记录后进行一次快照,启动单独异步线程dump数据,不影响主线程的任务。

宕机后重启,初始化 -> 尝试加载最近的100个快照,逐一加载,无法成功则报错 -> 加载未加载到的事务日志

分布式层面

顺序写数据

一主多从结构,只有一台master服务器对外提供写服务,每次写记录ZXID事务Id。原子写,保证mei yo

如何保证数据强一致

写的策略,半数以上机器写成功后返回。
写数据流程,非Leader节点会把请求转发给leader,写成功后leader再通知该节点。

ZAB(Zookeeper Atomic Broadcast)协议:初始阶段/宕机恢复(原子广播)

ZAB(Zookeeper Atomic Broadcast)协议?
ZAB协议的核心定义了对于那些改变ZooKeeper服务器数据状态的事务请求的处理方式。即,所有事务请求必须有一个全局唯一的服务器来协调处理,这样的服务器被称为Leader服务器,而余下的其他服务器则成为Follower服务器。Leader服务器负责将一个客户端事务请求转换成一个事务Proposal(提议),并将该Proposal分发给集群中所有的Follower服务器。之后Leader服务器需要等待所有Follower服务器的反馈,一旦超过半数的Follower服务器进行了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求其将前一个Proposal进行提交。(ZooKeeper中还有一种机器角色:Observer。Observer机器不参与Leader选举过程,也不参与写操作的“过半写成功”策略,因此Observer可以在不影响写性能的情况下提升集群的读性能)

Zookeeper 的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主-Leader选举)和广播模式(数据同步)。

Leader选举–初始阶段/宕机恢复(原子广播)如何进行?

  • 每个Server发出一个投票
  • 集群的每个服务器接受来自各个服务器的投票
  • 集群中的每个服务器处理投票,发现收到的zxid,myid比自己投的大,重新投大的那种票
  • 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息
  • 改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING

数据同步
Leader服务器会为每一个Follower服务器准备一个队列,并将那些没有被各个Follower服务器同步的事务以Proposal的形式逐条发给各个Follower服务器,并在每一个Proposal后都紧跟一个commit消息,表示该事务已经被提交,档follower服务器将所有尚未同步的事务proposal都从leader服务器同步过来并成功应用到本地后,leader服务器就会将该follower加入到真正可用的follower列表中。

  • 第一阶段-准leader生成初始事务集合(更新leader 周期epoch,ZXID 高32 位是leader 周期epoch,低32 位是递增计数器)
  • 第二阶段-正式同步(准leader 将epoch 与 初始化事务集合发送给集群中过半的follower,数据同步时分为,全量同步 直接差异化同步 先回滚再差异化同步 仅回滚同步 四种策略)

如何提高读的性能

follower节点,observer节点都可以对外提供读数据能力

客户端读的时候,需要最新的数据?
客户端在调用前,可以先申请连接的主机同步leader数据,调用sync()方法 ,之后再读取数据。

水平扩容

ZK做的不好的地方。
ZooKeeper 在水平扩容扩容方面做得并不十分完美,需要进行整个集群的重启。通常有两种重启方式,一种是集群整体重启,另外一种是逐台进行服务器的重启。

整体重启
  所谓集群整体重启,就是先将整个集群停止,然后更新 ZooKeeper 的配置,然后再次启动。如果在你的系统中,ZooKeeper 并不是个非常核心的组件,并且能够允许短暂的服务停止(通常是几秒钟的时间间隔),那么不妨选择这种方式。在整体重启的过程中,所有该集群的客户端都无法连接上集群。等到集群再次启动,这些客户端就能够自动连接上——注意,整体启动前建立起的客户端会话,并不会因为此次整体重启而失效。也就是说,在整体重启期间花费的时间将不计入会话超时时间的计算中。

逐台重启
  这种方式更适合绝大多数的实际场景。在这种方式中,每次仅仅重启集群中的一台机器,然后逐台对整个集群中的机器进行重启操作。这种方式可以在重启期间依然保证集群对外的正常服务。


迭代需求

  • 支持订阅发布
  • 分布式ID
  • 权限控制

迭代需求实现

  • 节点增加watcher
  • 节点类型增加顺序节点创建
  • 节点访问权限控制

Watcher(实现较简单,可以改进)

  • 一次性 减轻服务端压力
  • 客户端串行执行 不能一直反复注册watcher或者耗时watcher
  • 轻量 只告诉客户端发生了事件,不告诉具体内容

ACL

  • UGO user group others权限控制机制
  • 自带ip 账号密码Digest模式权限控制 支持超级管理员
  • 支持自定义扩展

扩展:

  • 节点增加持久节点/临时节点(更好的实现会话连接断开后,节点的销毁)

ZooKeeper支持的特性

支持的特性

Zookeeper的两大特性(节点特性和watcher机制):

  • 客户端如果对Zookeeper的数据节点注册Watcher监听,那么当该数据及诶单内容或是其子节点列表发生变更时,Zookeeper服务器就会向订阅的客户端发送变更通知。
  • 对在Zookeeper上创建的临时节点,一旦客户端与服务器之间的会话失效,那么临时节点也会被自动删除。
    ZooKeeper是一个典型的发布/订阅模式的高可用的分布式数据管理与协调框架,开发人员可以使用它来进行分布式数据的发布与订阅。另一方面,通过对ZooKeeper中丰富的数据节点类型进行交叉使用,配合Watcher通知机制,利用这两大特性,可以非常方便地构建一系列分布式应用中都会涉及的核心功能

支持的特性包括:

  • 数据发布/订阅
  • 负载均衡
  • 命名服务
  • 分布式协调/通知
  • 集群管理
  • Master选举
  • 分布式锁
  • 分布式队列

谁在用ZooKeeper

  • Hadoop

  • Hbase

  • Kafka

  • Dubbo

  • JStorm

几种应用场景的实现

负载均衡

基本原理是,每个应用的Server启动时创建一个EPHEMERAL(临时)节点,应用客户端通过读取节点列表获得可用服务器列表,并订阅节点事件,有Server宕机断开时触发事件,客户端监测到后把该Server从可用列表中删除

命名服务

基本原理:通过调用Zookeeper节点创建的API接口就可以创建一个顺序节点,并且在API返回值中会返回这个节点的完整名字,利用此特性,可以生成全局ID,其步骤如下

  • 1.客户端根据任务类型,在指定类型的任务下通过调用接口创建一个顺序节点,如"job-"。
  • 2.创建完成后,会返回一个完整的节点名,如"job-00000001"。
  • 3.客户端拼接type类型和返回值后,就可以作为全局唯一ID了,如"type2-job-00000001"。

Master选举

基本原理,利用Zookeeper的一致性,能够很好地保证在分布式高并发情况下节点的创建一定能够保证全局唯一性,即Zookeeper将会保证客户端无法重复创建一个已经存在的数据节点(由其分布式数据的一致性保证)。
首先创建/master_election/2016-11-12节点,客户端集群每天会定时往该节点下创建临时节点,如/master_election/2016-11-12/binding,这个过程中,只有一个客户端能够成功创建,此时其变成master,其他节点都会在节点/master_election/2016-11-12上注册一个子节点变更的Watcher,用于监控当前的Master机器是否存活,一旦发现当前Master挂了,其余客户端将会重新进行Master选举

分布式锁

实现方案:

  • 1、客户端调用create接口常见类似于/shared_lock/[Hostname]-请求类型-序号的临时顺序节点。
  • 2、客户端调用getChildren接口获取所有已经创建的子节点列表(不注册任何Watcher)。
  • 3、如果无法获取共享锁,就调用exist接口来对比自己小的节点注册Watcher。对于读请求:向比自己序号小的最后一个写请求节点注册Watcher监听。对于写请求:向比自己序号小的最后一个节点注册Watcher监听。
  • 4、等待Watcher通知,继续进入步骤2。
    此方案改动主要在于:每个锁竞争者,只需要关注/shared_lock节点下序号比自己小的那个节点是否存在即可。

分布式队列

① FIFO先入先出,先进入队列的请求操作先完成后,才会开始处理后面的请求。FIFO队列就类似于全写的共享模型,所有客户端都会到/queue_fifo这个节点下创建一个临时节点,如/queue_fifo/host1-00000001。

创建完节点后,按照如下步骤执行。

  • 1、通过调用getChildren接口来获取/queue_fifo节点的所有子节点,即获取队列中所有的元素。
  • 2、确定自己的节点序号在所有子节点中的顺序。
  • 3、如果自己的序号不是最小,那么需要等待,同时向比自己序号小的最后一个节点注册Watcher监听。
  • 4、接收到Watcher通知后,重复步骤1。

② Barrier分布式屏障,最终的合并计算需要基于很多并行计算的子结果来进行,开始时,/queue_barrier节点已经默认存在,并且将结点数据内容赋值为数字n来代表Barrier值,之后,所有客户端都会到/queue_barrier节点下创建一个临时节点,例如/queue_barrier/host1。
创建完节点后,按照如下步骤执行。

  • 1、通过调用getData接口获取/queue_barrier节点的数据内容,如10。
  • 2、通过调用getChildren接口获取/queue_barrier节点下的所有子节点,同时注册对子节点变更的Watcher监听。
  • 3、统计子节点的个数。
  • 4、如果子节点个数还不足10个,那么需要等待。
  • 5、接受到Wacher通知后,重复步骤3

从ZooKeeper的设计中可以学到什么

  • 如何保证原子性/处理并发(乐观锁)
  • 如何提高单机写的性能(预申请磁盘/顺序写磁盘)
  • 如何保证宕机后可恢复(事务日志+快照)
  • 如何快速操作数据(内存操作/树+Map)
  • 如何保证分布式一致性写入(过半策略+单机写)
  • 如何提升读的性能(读写分离/可伸缩性)
  • 如何便于扩展(模块独立+简洁设计+支持自定义扩展)
  • 如何做到精致(精确定位实现目标+追求自己实现的每个独立功能做到极致+提供基础及扩展能力+交给客户端来做性价比不高的功能)

Zookeeper vs Etcd

功能对比

ZooKeeper-分布式小文件存储系统--构建精致的应用_第4张图片

应用场景对比

ZooKeeper-分布式小文件存储系统--构建精致的应用_第5张图片

Zookeeper 和 Etcd 都是非常优秀的分布式协调系统,zookeeper 起源于 Hadoop 生态系统,etcd 的流行是因为它是 kubernetes 的后台支撑。

ZooKeeper

优点

  • 非阻塞全部快照(达成最终一致)
  • 高效的内存管理
  • 高可靠
  • API 简单
  • 连接管理可以自动重试
  • ZooKeeper recipes 的实现是经过完整良好的测试的。
  • 有一套框架使得写新的 ZooKeeper recipes 非常简单。
  • 支持监听事件
  • 发生网络分区时,各个区都会开始选举 leader,那么节点数少的那个分区将会停止运行。

缺点

  • zookeeper 是 java 写的,那么自然就会继承 java 的缺点,例如 GC 暂停。
  • 如果开启了快照,数据会写入磁盘,此时 zookeeper 的读写操作会有一个暂时的停顿。
  • 对于每个 watch 请求,zookeeper 都会打开一个新的 socket 连接,这样 zookeeper 就需要实时管理很多 socket 连接,比较复杂。

Etcd

特性:

  • 原子性
  • 一致性
  • 顺序一致性
  • 可串行化级别
  • 高可用
  • 可线性化

优点:

  • 支持增量快照,避免了 zookeeper 的快照暂停问题
  • 堆外存储,没有垃圾回收暂停问题
  • 无需像 zookeeper 那样为每个 watch 都做个 socket 连接,可以复用
  • zookeeper 每个 watch 只能收到一次事件通知,etcd 可以持续监控,在一次 watch 触发之后无需再次设置一次 watch
  • zookeeper 会丢弃事件,etcd3 持有一个事件窗口,在 client 断开连接后不会丢失所有事件

缺点

  • 如果超时,或者 client 与 etcd 网络中断,client 不会明确的知道当前操作的状态
  • 在 leader 选举时,etcd 会放弃操作,并且不会给 client 发送放弃响应
  • 在网络分区时,当 leader 处于小分区时,读请求会继续被处理


参考:
Java Web分布式篇之ZooKeeper
初识ZooKeeper
Zookeeper vs Etcd
zookeeper与etcd的对比

你可能感兴趣的:(Java,Web知识总结)