zoukankan      html  css  js  c++  java
  • Zookeeper的学习(一)

    zookeeper的概述

     

    ZooKeeper是一个分布式协调服务的开源框架。主要用来解决分布式集群中应用系统的一致性的问题,例如怎样避免同时操作同一数据造成脏读的问题。可用于服务发现,分布式锁,分布式领导选举,配置管理等。
    ZooKeeper本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树上的节点进行有效管理(提供了对于每个节点的监控和通知机制)。从而来维护和监控你存储的数据的状态变化。将通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。诸如:统一命名服务(dubbo)、分布式配置管理(solr的配置集中管理)、分布式消息队列(sub/pub)、分布式锁、分布式协调等功能。
    通俗的说法:
    zookeeper,英/'zuːkiːpə/,动物园管理员,将分布式系统比作动物园,那么Zookeeper就是用来管理分布式应用的。
    是一个开源的分布式应用的服务,提供了更高级别的服务,包括:同步、配置维护、分组和命名。主要目标:通过与标准文件系统一致的组织结果的命名空间,允许分布式进程之间可以进行同步。功能:提供了优质的高性能、高可用性、和严格有序的访问。

     
    zookeeper的特性
     
    1. 全局数据的一致:每个server保存一份相同的数据副本,client无论链接到哪个server,展示的数据都是一致的。
    2. 可靠性:如果消息被其中一台服务器接受,那么将被所有的服务器接受。
    3. 包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有server上消息a豆浆在消息b前被发布,偏序是指如果以个消息b在消息a后被同一个发送者发布,a必须将排在b前面。
    4. 数据更新原子性:一次数据更新要么成功(半数以上节点成功),要么失败,不存在中间状态。
    5. 实时性:ZooKeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。
     
    zookeeper的集群结构和角色
     
    Zookeeper集群是一个基于主从复制的高可用集群。集群数量为奇数,并且超过N+1台,只要集群中大多数节点都处于可用状态,那么集群就是可用的。
     
    Leader:

    ZooKeeper集群工作的核心

    1. 维护与各Follower及Observer之间的心跳。

    2. 完成读写的操作,完成后将写操作广播给其他服务器,只要有半数的节点(不包括Observer)写入成功,这个写的请求就会被提交。

    Follower:

    一个集群中可以同时存在多个Follower。

    1. 响应leader的心跳。

    2. 处理客户端的读请求。

    3. 将写的请求提交给Leader。

    4. 选举领导者时进行投票。

    Observer:

    角色跟Follower类似,作用是扩展系统,提高读取速度。

    1. 处理客户端的读请求。

    2. 将写的请求提交给Leader。

    3. 不参与投票。

    为了支持更多的客户端,需要增加更多的server,但是Server越多,投票阶段延迟越大,会影响性能,引入观察者,观察者不参与投票,多加入Observer节点,提高伸缩性,同时不影响吞吐率。

    总结:集群中由一个Leader节点和N个Follower节点组成,所有的Server之间能彼此通信,并且在各自内存中维护了状态图,与之一致的事务日志和快照位于持久化存储中。这一点类似Redis的Sentinel模式,它们都具有高度的自治能力,一旦Leader节点不可达,则自动完成新Leader的选举并恢复服务。

     
    zookeeper的数据模型
     
    数据结构图
    Zookeeper提供的命名空间与标准的文件系统非常类似,一个名称就是一个由“/”分割的路径序列,跟Linux的标准文件系统的结构是一致的;每一个节点就是一个路径。

    图中的每个节点称为一个Znode。每个Znode由三部分组成:

    1. stat:此为状态信息,描述该Znode的版本,权限等信息。

    2. data:与该Znode关联的数据。

    3. children:该Znode下的子节点。

    节点类型

    Znode有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。

    临时节点

    该节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,当然也可以手动删除。临时节点不允许拥有子节点。

    永久节点

    该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。

    序列化特性

    Znode还有一个序列化的特性,如果创建的时候指定的话,该Znode的名字后面会自动追加一个不断增加的序列号。序列号对于此节点的父节点来说是唯一的,这样便会记录每个子节点创建的先后顺序。他的格式为 “%10d”(10位数字,没有数值的数位用0补充,例如:0000000001)

    这样便会存在四种类型的Znode节点,分别对应:

    PERSISTENT:永久节点

    EPHEMERAL:临时节点

    PERSISTENT_SEQUENTIAL:永久节点、序列化

    EPHEMERAL_SEQUENTIAL:临时节点、序列化

    节点属性

    每个Znode都包含了一系列的属性,通过命令get,可以获得节点的属性。

    dataVersion:数据版本号,每次对节点进行set操作,dataVersion的值都会增加1(即使设置的是相同的数据),可有效避免了数据更新时出现的先后顺序问题。

    cversion:子节点的版本号。当Znode的子节点有变化时,cversion的值就会增加1。

    aclVersion:ACL的版本号。

    cZxid:Znode创建的事务id。

    mZxid:Znode被修改的事务id,即每次对Znode的修改都会更新mZxid。

    对于zk来说,每次的变化都会产生一个唯一的事务id,zxid(Zookeeper Transaction Id)。通过zxid,可以确定更新操作的先后顺序。例如,如果zxid1小于zxid2,说明zxid1操作先于zxid2发生,zxid对于整个zk都是唯一的,即使操作的是不同的Znode。

    ctime:节点创建时的时间戳。

    mtime:节点最新一次更新发生时的时间戳。

    ephemeralOwner:如果该节点为临时节点,ephemeralOwner值表示与该节点绑定的session id,如果不是,ephemeralOwner值为0。

    在client和server通信之前,首先需要建立连接,该连接称为session。连接建立后,如果发生连接超时、授权失败,或者显式关闭连接,连接便处于CLOSED状态,此时session结束。

    zookeeper的监控 

     

    Zookeeper支持监控,即客户端可以设置在某个znode上的监控;当一个节点发生变化时,就会触发和删除一个监控。当一个监控触发时,客户端会收到一个说明节点变化的包。

    当客户端和Zookeeper Server断开时,客户端将收到一个本地通知。
    本质就是:监听器(观察者模式)

    watcher

     

    watcher的工作流程

    客户端在向zookeeper服务器注册watcher的同时,会将watcher对象存储在客户端的watcherManager中,当zookeeper服务器触发watcher事件后,会向客户端发送通知,客户端线程从watchermanager中取出对应的watcher对象执行回调逻辑。

    watcher监听机制

    Watcher 监听机制是 Zookeeper 中非常重要的特性,我们基于 zookeeper 上创建的节点,可以对这些节点绑定监听事件。比如可以监听节点数据变更、节点删除、子节点状态变更等事件,通过这个事件机制,可以基于 zookeeper 实现分布式锁、集群管理等功能。

    Watcher特性

    Watcher具有一次性,无论是服务端还是客户端,一旦一个Watcher被触发,ZooKeeper都会将其从相应的存储中移除。因此Watcher需要反复注册。

    客户端串行执行:最终Watcher会被放入一个队列中串行执行。

    zookeeper的选举机制

     

    zookeeper默认的算法是FastLeaderElection,采用投票数大于半数则胜出的逻辑。

    概念

    服务器ID

    比如有三台服务器,编号分别为1,2,3。编号越大在选择算法中的权重越大。

    选举状态

    LOOKING:竞选状态。

    FOLLOWING:随从状态,同步leader状态,参与投票。

    OBSERVING:观察状态,同步leader状态,不参与投票。

    LEADING:领导者状态。

    数据ID

    服务器中存放的最新数据version。值越大说明数据越新,在选举算法中数据越新权重越大。

    逻辑时钟

    也叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的,每投完一次票这个数据就会增加,然后与接收到的其他服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。

    全新集群选举

    假设目前有5台服务器,每台服务器均没有数据,他们的编号分别是1,2,3,4,5 按编号依次启动,他们的选举过程如下:

    1. 服务器1启动,给自己投票,然后发投票信息,由于其他机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking。

    2. 服务器2启动,给自己投票,同时与之前启动得服务器1交换结果,由于服务器2得编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器得状态依然是Looking。

    3. 服务器3启动,给自己投票,同时与之前启动得服务器1,2交换信息,由于服务器3得编号最大,所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。

    4. 服务器4启动,给自己投票,同时与之前启动得服务器1,2,3交换信息,尽管服务器4得编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。

    5. 服务器5启动,后面的逻辑同服务器4成为小弟。

    非全新集群选举

    对于运行正常的zookeeper集群,中途有机器down掉,需要重新选举时,选举过程就需要加入数据ID,服务器ID和逻辑时钟。

    数据ID:数据新的 version就大,数据每次更新都会更新version。

    服务器ID:就是我们配置的myid中的值,每个机器一个。

    逻辑时钟:这个值从0开始递增,每次选举对应一个值。如果在同一次选举中,这个值是一致的。

    这样选举的标准就变成:

    1. 逻辑时钟小的选举结果被忽略,重新投票

    2. 统一逻辑时钟后,数据id大的胜出

    3. 数据id相同的情况下,服务器id大的胜出,根据这个规则选出leader。

    zookeeper的数据发布与订阅(配置中心)

    发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。

    应用在启动的时候会自动来获取一次配置,同时,在节点上注册一个Watcher,这样一来,以后每次配置有更新的时候,都会实时通知到订阅的客户端,从而达到获取最新配置信息的目的。

    比如:分布式搜索服务中,索引的元信息和服务器集群机器的节点状态存放在Zk的一些指定节点,供各个客户端订阅使用。

    注意:适合数据量很小的场景,这样数据更新可能会比较快。

    zookeeper的命名服务(Naming Service)

     

    在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。被命名的实体通常可以是集群中的机器,提供的服务地址,远程对象等等--这些我们都可以统称他们为名字(Name)。其中较为常见的就是一些分布式服务框架中的服务地址列表。通过调用ZK提供的创建节点的API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。

    阿里巴巴集团开源的分布式服务框架Dubbo中使用Zookeeper来作为其命名服务,维护全局的服务地址列表。

    zookeeper的分布式锁

     

    分布式锁,这个主要得益于zookeeper保证了数据的强一致性。锁服务可以分为两类,一个是保持独占,另一个是控制时序。

    所谓保持独占,就是所有试图来获取这个锁的客户端,最终只有一个可以成功获得这把锁,通常的做法是把zk上的一个znode看作是一把锁,通过create znode的方式来实现。所有客户端都去创建/distribute_lock节点,最终成功创建的那个客户端也即拥有了这把锁。

    参考:
    1. 黑马程序员官方视频,如有侵权,联系必删,本文章只用于个人学习。
    2. https://blog.csdn.net/wojiushiwo945you/article/details/86579061
    3. https://www.jianshu.com/p/5b40286a7854
    4. https://blog.csdn.net/hanxintong9/java/article/details/52679320
    5. https://blog.csdn.net/lzb348110175/java/article/details/97643514
    持续更新!!
     
  • 相关阅读:
    vue-router 实践
    修改vue中<router-link>的默认样式
    JSON.parse() 与 JSON.stringify() 的区别
    JS 中的异步操作
    CSS3 box-sizing:border-box的好处
    element ui 栅格布局
    css overflow用法
    koa中间件机制
    canvas 入门
    前端面试题:淘宝首页用了多少种标签
  • 原文地址:https://www.cnblogs.com/flyinghome/p/12320271.html
Copyright © 2011-2022 走看看