分布式小数据存储系统-初识ZooKeeper

纵然是瞬间 提交于 2019-12-17 05:32:50

初始需求

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

设计

单机层面

节点数据结构的选取

  • 树结构,每个节点是一个ZNode
  • 数据保存在内存中

优点:高效读写

为什么ZK不擅长存储大的数据?

单机高效写磁盘

高效写磁盘的两种方式:

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

ZK每次写磁盘,先申请固定大小的磁盘空间,之后再写磁盘,大大提升写入性能。

顺序写数据

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

高可用、宕机可恢复

快照+事务日志
什么时候记录事务日志?
如何快照?新起线程,不影响主流程

分布式层面

顺序写数据

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

如何保证数据强一致

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

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

如何提高读的性能

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

怎么保证读取的强一致?
客户端在调用前,可以先申请连接的主机同步leader数据,调用sync()方法 。

水平扩容

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

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

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


迭代需求

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

迭代需求实现

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

扩展:

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

ZooKeeper支持的特性

支持的特性

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

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

支持的特性包括:

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

几种应用场景的实现

负载均衡

基本原理是,每个应用的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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!