什么?你还不了解ZooKeeper?

作者: 公众号_WU双 | 来源:发表于2019-01-11 15:18 被阅读3次

    1 前言

    谈到分布式应用,就离不开ZooKeeper,那么ZooKeeper是怎么来的,它又是做什么的?通过这篇文章,希望大家最ZooKeeper有个基本的了解。
    本篇文章主要介绍ZooKeeper的基本概念及其在并发情况下的读写流程分析,希望能对大家有所帮助!

    2 ZooKeeper简介

    2.1 背景

    众所周知,分布式应用程序都需要一个协调服务,那么为什么分布式应用程序需要协调服务呢?因为分布式应用程序是分布在多台主机上面的,分布在多台主机上面的应用要想共同地去很好地完成任务,当然得需要一个协调者了,ZooKeeper就是这样一个协调者。其实分布式应用程序就跟团队一样,团队要想高效的完成任务,当然需要一个协调者去协调各项工作了,这个协调者就是团队Leader啦。
    我们都知道协调者不是这么好当的,对于分布式应用程序来说也是如此,协调服务很容易出现竞态条件、死锁等问题。为了减少分布式应用程序开发协调服务的成本,所以就诞生了ZooKeeper——开源的分布式协调服务。

    2.2 基本概念

    ZooKeeper 是一个开源的分布式协调服务,由雅虎创建,是 Google Chubby 的开源实现。分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅、命名服务、分布式协调/通知、集群管理、配置维护、分布式锁等功能。

    2.3 特点

    1、简单

    分布式应用程序可以通过一个共享的层级命名空间(类似标准的文件系统)来进行协调,该层级命名空间由数据节点构成——在ZooKeeper中我们称它为znode,znode类似标准文件系统中的文件或是目录。不同于标准文件系统,ZooKeeper中的数据是存在在内存中的,所以ZooKeeper可以实现高吞吐量和低延迟。

    2、基于复制

    ZooKeeper集群中的server是互相复制的,所以集群中的每个server数据都是一样的,正因如此,我们可以通过任意server来读取数据。


    ZooKeeper集群中的主机是互相复制的

    集群中的server必须能够互相感知,它们拥有相同的状态。客户端可以连接任意server,然后与server保持着TCP连接,通过此连接可以发送请求、获得响应,当此连接断掉的时候,客户端会重新连接一个不同的server。
    集群中每一个server都可以服务客户端,读请求通过每个server的本地数据副本来提供,写请求则由ZooKeeper协议来处理。作为协议的一部分,所有的写请求都会被转发到集群中一个叫做Leader的节点来处理,集群中的其余节点则称为Follower,Follower用来接收并处理来自Leader的提案。Leader宕机重新选举与Follower与Leader的同步也都是此协议的内容,感兴趣可以搜索ZooKeeper zab协议进一步了解。

    3、有序性

    ZooKeeper通过为每一个更新操作编号来保证事务有序性。

    4、快速

    ZooKeeper在读多写少的场景中,拥有极快的速度。

    2.4 数据模型

    ZooKeeper的数据模型是层次型,类似文件系统。


    ZooKeeper的数据模型

    每个数据节点,在ZooKeeper中叫做znode,并且其有一个唯一的路径标识,znode既可以包含数据也可以包含子节点,好比文件系统中允许文件也作为目录一样。znode中数据的读写都是原子的。
    ZooKeeper中也有临时节点的概念,只要创建临时节点的会话存在,该节点就存在,会话一结束,那么该节点就会被删除。

    2.5 ZooKeeper的一些保证

    作为构建复杂分布式服务的基础,ZooKeeper提供了一些保证,利用这些保证,我们可以保证应用的正确性:

    • 顺序一致性:来自同一客户端的更新将按顺序进行。
    • 原子性:更新操作要么成功要么失败,不存在中间状态。
    • 单系统映像:不论连接到哪个服务器,客户端都会看到相同的视图,因为ZooKeeper是基于复制的嘛。
    • 可靠性:更新操作成功后,znode状态会一直持续到下次更新操作,在此期间,状态不变。
    • 及时性:客户端会及时获取ZooKeeper最新状态。

    3 ZooKeeper的读写流程分析

    在了解了ZooKeeper的基本概念后,我们来详细看下ZooKeeper的读写流程,以及ZooKeeper在并发情况下的读写控制。以求对ZooKeeper有进一步的了解。

    3.1 读流程分析

    读流程如下图所示:


    ZooKeeper读流程

    因为ZooKeeper集群中所有的server节点都拥有相同的数据,所以读的时候可以在任意一台server节点上,客户端连接到集群中某一节点,读请求,然后直接返回。当然因为ZooKeeper协议的原因(一半以上的server节点都成功写入了数据,这次写请求便算是成功),读数据的时候可能会读到数据不是最新的server节点,所以比较推荐使用watch机制,在数据改变时,及时感应到。

    3.2 写流程分析

    写流程如下图所示:


    ZooKeeper写流程

    当一个客户端进行写数据请求时,会指定ZooKeeper集群中的一个server节点,如果该节点为Follower,则该节点会把写请求转发给Leader,Leader通过内部的协议进行原子广播,直到一半以上的server节点都成功写入了数据,这次写请求便算是成功,然后Leader便会通知相应Follower节点写请求成功,该节点向client返回写入成功响应。

    3.3 ZooKeeper并发读写情况分析

    我们已经知道ZooKeeper的数据模型是层次型,类似文件系统,不过ZooKeeper的设计目标定位是简单、高可靠、高吞吐、低延迟的内存型存储系统,因此它的value不像文件系统那样适合保存大的值,官方建议保存的value大小要小于1M,key为路径。

    ZooKeeper的数据模型

    每个数据节点在ZooKeeper中叫做znode,并且其有一个唯一的路径标识,znode节点可以包含数据和子节点。

    ZooKeeper的层次模型是通过ConcurrentHashMap实现的,key为path,value为DataNode,DataNode保存了znode中的value、children、 stat等信息。

    ZooKeeper的层次模型是通过ConcurrentHashMap实现的,而ConcurrentHashMap是线程安全的Hash Table,它采用了锁分段技术来减少锁竞争,提高性能的同时又保证了并发安全,其结构如下图所示:

    ConcurrentHashMap结构

    ConcurrentHashMap由两部分组成,Segment和HashEntry,锁的粒度是Segment,每个Segment 对象包含整个散列映射表的若干个桶,散列冲突时通过链表来解决。

    因为插入键 / 值对操作只是在 Segment 包含的某个桶中完成,所以这里的加锁操作是针对(键的 hash 值对应的)某个具体的 Segment,不需要锁定整个ConcurrentHashMap,所以对于ConcurrentHashMap,可以进行并发的写操作,只要写入的Segment不同。而所有的读线程几乎不会因为写操作的加锁而阻塞(除非读线程刚好读到这个 Segment 中某个 HashEntry 的 value 域的值为 null,此时需要加锁后重新读取该值。这便是锁分段技术,保证并发安全的情况下又提高了性能。

    对于ZooKeeper来讲,ZooKeeper的写请求由Leader处理,Leader能够保证并发写入的有序性,即同一时刻,只有一个写操作被批准,然后对该写操作进行全局编号,最后进行原子广播写入,所以ZooKeeper的并发写请求是顺序处理的,而底层又是用了ConcurrentHashMap,理所当然写请求是线程安全的。而对于并发读请求,同理,因为用了ConcurrentHashMap,当然也是线程安全的了。总结来说,ZooKeeper的并发读写是线程安全的。
    但是对于ZooKeeper的客户端来讲,如果使用了watch机制,在进行了读请求但是watch znode前这段时间中,如果znode的数据变化了,客户端是无法感知到的,这段时间客户端的数据就有一定的滞后性了,只有当下次数据变化后,客户端才能感知到,所以对于客户端来说,数据是最终一致性。

    4 总结

    通过上述阅读,相信大家对ZooKeeper的基本概念与读写流程都有了一定的了解。这篇文章内容不算多,但却花了我不少时间,主要时间花在了对知识、概念正确性的考察上面,尽力避免因为我的理解错误而误导大家。最后,文章有什么不正确的地方,感谢大家的指出,如果文章对你有帮助,也欢迎点赞。

    5 参考资料

    [1] ZooKeeper官方文档资料. http://zookeeper.apache.org/doc/current/zookeeperOver.html.
    [2] zookeeper的原理和应用场景. https://www.jianshu.com/p/b48d50e1fcb1.
    [3] 探索 ConcurrentHashMap 高并发性的实现机制. https://www.ibm.com/developerworks/cn/java/java-lo-concurrenthashmap/.

    相关文章

      网友评论

        本文标题:什么?你还不了解ZooKeeper?

        本文链接:https://www.haomeiwen.com/subject/eduxdqtx.html