本篇是开源软件最后一篇,接下来的一周将推送语言相关或项目管理相关内容。敬请期待。以下正文:
所谓集群系统,是指由多个进程和服务器合作组成完成一定功能的系统。之所以要由多个节点(进程或服务器)组成,其中一个重要目标是:容灾。但是,一大堆服务器要能协同工作,必须要有一个负责组织整个集群的中心,这个中心由于具有唯一性,所以往往都会是一个单点。这个时候问题来了,这个单点如果故障了,整个集群都可能瘫痪,是命门所在。因此,为了让集群中心不再成为单点,Google开发了ZooKeeper这款著名的开源软件。
ZooKeeper是Apache Hadoop下的一个子项目,其官网是:http://zookeeper.apache.org
它是Google chubby的开源实现,基于著名的Paxos算法。有兴趣的可以去搜索一下这个算法。ZooKeeper主要的功能是充当集群的中心点,维护集群中各节点的配置信息、提供名字服务、实现分布式同步等。而这些功能的核心,就是一个分布式的最终一致性存储系统。集群的共享信息,都放到ZooKeeper上,这样集群中任何的节点都能互相协作了。
ZooKeeper本身可以由多个进程组成一个服务,这样就不构成单点风险。这些进程之中,会有一个充当“领导者”(leader)的角色,为整个集群提供一致性保证。在ZooKeeper服务中存放的数据,都是在进程的内存中保存的。同时还可以提供事物日子和持久化的快照存储功能。客户端通过tcp和ZooKeeper的服务进程建立连接,如果连接的这个ZooKeeper进程挂掉了,可以在ZooKeeper算法的指导下连接到另外一台。由于ZooKeeper的数据是在多个服务器进程上复制的,所以特别适合读写比在10:1的场景。
ZooKeeper存储的数据模型,给人的感觉就类似一个目录树,和文件系统非常像,都是一个树状的结构,以字符串和反斜杠隔开表达。用户可以在“根”节点上建立多个任意名字的子节点,然后再在这些子节点下建立其他子节点,或者只是在子节点下存放一些数据。这些数据都是序列化的字节数组类型,可以用来存放任何信息。
ZooKeeper的内部在处理请求的时候,读和写是分开的:
ZooKeeper会在内存中保留上述树状数据的完整版,并且把写操作记录到磁盘,以便用于恢复。写操作会先把记录写到磁盘,再更新内存中的状态。每个ZooKeeper服务进程都可以提供服务。客户端可以通过任何一个具体的服务进程来提交读写请求。读操作可能通过就近的任何一个数据库副本来完成,写操作则通过一致性协议进行处理。也就是说客户端并不是一直都只连接着一个ZooKeeper服务进程的,在进行读写不同操作的时候,是连接不同的服务进程的。那么,写操作到底是会给哪个服务进程处理了?实际上是全部转发给某一个特点的服务进程,这个进程叫“领导者”(leader),这个进程写入数据后,其他的进程将会跟随这个进程的数据做同步,所以叫“跟随者”(follower)。因此ZooKeeper服务进程中,会有两种角色,有的进程负责领导,其他进程负责跟随,这样来保证数据的最终一致性。因此我们可以看到,ZooKeeper为了保证数据在各个服务节点上的一致性,是采用复制数据和更新广播的方式来做的,因此读会比写效率高的多。
由于ZooKeeper这种安全的数据同步方案,所以它可以提供非常高的可靠性保证:
ZooKeeper的API看起来和文件系统类似,提供了C和JAVA语言的接口,他们包括:
可以看到,基本的操作和文件系统差别不大,比较特别的是可以设置“监视器”(watch),这可以在集群节点数据有变化的时候,相关的客户端进程及时的收到通知。这种主动通知的方法,比起客户端轮询(不断的刷新)状态来的更高效和及时。因此是ZooKeeper上使用最多的功能之一。监听的具体用法是:
1. 所有的读操作都可以设置监听:getData(), getChildren(), exists()
2. 监听的定义:当被监听的数据被修改时,一个监听事件会发给置了监听的客户端。监听只触发一次。
3. 只触发一次
a) 数据改变只会给客户端发送一个监听事件。如果数据再次改变,不会再发送监听事件,除非客户端设置另外一个监听。
4. 监听数据的设置
a) getData()和exists()是为当前结点设置监听
b) getChildren()是为孩子结点设置监听
c) setData()会触发对当前结点的监听
d) Create()会触发当前结点及孩子结点
e) Delete()会触发当前结点及孩子结点
5. 如果客户端与服务器断开,期间被监听数据发生变化,重连后监听依然会被触发。
6. 有一种情况会错失监听消息:监听一个结点是否存在,但这个结点还没有创建。如果在断开状态,这个结点被创建并且被删除。
7. 一个结点是先监听事件,才能看到新数据
但是监听也有一些需要注意的问题:
最后列举一下官方提供的性能数据:
实测的数据:
一个集群承载每秒2万的写操作,其实是比较低的,所以要非常注意不要用ZooKeeper承担太大的写操作功能,比如不要让他承担NoSQL或者Memcache的功能。
最常见的用法是管理集群。做法是,在ZooKeeper上建立一个EPHEMERAL类型的目录节点,然后每个 Server在它们创建目录节点的父目录节点上调用getChildren(String path, boolean watch) 方法并设置 watch 为 true,由于是 EPHEMERAL目录节点,当创建它的Server 死去,这个目录节点也随之被删除,所以Children 将会变化,这时getChildren上的Watch 将会被调用,所以其它Server 就知道已经有某台Server 死去了。这样每个集群中的服务进程,都能通过ZooKeeper及时的知道现在集群中都有哪些进程“活着”。当然啦,如果你新加了进程进来,一样会让目录节点产生变化(新建了子节点),这样其他的服务进程也都知道了。
第二个用法是分布式锁:有时候我们的集群系统,不同进程对同一个资源会有锁的需求,比如我们的集群系统只有一个发短信的端口,不能一起去向这个端口投递消息,这样就需要一把“锁”,当一个进程在发短信的时候,其他所有想发短信的进程都被锁住而不能发。
第三个用法很常见,就是用来做配置管理。Config文件一直是服务器软件的重要数据,在集群系统中,有很多数据是需要多个进程共享的。传统的做法常常是把配置文件写在服务器磁盘上,当配置需要修改的时候,把文件批量拷贝到各个服务器上,然后发送一些信号给服务进程,让其主动重新读这个配置,然后生效。这个过程繁琐而且容易出错,因为批量拷贝文件,批量发送信号,都可能中途出错。而如果把配置信息放在ZooKeeper上,一旦配置有修改,watch机制就会触发所有的进程刷新配置。这样我们只要修改ZooKeeper上的配置信息,整个配置刷新过程就自动完成了。有时候我们需要同步的信息不止是静态的配置信息,还会包括一些业务数据信息,只要写操作不太频繁,ZooKeeper都是能胜任的。
第四个用法是做名字服务,用来存储“名字”-“地址”的映射表。在RPC系统和SOA架构的集群中,为了让集群的服务能够根据运行情况,动态的进程负载均衡或者容灾,往往在请求集群中服务的时候,不会写死一个物理的IP地址,而是先用服务请求的“名字”来查询哪个IP+端口地址上的进程能提供服务,然后再连接过去。这种情况下,ZooKeeper就可以用来承担名字查找服务的任务。由于集群中注册服务的操作远远少于查询服务地址的操作,所以很适合用ZooKeeper完成。
2. ZooKeeper性能和那些指标有关
长短连接方式>已有连接个数>读写节点大小>读写节点深度>服务器上节点个数。
3. ZooKeeper最大并发读能达到怎样的性能?
12000连接并发读1K数据。
4. ZooKeeper最大并发写的性能如何?
10000连接并发写1byte数据。5600连接并发写2k数据。
5. Leader失效对ZooKeeper有什么影响?
导致服务暂停1~2秒(官方称200ms)
6. ZooKeeper单一节点允许写入的数据大小是多少?
标配1MB
7. 单个ZooKeeper服务器挂掉是否影响服务?
不影响,已经连接到这个服务器上的客户端连接会被转移,并受到一个连接丢失的警告。
8. ZooKeeper瓶颈在哪里?
瓶颈是leader节点。除了只读操作,其他操作都要经过leader。因此它的CPU是其他节点的4~5倍,网络流量也是4~5倍。应该禁止大于1MB的数据写入节点,否则leader服务器的带宽很容易耗尽。由于是JAVA构造的服务,所以leader的GC(垃圾回收)操作会很频繁。
9. 如果遇到timeout/连接失败怎么办?
重试2~3次,ZooKeeper作为集群系统大多数情况下都能恢复。
10. 如果遇到删除节点失败/创建节点失败怎么办?
这是逻辑代码问题,一般原因是子节点非空(ZooKeeper可没有rm –rf的功能)和父节点不存在(一步建立多个层次节点树是不允许的)导致的。
11. 持续遇到连接丢失怎么办?
ZooKeeper集群有严重问题,或者并发太多连接ZooKeeper已经无法接受了。
ZooKeeper作为一个严谨的最终一致性数据系统,接口使用非常简单,安装部署也很方便,是构建集群服务中心点的最好选择。只是使用的时候注意不要滥用其功能,要严格控制节点中数据的大小,以及写操作数量。读操作也应该控制不宜太多,比如不适合用来做公开dns服务器,如果真的需要对外提供大量的读操作,可以在外面以复制缓冲的形式建立多个缓存服务器,开发起来也是很简单的。
感谢大家的阅读,如觉得此文对你有那么一丁点的作用,麻烦动动手指转发或分享至朋友圈。如有不同意见,欢迎后台留言探讨。