Zookeeper做为一个分布式协调系统提供了一项基本服务:分布式锁服务,分布式锁是分布式协调技术实现的核心内容。像配置管理、任务分发、组服务、分布式消息队列、分布式通知/协调等,这些应用实际上都是基于这项基础服务由用户本身摸索出来的。node
zookeeper做为分布式协调系统在大数据领域很是经常使用,它是一个很好的中心化管理工具。下面举几个常见的应用场景。算法
HA(分布式锁的应用):Master挂掉以后迅速切换到slave节点。数据库
任务发布:regionserver挂了一台,master须要从新分配region,会把任务放在zookeeper等regionserver来获取服务器
任务分配:给topic分配partitions和replication网络
ZooKeeper命名空间中的Znode,兼具文件和目录两种特色。既像文件同样维护着数据、元信息、ACL、时间戳等数据结构,又像目录同样能够做为路径标识的一部分。 每一个Znode由3部分组成:数据结构
ZooKeeper中的每一个节点存储的数据要被原子性的操做。也就是说读操做将获取与节点相关的全部数据,写操做也将替换掉节点的全部数据。另外,每个节点都拥有本身的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点能够执行的操做。架构
ZooKeeper能够为全部的读操做设置watch,包括:exists()、getChildren()及getData()。当节点状态发生改变时(Znode的增、删、改)将会触发watch所对应的操做。当watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知,由于watch只能被触发一次,这样能够减小网络流量。分布式
孩子watch(child watches):getChildren负责设置孩子watch工具
永久节点:该节点的生命周期不依赖于会话,而且只有在客户端显示执行删除操做的时候,他们才能被删除。性能
两种方式:
存储集群元数据提供给client使用,体如今好比须要对HBase和Kafka操做时,都会直接连到zookeeper,zookeeper记录了数据存储的位置,存活的节点等元数据信息。
Master要监视/works和/tasks两个永久节点,以便能感知到由哪些slave当前可用,当前有新任务须要分配。
分配过程:在/assign下建立当前可用的workA,找到须要分配的taskA,建立/assign/workA/taskA
zookeeper做为一个分布式协调系统,不少组件都会依赖它,那么此时它的可用性就很是重要了,那么保证可用性的同时做为分布式系统的它是怎么保证扩展性的?问题不少,读完接下来的内容你会有答案。
上图来自zookeeper的官方文档,我解释下这张图的各个角色(observer在上图中能够理解为特殊的follower)
角色 | 分工 | 数量 |
---|---|---|
client客户端 | 请求发起方 | 不限 |
observer观察者 | 接受用户读写请求,写转发给leader,读直接返回(选主过程不参加投票) | 不限 |
follower跟随者 | 接受用户读写请求,写转发给leader,读直接返回(选主过程参加投票) | 奇数个(不可过多) |
leader领导者 | 负责提议,更新系统状态 | 1个 |
另外:follower和observer同时均为learner(学习者)角色,learner的分工是同步leader的状态。
zookeeper的各个复制集节点(follower,leader,observer)都包含了集群全部的数据且存在内存中,像个内存数据库。更新操做会以日志的形式记录到磁盘以保证可恢复性,而且写入操做会在写入内存数据库以前序列化到磁盘。
每一个ZooKeeper服务器都为客户端服务。客户端只链接到一台服务器以提交请求。读取请求由每一个服务器数据库的本地副本提供服务。更改服务状态,写请求的请求由zab协议处理。
做为协议协议的一部分,来自客户端的全部写入请求都被转发到称为leader的单个服务器。其他的ZooKeeper服务器(称为followers)接收来自领导者leader的消息提议并赞成消息传递。消息传递层负责替换失败的leader并将followers与leader同步。
ZooKeeper使用自定义原子消息传递协议zab。因为消息传递层是原子的,当领导者收到写入请求时,它会计算应用写入时系统的状态,并将其转换为捕获此新状态的事务。
cap原则是指做为一个分布式系统,一致性,可用性,分区容错性这三个方面,最多只能任意选择两种。就是一定会要有取舍。
Zookeeper是强一致性系统,同步数据很快。可是在不用sync()操做的前提下没法保证各节点的数据彻底一致。zookeeper为了保证一致性使用了基于paxos协议且为zookeeper量身定作的zab协议。这两个协议是什么东西以后的文章会讲。
Zookeeper数据存储在内存中,且各个节点均可以相应读请求,具备好的响应性能。Zookeeper保证了可用性,数据老是可用的,没有锁.而且有一大半的节点所拥有的数据是最新的,实时的。
有2点须要分析的
严格地意义来说zk把取舍这个问题抛给了开发者即用户。
为了协调CA(一致性和可用性),用户能够本身选择是否使用Sync()操做。使用则保证全部节点强一致,可是这个操做同步数据会有必定的延迟时间。反过来若不是必须保证强一致性的场景,可不使用sync,虽然zookeeper同步的数据很快,可是此时是没有办法保证各个节点的数据必定是一致的,这一点用户要注意。实际的开发中就要开发者根据实际场景来作取舍了,看更关注一致性仍是可用性。
为了协调AP(一致性和扩展性),用户能够本身选择是否添加obsever以及添加个数,observer是3.3.0 之后版本新增角色,它不会参加选举和投票过程,目的就是提升集群扩展性。由于follower的数量不能过多,follower须要参加选举和投票,过多的话选举的收敛速度会很是慢,写数据时的投票过程也会好久。observer的增长能够提升可用性和扩展性,集群可接受client请求的点多了,可用性天然会提升,可是一致性的问题依然存在,这时又回到了上面CA的取舍问题上。
FastLeaderElection原理
每一个Zookeeper服务器,都须要在数据文件夹下建立一个名为myid的文件,该文件包含整个Zookeeper集群惟一的ID(整数)。例如某Zookeeper集群包含三台服务器,hostname分别为zoo一、zoo2和zoo3,其myid分别为一、2和3,则在配置文件中其ID与hostname必须一一对应,以下所示。在该配置文件中,server.后面的数据即为myid
server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
相似于RDBMS中的事务ID,用于标识一次更新操做的Proposal ID。为了保证顺序性,该zkid必须单调递增。所以Zookeeper使用一个64位的数来表示,高32位是Leader的epoch,从1开始,每次选出新的Leader,epoch加一。低32位为该epoch内的序号,每次epoch变化,都将低32位的序号重置。这样保证了zkid的全局递增性。
可经过electionAlg配置项设置Zookeeper用于领导选举的算法。
到3.4.10版本为止,可选项有
0 基于UDP的LeaderElection 1 基于UDP的FastLeaderElection 2 基于UDP和认证的FastLeaderElection 3 基于TCP的FastLeaderElection
在3.4.10版本中,默认值为3,也即基于TCP的FastLeaderElection。另外三种算法已经被弃用,而且有计划在以后的版本中将它们完全删除而再也不支持。
FastLeaderElection选举算法是标准的Fast Paxos算法实现,可解决LeaderElection选举算法收敛速度慢的问题。
服务器状态
OBSERVING 观察者状态。代表当前服务器角色是Observer,与Folower惟一的不一样在于不参与选举,也不参与集群写操做时的投票
vote_zxid 被推举的服务器上所保存的数据的最大zxid
自增选举轮次
Zookeeper规定全部有效的投票都必须在同一轮次中。每一个服务器在开始新一轮投票时,会先对本身维护的logicClock进行自增操做。
每一个服务器在广播本身的选票前,会将本身的投票箱清空。该投票箱记录了所收到的选票。例:服务器2投票给服务器3,服务器3投票给服务器1,则服务器1的投票箱为(2, 3), (3, 1), (1, 1)。票箱中只会记录每一投票者的最后一票,如投票者更新本身的选票,则其它服务器收到该新选票后会在本身票箱中更新该服务器的选票。
每一个服务器最开始都是经过广播把票投给本身。
服务器会尝试从其它服务器获取投票,并记入本身的投票箱内。若是没法获取任何外部投票,则会确认本身是否与集群中其它服务器保持着有效链接。若是是,则再次发送本身的投票;若是否,则立刻与之创建链接。
收到外部投票后,首先会根据投票信息中所包含的logicClock来进行不一样处理
外部投票的logicClock大于本身的logicClock。说明该服务器的选举轮次落后于其它服务器的选举轮次,当即清空本身的投票箱并将本身的logicClock更新为收到的logicClock,而后再对比本身以前的投票与收到的投票以肯定是否须要变动本身的投票,最终再次将本身的投票广播出去。
外部投票的logicClock小于本身的logicClock。当前服务器直接忽略该投票,继续处理下一个投票。
外部投票的logickClock与本身的相等。当时进行选票PK。
选票PK是基于(self_id, self_zxid)与(vote_id, vote_zxid)的对比
外部投票的logicClock大于本身的logicClock,则将本身的logicClock及本身的选票的logicClock变动为收到的logicClock
若logicClock一致,则对比两者的vote_zxid,若外部投票的vote_zxid比较大,则将本身的票中的vote_zxid与vote_myid更新为收到的票中的vote_zxid与vote_myid并广播出去,另外将收到的票及本身更新后的票放入本身的票箱。若是票箱内已存在(self_myid, self_zxid)相同的选票,则直接覆盖
若两者vote_zxid一致,则比较两者的vote_myid,若外部投票的vote_myid比较大,则将本身的票中的vote_myid更新为收到的票中的vote_myid并广播出去,另外将收到的票及本身更新后的票放入本身的票箱
若是已经肯定有过半服务器承认了本身的投票(多是更新后的投票),则终止投票。不然继续接收其它服务器的投票。
投票终止后,服务器开始更新自身状态。若过半的票投给了本身,则将本身的服务器状态更新为LEADING,不然将本身的状态更新为FOLLOWING
《每日五分钟搞定大数据》原创系列,每周不定时更新。评论不能及时回复可直接加公众号提问或交流,知无不答,谢谢 。