MyException - 我的异常网
当前位置:我的异常网» 开源软件 » 深入懂得zookeeper

深入懂得zookeeper

www.MyException.Cn  网友分享于:2013-09-28  浏览:0次
深入理解zookeeper

      

zookeeper概况

背景&问题

在生产环境中,为了提高服务可用性、支撑更多的用户量等,分布式应用服务都会在不同IDC多个节点上部署,我们很可能会遇到以下问题:

  • 分布在各个机器、IDC的应用程序如何能高效读取、修改配置?
  • 当配置变更时,各节点应用如何快速发现变化、及时响应处理?
  • 如何在应用程序部署的各个节点中,选举一个节点,作为leader,执行协调相关操作? leader挂掉时,其他节点能重新发起leader选举? 如何避免脑裂? 如何处理网络分区?
  • 当某个节点异常挂掉时,如何及时发现?

第1、2点在节点数较少、对性能要求不高的情况下,我们可以通过将配置存储在mysql+定时轮询解决。若对性能要求较高我们就需要结合cache、agent、配置变更notify、mysql等组件实现一套配置系统来解决,如淘宝的diamond。

第3、第4点,在复杂的分布式环境中,我们会遇到高网络延时、网络波动、磁盘故障、机器宕机、机器半死不活、机房断电、网络分区等一系列问题,同时要避免数据不一致、脑裂,还要追求高吞吐、低延时,最大程度减少因选举leader导致服务不可用时间等,最糟糕的是分布式理论FLP(consensus is impossible with asynchronous systems and even one failure)、CAP(consistency,high availability,partition-tolerance)告诉我们在设计上需要权衡取舍,这些如果让业务应用程序来处理,就具有一定的复杂性。

因此,这类复杂问题不适合应用程序自己解决,应用程序需要一个God,一个值得信赖的Oracle,同时God提供的Service应该尽量简单、易理解、高性能、易扩展。

Yahoo的工程师们为了解决应用这些问题,设计实现了zookeeper,为什么叫zookeeper? 因为yahoo内部不少分布式系统命名是动物名字,同时分布式环境中的复杂、混乱跟动物园(zoo)是不是有点类似?而zookeeper就是维持、管理整个动物园的秩序,这就是zookeeper的名称的来历。

ZooKeeper是一个分布式管理服务,可为应用提供配置管理、名称服务、状态同步、集群管理等功能,我们的应用场景主要是配置管理、分布式锁。

  • 为什么apache给它定义是个分布式协调式服务,而不是存储服务?它的设计目标定位是什么?
  • 它可以当存储服务来使用吗? 它的数据模型是怎样的? 如何持久化存储的?
  • zookeeper服务端的读写流程是怎样实现的?
  • zookeeper c api是如何实现的?
  • 如何通过zookeeper实现分布式锁、Leader Election?
  • 在生产环境实践中我们遇到了哪些问题?如何优化zookeeper性能? 对zookeeper进行监控?

本文将结合zookeeper源码(3.4.6)、在生产环境实践经验,通过分析以上问题来深入理解zookeeper,以及分享我们在实践中遇到的问题及经验。

首先,我们一窥zookeeper全貌,了解下其总体架构及设计目标。

zookeeper架构

zookeeper架构

zookeeper集群节点数量一般由奇数个节点组成,节点角色由follower和leader组成,所有写请求需转发到leader,每次写请求需集群一半以上节点应答成功才写入成功,读请求在任意一台follower节点上都可以处理,只要集群中有一半以上的节点存活、并能相互通信,zookeeper集群就可以持续提供服务,因此具有较高的可用性。节点数量越多,可用性会进一步提高,但是会影响写性能,因此在生产环境中一般部署5台。 zookeeper提供的接口类似nosql系统,常用的接口有get/set/create/getchildren等,接口简单易用。

zookeeper设计目标

简单

zookeeper数据模型简单,易懂,类似文件系统的层次树形数据结构,存储数据未做shard分散到多机,而是各单机完整存储整个树形层次空间上所有路径的节点数据,数据全部保存在内存,因此可提供高吞吐量、低延迟的服务,也意味着zookeeper不适合保存大节点数据。

高可用、高性能读

因单机上保存了所有数据,若没有多机之间数据同步复制机制,zookeeper系统可用性将极低,因此zookeeper在设计上一个重要目标是可复制的,各节点通过zookeeper atomic broadcast算法选举leader,同步数据。所有写请求follower节点都需转发给leader,读请求在任意一台follower节点上都可以处理。

有序

zookeeper通过基于tcp连接、写请求由leader处理等机制提供有序保证,基于有序机制,zookeeper可以提供同步原语,实现分布式锁等机制。

zookeeper数据模型

存储系统常见的数据模型有关系型表格型(Relational Model)、层次树型(Hierarchical model)、扁平型(Flat model)、网络型(Network Model)、对象型(Object-oriented Model).

五种常见存储模型图

zookeeper的数据模型是层次型,类似文件系统,但是zookeeper的设计目标定位是简单、高可靠、高吞吐、低延迟的内存型存储系统,因此它的value不像文件系统那样会适合保存大的值,官方建议保存的value大小要小于1M,提供的接口类似nosql存储系统(key是路径)。

zookeeper层次模型

那么zookeeper的层次模型是通过什么数据结构实现的呢? get、set、getchildren的时间复杂度又分别是多少呢? 通过阅读zookeeper server源码,zookeeper是基于ConcurrentHashMap实现的,path是key,value是DataNode,DataNode保存了value、children、 stat等信息。

  1. zookeeper database模型的调用链路
  2. ZKDatabase
  3. DataTree
  4. ConcurrentHashMap<String,DataNode> nodes =newConcurrentHashMap<String,DataNode>();
  5. DataNode
  6. data,acl,stat,children
  7. classStat{
  8. long czxid;// created zxid
  9. long mzxid;// last modified zxid
  10. long ctime;// created
  11. long mtime;// last modified
  12. int version;// version
  13. int cversion;// child version
  14. int aversion;// acl version
  15. long ephemeralOwner;// owner id if ephemeral, 0 otw
  16. int dataLength;//length of the data in the node
  17. int numChildren;//number of children of this node
  18. long pzxid;// last modified children
  19. }

ConcurrentHashMap是线程安全的hash table,采用了锁分段技术来减少锁竞争,以提高性能。其结构如下图所示,由两部分组成,Segment和HashEntry,锁的粒度是Segment,每个Segment 对象包含整个散列映射表的若干个桶,散列冲突时通过链表来解决.

ConcurrentHashMap

因此zookeeper在使用ConcurrentHashMap时其各接口期望时间复杂度如下:

  • get:O(1)
  • create/set:O(1)
  • getchildren:O(1)

zookeeper持久化存储

从数据模型我们知道zookeeper所有数据都是加载都内存,基于ConcurrentHashMap构建一颗DataTree,那么zookeeper要保证机器重启数据不丢失就需要实现持久化存储,而zookeeper的持久化实现是通过snapshot、txnlog实现的,snapshot是zookeeper内存数据的完整镜像,zookeeper在运行中会定时生成,txnlog是快照时间点之后的事物日志,zookeeper在重启时,通过snapshot和txnlog重建DataTree. 下图是运行中的zookeeper集群的生成的数据文件。

zookeeper数据文件

snapshot和log文件分布保存在哪?保留多少个snapshot和log文件? 什么时候清理废弃的snapshot和log 文件? 这些都可以通过在zookeeper的zoo.cfg配置文件中指定,dataDir指定snapshot路径,dataLogDir指定事物日志路径,事物日志对zk吞吐量、延时有着非常大的延时,建议datadir与dataLogDir使用不同的设备,避免磁盘IO资源的争夺,影响整个系统性能和稳定性。autopurge.snapRetainCount项表示保留多少个snapshot,每个snapshot快照清理间隔小时可以通过autopurge.purgeInterval来指定。

snapshot的生成和log文件的写入是在SyncRequestProcessor类中实现的,事物日志类TxnLog,快照类FileSnap,事物日志会追加到TxnLog,当记录数大于1000会刷到磁盘,当写入log数大于snapCount/2+randRoll(nextInt(snapCount/2)时,会开启线程将DataTree dump到磁盘,具体实现逻辑如下:

  1. if(zks.getZKDatabase().append(si)){
  2. logCount++;
  3. if(logCount >(snapCount /2+ randRoll)){
  4. randRoll = r.nextInt(snapCount/2);
  5. // roll the log
  6. zks.getZKDatabase().rollLog();
  7. // take a snapshot
  8. if(snapInProcess !=null&& snapInProcess.isAlive()){
  9. LOG.warn("Too busy to snap, skipping");
  10. }else{
  11. snapInProcess =newThread("Snapshot Thread"){
  12. publicvoid run(){
  13. try{
  14. zks.takeSnapshot();
  15. }catch(Exception e){
  16. LOG.warn("Unexpected exception", e);
  17. }
  18. }
  19. };
  20. snapInProcess.start();
  21. }
  22. logCount =0;
  23. }
  24. }
  25. toFlush.add(si);
  26. if(toFlush.size()>1000){
  27. flush(toFlush);
  28. }

从zookeeper持久化的基本实现可知若写请求较大会频繁生成快照,同时因为toFlush是同步刷新数据到磁盘的,所以会影响吞吐率、延时,这也是为什么txnlog建议使用性能较好的存储硬件的原因(如SSD)。

zookeeper核心角色及概念

leader

follower

observer

session

watcher

access control

zookeeper server读写流程分析

在zookeeper的服务端实现中,通过抽象出leader、follower、observer共性特点,读写请求的处理流程可以按照功能拆分成各阶段(pipeline),每个processor负责处理其中一个阶段,采用设计模式的职责链形式,一个processor处理完,通过队列分发到下一个processor中。processor相当于工厂各元部件,而leader、follower、observer只是使用、组装的各元部件不一致,但他们可以高度复用相同的元部件,精简实现,减少代码冗余。

职责链处理类介绍

PrepRequestProcessor

此处理类根据请求的命令(create,set等)负责生成事物请求信息数据结构request,统计正在进行的事物等。

FollowerRequestProcessor

此处理类负责将写请求分发给leader.

CommitProcessor

SyncRequestProcessor

如前面持久化存储所述,此处理类负责持久化存储,将批量事物日志刷新到磁盘和定时生成快照。

SendAckRequestProcessor

此处理类在收到写请求提议后,回复ACK给leader.

ProposalRequestProcessor

此处理类负责将所有写请求转发给follower节点。

ToBeAppliedRequestProcessor

FinalRequestProcessor

此处理类如名字所言,是请求流行线式处理最后一环,负责处理查询请求(从zkdatabase的DataTree读取数据)和写事务请求。

zookeeper读流程

zookeeper写流程

zookeeper c api

总结

参考资料

  • Apache Zookeeper
  • Apache ZooKeeper: the making of
  • ZooKeeper学习之server端实现的基本骨架

文章评论

我是如何打败拖延症的
我是如何打败拖延症的
我的丈夫是个程序员
我的丈夫是个程序员
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
程序员和编码员之间的区别
程序员和编码员之间的区别
10个调试和排错的小建议
10个调试和排错的小建议
程序员应该关注的一些事儿
程序员应该关注的一些事儿
鲜为人知的编程真相
鲜为人知的编程真相
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
那些争议最大的编程观点
那些争议最大的编程观点
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
一个程序员的时间管理
一个程序员的时间管理
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
 程序员的样子
程序员的样子
程序员必看的十大电影
程序员必看的十大电影
总结2014中国互联网十大段子
总结2014中国互联网十大段子
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
漫画:程序员的工作
漫画:程序员的工作
如何成为一名黑客
如何成为一名黑客
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
为什么程序员都是夜猫子
为什么程序员都是夜猫子
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
编程语言是女人
编程语言是女人
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
Java程序员必看电影
Java程序员必看电影
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
代码女神横空出世
代码女神横空出世
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
每天工作4小时的程序员
每天工作4小时的程序员
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
旅行,写作,编程
旅行,写作,编程
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有