集群开源软件赏:ZooKeeper

本篇是开源软件最后一篇,接下来的一周将推送语言相关或项目管理相关内容。敬请期待。以下正文:

所谓集群系统,是指由多个进程和服务器合作组成完成一定功能的系统。之所以要由多个节点(进程或服务器)组成,其中一个重要目标是:容灾。但是,一大堆服务器要能协同工作,必须要有一个负责组织整个集群的中心,这个中心由于具有唯一性,所以往往都会是一个单点。这个时候问题来了,这个单点如果故障了,整个集群都可能瘫痪,是命门所在。因此,为了让集群中心不再成为单点,Google开发了ZooKeeper这款著名的开源软件。

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的内部在处理请求的时候,读和写是分开的:

  1. 写数据的请求,会通过一个请求处理器,然后转交给原子广播系统,把请求内容“真实”的同步到所有节点上,更新各节点的复制数据库后,再返回结果
  2. 读请求比较简单,直接从节点的复制数据库那里读取结果就完成了。

ZooKeeper会在内存中保留上述树状数据的完整版,并且把写操作记录到磁盘,以便用于恢复。写操作会先把记录写到磁盘,再更新内存中的状态。每个ZooKeeper服务进程都可以提供服务。客户端可以通过任何一个具体的服务进程来提交读写请求。读操作可能通过就近的任何一个数据库副本来完成,写操作则通过一致性协议进行处理。也就是说客户端并不是一直都只连接着一个ZooKeeper服务进程的,在进行读写不同操作的时候,是连接不同的服务进程的。那么,写操作到底是会给哪个服务进程处理了?实际上是全部转发给某一个特点的服务进程,这个进程叫“领导者”(leader),这个进程写入数据后,其他的进程将会跟随这个进程的数据做同步,所以叫“跟随者”(follower)。因此ZooKeeper服务进程中,会有两种角色,有的进程负责领导,其他进程负责跟随,这样来保证数据的最终一致性。因此我们可以看到,ZooKeeper为了保证数据在各个服务节点上的一致性,是采用复制数据和更新广播的方式来做的,因此读会比写效率高的多。

由于ZooKeeper这种安全的数据同步方案,所以它可以提供非常高的可靠性保证:

  1. 一致性:客户端无论连接到哪个服务器,展示的都是同一个视图。
  2. 顺序性:客户端的update请求,会根据他们发出的顺序进行处理。
  3. 原子性:对节点的更新操作,要么成功,要么失败。
  4. 高可用性:在2n+1台机器组成的集群中,即使有n台失效,仍然不影响系统整体可用性。
  5. 高效性:由于采用内存镜像,降低了读的延迟。
  6. 最终一致性:经过一段时间,客户端看到的数据最终是一致的。

ZooKeeper的API看起来和文件系统类似,提供了C和JAVA语言的接口,他们包括:

  1. create(path,data,acl,falg)创建节点
  2. delete(path,version)删除节点,子节点不为空不能删除
  3. setData(path,data,version)更新节点的内容
  4. getData(path,watch)读取节点,设置watch节点变化时会通知
  5. exists(path,watch)判断节点是否存在
  6. getchildren(path,watch)获取子节点
  7. setACL(path,acl,version)设置Acl
  8. getACL(path)获得Acl

可以看到,基本的操作和文件系统差别不大,比较特别的是可以设置“监视器”(watch),这可以在集群节点数据有变化的时候,相关的客户端进程及时的收到通知。这种主动通知的方法,比起客户端轮询(不断的刷新)状态来的更高效和及时。因此是ZooKeeper上使用最多的功能之一。监听的具体用法是:

1. 所有的读操作都可以设置监听:getData(), getChildren(), exists()

2. 监听的定义:当被监听的数据被修改时,一个监听事件会发给置了监听的客户端。监听只触发一次。

3. 只触发一次

a) 数据改变只会给客户端发送一个监听事件。如果数据再次改变,不会再发送监听事件,除非客户端设置另外一个监听。

4. 监听数据的设置

a) getData()和exists()是为当前结点设置监听

b) getChildren()是为孩子结点设置监听

c) setData()会触发对当前结点的监听

d) Create()会触发当前结点及孩子结点

e) Delete()会触发当前结点及孩子结点

5. 如果客户端与服务器断开,期间被监听数据发生变化,重连后监听依然会被触发。

6. 有一种情况会错失监听消息:监听一个结点是否存在,但这个结点还没有创建。如果在断开状态,这个结点被创建并且被删除。

7. 一个结点是先监听事件,才能看到新数据

但是监听也有一些需要注意的问题:

  1. 因为监听只触发一次,并且在获得事件和发送下一个监听之前是一个延迟的,所以不能够可靠地监听到数据的每一次变化。要注意处理这种情况:结点在得到监听事件,并再次设置监听之间改变多次。也就是说,你在得到监听事件之后,和再次调用getData(path,watch)之前的数据变化是不会知道的。如果你只是想维持知道最新的数据状态,而不是每次的变化过程,这个就没什么问题。
  2. 一个监听事件,或者功能上下文对,只会被触发一次。如:在同一个结点上注册了exists()和getData()的监听,如果这个文件被删除,只会收到一个删除的通知。所以你最好规划好哪些节点监听数据变化,哪些节点监听是否存在。
  3. 当你与服务器断开(如服务器故障),在连接重建之前你得不到任何监听事件。也就是事件不会重放,你最好在重新连接之后立刻读取一次最新的数据。

最后列举一下官方提供的性能数据:

实测的数据:

一个集群承载每秒2万的写操作,其实是比较低的,所以要非常注意不要用ZooKeeper承担太大的写操作功能,比如不要让他承担NoSQL或者Memcache的功能。

ZooKeeper可以做什么

最常见的用法是管理集群。做法是,在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完成。

ZooKeeper使用技巧

  1. 常见的ZooKeeper错误是什么? ZOPERATIONTIMEOUT/ZCONNECTIONLOSS/ZSESSIONEXPIRED。前两种重试就可以了,后一种表示通信严重错误。

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服务器,如果真的需要对外提供大量的读操作,可以在外面以复制缓冲的形式建立多个缓存服务器,开发起来也是很简单的。

感谢大家的阅读,如觉得此文对你有那么一丁点的作用,麻烦动动手指转发或分享至朋友圈。如有不同意见,欢迎后台留言探讨。

原文发布于微信公众号 - 韩大(handa1740168)

原文发表时间:2016-01-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据和云

【推荐】 RAC 性能优化全攻略与经典案例剖析

ORACLE RAC凭借其卓越的容错能力和可扩展性以及对应用透明的切换能力引领了数据库高可用架构的潮流,但在实际的生产环境中,出现的性能问题非常多,对数据库的稳...

2737
来自专栏数据和云

Oracle 原版经典ppt首次公开,免费下载:Oracle RAC Internals

接下来我们将会针对ppt中最经典的模块进行讲解分享。 一、Cluster Domain 在12.2中,OracleRAC有很多重要的改进。而Cluster Do...

3727
来自专栏Java工程师日常干货

分布式利器Zookeeper(一)Hello,Zookeeper初步认识Zookeeper的数据模型初步认识Zookeeper的角色组成install Zookeeper基本的ZK命令ZooInspe

Zookeeper不论是在实际项目中,还是在各种分布式开源项目中都得到了广泛应用,从本篇博客开始,将为大家带来我对Zookeeper的认识。这个系列将会涵盖Zo...

752
来自专栏Spark学习技巧

干货:Flink+Kafka 0.11端到端精确一次处理语义实现

实时处理里消息的仅一次处理是大家关注的重点吧,前面浪尖分享过一篇对比spark streaming 和 flink的文章 <Spark Streaming VS...

933

Apache CloudStack系统VM架构选择

最近我和一些人讨论了为什么现在有32位或64位虚拟机系统和云计算平台4.3的选择。我提供了一个答案,并链接到一些邮件列表进行讨论。我想这可能是随兴...

1659
来自专栏开源项目

Git 项目推荐 | 基于go+protobuff 实现的分布式

eQ ? 基于go+protobuff实现的多种持久化方案的mq框架 Client For KiteQ Go: https://github.com/b...

41014
来自专栏芋道源码1024

【消息队列 MQ 专栏】消息队列之 RocketMQ

RocketMQ 是阿里巴巴在2012年开源的分布式消息中间件,目前已经捐赠给 Apache 软件基金会,并于2017年9月25日成为 Apache 的顶级项目...

1530
来自专栏BeJavaGod

分布式协调服务中间件ZooKeeper 入门(1)-ZK的介绍与特性

一、Zookeeper简介 Zookeeper是一个服务,是一个分布式协调技术,他提供高性能,分布式的协调服务。主要用来解决分布式环境当中多个进程之间的同步控制...

3624

Kafka体系结构:日志压缩

这篇文章是从我们介绍Kafka 体系结构的一系列文章中获得的启发,包括Kafka topic架构,Kafka生产者架构,Kafka消费者架构和Kafka生态系统...

1763
来自专栏IT技术精选文摘

LVS集群的体系结构

1.引言 在过去的十几年中,Internet从几个研究机构相连为信息共享的网络发展成为拥有大量应用和服务的全球性网络,它正成为人们生活中不可缺少的 一部分。虽...

1938

扫码关注云+社区