Zookeeper是分布式一致性问题的工业解决方案,是Apache Hadoop下解决分布式一致性的一个组件,后被分离出来成为Apache的顶级项目。

工程来源:是雅虎公司内部项目,据说雅虎内部很多项目都是以动物命名,这个动物管理员的名字起的很是形象。

被开源出来后得到开源社区的快速推进,服务端Java语言实现,棒,git有3000+的star:

https://github.com/apache/zookeeper

zookeeper集群结构

集群的角色,比较典型的是Master/Slave(主备模式),zk中的概念跟这个不一样,包含Leader、Follower、Observer三个角色,leader提供读和写的能力,follower只对外提供读的能力。

会话(session)

客户端跟服务端交互,是先与服务端建立一个TCP长连接,会话开始,通过心跳检测与服务端保持会话有效,向服务端发送请求和接收响应。

zk将所有的数据都加载在内存一份,同时有事务日志文件(持久化文件),服务端会定时dump快照数据,重启机器的时候会根据快照和事务日志恢复内存数据库的数据,这跟redis的AOF和RDB概念类似。

zookeeper上的数据结构

zk上的数据的结构跟linux文件系统很像,是个树状结构

节点(node)上的信息字段

节点类型包括:

  • 持久型节点
  • 顺序持久型节点
  • 临时节点
  • 顺序临时节点

其中临时节点特性就是创建它的主体消失后,它就跟着消失了。后续的应用就是利用的节点的特性实现的。

事件监听器(watcher)

这个应该是zookeeper最重要的概念之一了,zk允许用户在特定的节点(znode)上注册watcher,并且在特定事件触发的时候,zk服务端会将事件通知到感兴趣的客户端上。

伪集群的搭建:

zoo.cfg 配置文件

  • 拷贝三份文件
  • 修改配置zoo.cfg,区别出日志目录和端口号,dataDir文件下添加表示机器号的文件myid
  • 集群配置列表,第一个端口是机器间业务通讯的端口,第二个端口是用来进行leader选举的端口
  • 分别启动三台服务器

启动成功后,命令行连接zk,可以用指令做些增删改查的操作

telnet 127.0.0.1 2181

stat:可以看集群的状态信息

stat信息

每次事务操作,会在dataDir的目录下的事务日志,是序列化的二进制文件,zookeeper提供了查看事务日志的工具类LogFormatter

LogFormatter转换后的事务日志文件

Java客户端使用

  1. zookeeper自带的
  2. 开源的客户端ZkClient,实现session超时重连,watcher反复注册的功能,简化开发人员的使用
  3. 开源客户端Crurator,解决底层的细节开发工作,目前是apache的顶级项目,是使用最广泛的zk客户端。
  4. 提供了可读性更新的api接口
  5. 提供了各种应用场景的抽象封装(共享锁服务、Master选举机制,分布式计数器等)
  6. guava is to java what cruator is to zookeeper

cruator客户端例子

zookeeper应用场景

利用zookeeper的特性,可以比较方便的构建分布式应用会涉及的核心功能,比如:配置中心、命名服务、分布式协调/通知、集群的管理、master选举、分布式锁等

以下应用基本基于zookeeper的两大特性实现

  • 客户端如果对zookeeper的一个数据节点注册watcher监听,那么当该数据节点的内容或其子节点的列表发生变更是,zookeeper服务器就会想订阅的客户端发送变更通知
  • 对在zookeeper上创建的临时节点(sequent类型),一旦客户端与服务器之间的回话失效,那么临时节点会被自动清除

--配置中心:

配置中心

zookeeper利用推拉结合的方式,客户端向服务端注册自己需要关注的节点,一旦该数据发生变更,那么服务器就会向相应的客户端发送watcher时间通知。

客户收到这个消息通知之后,再主动到服务端获取最新的数据。即回调的event中包含具体的数据。

这个应用的的业务员特点:

  1. 数据不经常变化
  2. 数据量通常比较小,保存的内存里,访问很快
  3. 配置动态变化,不需要重启机器,数据变化,会通知相应的客户端
  4. 集群共享配置一致,比如数据库连接的配置,业务的开关,甚至一些数据量小,不经常变的业务数据如弹窗文案,活动文案也放里边,用于快速迭代实现功能。

--命名服务

利用zookeeper的顺序节点,树形结构的数据特点,实现命名服务:

  • 比如RPC框架中,每个服务在zookeeper中对应一个节点(serviceName),节点下存放这个服务所用到的资源,比如部署的ip列表,接口列表

这样RPC的客户端只需要传对应的服务名字,和接口,就能找到对应的服务。

  • 全局唯一id的生成,UUID(通用的唯一标识码)可以实现,但是他的缺点是:太长,包含32位字符和4个短线字符串;看不出业务含义,不方便排查问题。

使用zookeeper实现:不同的业务下创建一个节点,具体的节点下用zk的顺序节点(sequent)生成id当做这个业务的全局唯一id使用

--分布式通知/协调

ZooKeeper中特有watcher注册与异步通知机制,能够很好的实现分布式环境下不同系统之间的通知与协调,实现对数据变更的实时处理。

使用方法通常是不同系统都对ZK上同一个znode进行注册,监听znode的变化(包括znode本身内容及子节点的),其中一个系统update了znode,那么另一个系统能够收到通知,并作出相应处理。

应用:

心跳检测机制:传统的方式是ping,复杂的话是建立长连接检测系统和被检测系统之间并不直接关联起来,而是通过zookeeper上某个节点关联,大大减少系统耦合

系统调度模式:某系统由控制台和推送系统两部分组成,控制台的职责是控制推送系统进行相应的推送工作。

管理人员在控制台作的一些操作,实际上是修改了ZK上某些节点的状态,而ZK就把这些变化通知给他们注册Watcher的客户端,即推送系统。于是,作出相应的推送任务

作汇报模式:一些类似于任务分发系统,子任务启动后,到ZK来注册一个临时节点,并且定时将自己的进度进行汇报(将进度写回这个临时节点)

总之,使用zookeeper来进行分布式通知和协调能够大大降低系统之间的耦合。

--分布式锁

这个应用主要得益于ZooKeeper为我们保证了数据的强一致性

即用户只要完全相信每时每刻,zk集群中任意节点(一个zk server)上的相同znode的数据是一定是相同的。

一个节点要么创建成功,要么失败,并且只由一个客户端创建。

独占锁:

保持独占,就是所有试图来获取这个锁的客户端,最终只有一个可以成功获得这把锁。

通常的做法是把ZK上的一个znode看作是一把锁,通过create znode的方式来实现。所有客户端都去创建/distribute_lock节点,最终成功创建的那个客户端也即拥有了这把锁。

共享时序控制锁:

Zookeeper很容易实现这个功能,实现方式是需要获得锁的Server,创建一个EPHEMERAL_SEQUENTIAL目录节点。

然后调用getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点。

如果正是自己创建的,那么它就获得了这个锁,如果不是,那么它就调用exists(String path, boolean watch)方法,并监控Zookeeper上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁。

释放锁很简单,只要删除前面它自己所创建的目录节点就行了。

--master选举

master选举应用图

有个容易理解的方案,依靠关系型数据库主键的特性,集群的机器同时往一张表里插入数据,数据库会自动进行主键冲突检查,可以选择插入成功的客户端作为master

这种方式存在一个问题就是,master机器挂了,没有人通知

zk实现可以方便做到这一点:zk的创建节点api接口,具有强一致性,能够保证客户端并发创建的时候,最终一定只有一个客户端创建成功。

  • 客户端集群每天定时往/master_election/(当天日期)2017-03-24/binding创 建临时节点,只有一个成功,为master
  • 创建失败的在/master_election/(当天日期)2017-03-24 注册监听事件,当master挂了,其余机器收到通知,重新进行选举

--集群管理

应用举例:集群机器存活性监控系统,例如:

监控系统在/clusterServers节点注册一个watcher监听,那么但凡进行动态添加机器的操作,就在/clusterServers下创建一个临时节点, /clusterServers/ip。

这样监控系统就能够实时的检测到机器的变动,通过getChild方法获取所有的临时节点,来判断增加的机器。

当有机器down调或者手动下线,相应临时节点会消失,监控系统也会接收到,来处理监控服务的具体业务

具体服务器部署agent实现

zookeeper的HA设计实现

以上说了那么多犀利实用的应用场景,它们依赖zookeeper,说明这些应用服务的高可用性依赖的zookeeper本身的HA。

zk的选举算法

算法协议zab协议,“少数服从多数”协议一种

3.4.0版本之后Zookeeper只保留了TCP版本的FastLeaderElection选举算法

分析选举算法前,先熟悉了解下zk的一些术语定义解释:

  • SID:服务器ID,在集群的配置文件里配置
  • ZXID:是一个事务ID,用来唯一标示一次服务器状态变更,集群中每台机器上的ZXID可能不一样
  • Vote:投票选举,当集群中机器发现自己无法检测到leader的时候,开始尝试进行投票
  • Quorum:过半机器数, quorum=(集群机器数)n/2+1,比如集群数量是3, quorum=2

集群数量是4,quorum=2,集群数量是5,quorum=3

当哪些情况发生时会出发leader重新选举呢?

当zk的一台服务器出现以下两种情况的时候,会进入leader选举流程

  1. 加机器,服务器初始化
  2. 服务器运行期间无法和leader通信,leader所在服务器down掉了

对于第一种情况,即已经存在一台leader服务器,当改机器试图去选举leader的时候,会被告知当前服务器的leader信息,对于该机器仅仅需要和leader建立连接,并进行状态同步即可

主要看下第二种情况:

有两种情况导致集群不存在leader,一个是集群刚启动初始化的时候,另一种情况是运行期间leader所在服务器挂了。

无论哪种情况集群所有集群都处在一个找leader的状态,称作Looking状态,开始想其他机器发送消息投票

开始leader选举投票的协议规则是怎样呢?

  • 投票的消息包含(SID,ZXID)
  • 一开始试图投自己,把投票消息广播出去
  • 先比较ZXID,选择ZXID大的
  • ZXID相等的,比较SID,选择SID大的
  • 如果自己的值大于别的服务器广播来的消息,投票不做变更
  • 反正,更换投票,开始第二轮投票,广播出去投票信息
  • 每轮结束统计投票,如果一台服务器收到超过半数的相同投票,那个这个服务器对用的SID机器为Leader

5台机器宕机两台后,leader选举的过程图示

因此,一个错误的认识,为了使zookeeper集群能顺利的选出leader,必须将zookeeper集群的服务器数部署为奇数。

从上边例子能看出来部署任意台机器都能够正常选举运行。部署奇数台是官方给的建议,因为奇数和奇数+1的容灾能力是一样的。比如:

5台服务器,能够对2台机器挂掉的情况进程容灾

6台服务器,能够对2台机器挂掉的情况进程容灾,如果挂掉3台,剩下的机器就无法实现过半了。

想精通分布式以及高并发架构?那你得先搞定ZooKeeper架构原理!的更多相关文章

  1. 想玩转JAVA高并发,这些概念你必须懂!

    我们在找工作时,经常在招聘信息上看到有这么一条:有构建大型互联网服务及高并发等经验,你第一时间想到的是媒体常说的双十一吗?带着问题,我们一起思考技术…. 高并发高并发 它是互联网分布式系统架构设计中必 ...

  2. 想玩转JAVA高并发,这些概念你必须懂

    高并发高并发 它是互联网分布式系统架构设计中必须考虑的因素之一,通常是指,保证系统能够同时并行化处理海量请求 同步和异步 同步:发送一个请求,等待返回,然后再发送下一个请求.提交请求 -> 等待 ...

  3. 20 行代码:Serverless 架构下用 Python 轻松搞定图像分类和预测

    作者 | 江昱 前言 图像分类是人工智能领域的一个热门话题.通俗解释就是,根据各自在图像信息中所反映的不同特征,把不同类别的目标区分开来的图像处理方法. 它利用计算机对图像进行定量分析,把图像或图像中 ...

  4. 高并发IM系统架构优化实践

    互联网+时代,消息量级的大幅上升,消息形式的多元化,给即时通讯云服务平台带来了非常大的挑战.高并发的IM系统背后究竟有着什么样的架构和特性? 以上内容由网易云信首席架构师内部分享材料整理而成 相关阅读 ...

  5. python高并发和多线程的关系

    “高并发和多线程”总是被一起提起,给人感觉两者好像相等,实则 高并发 ≠ 多线程 多线程是完成任务的一种方法,高并发是系统运行的一种状态,通过多线程有助于系统承受高并发状态的实现.   高并发是一种系 ...

  6. php-fpm和cgi,并发响应的理解以及高并发和多线程的关系

    首先搞清楚php-fpm与cgi的关系 cgi cgi是一个web server与cgi程序(这里可以理解为是php解释器)之间进行数据传输的协议,保证了传递的是标准数据. php-cgi php-c ...

  7. Netty Redis 亿级流量 高并发 实战 (长文 修正版)

    目录 疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之 -30[ 博客园 总入口 ] 写在前面 1.1. 快速的能力提升,巨大的应用价值 1.1.1. 飞速提升能力,并且满足实际开发要求 1 ...

  8. Netty 高并发 (长文)

    目录 Netty+Zookeeper 亿级 高并发实战 (长文) 写在前面 1. 高并发IM架构与部分实现 1.1. 高并发的学习和应用价值 1.1.1. 高并发IM实战的价值 1.1.2. 高并发I ...

  9. 关于Redis的几件小事 | 高并发和高可用

    如果你用redis缓存技术的话,肯定要考虑如何用redis来加多台机器,保证redis是高并发的,还有就是如何让Redis保证自己不是挂掉以后就直接死掉了. redis高并发:主从架构,一主多从,一般 ...

随机推荐

  1. Scrapy持久化存储-爬取数据转义

    Scrapy持久化存储 爬虫爬取数据转义问题 使用这种格式,会自动帮我们转义 'insert into wen values(%s,%s)',(item['title'],item['content' ...

  2. 有趣的动态规划(golang版本)

    多年前就听过这个动态规划,最近在复习常用算法的时候才认真学习了一下,发现蛮有意思,和大家安利一波. 定义: 准确来说,动态规划师吧一个复杂问题分解成若干个子问题,并且寻找最优子问题的一种思想,而不是一 ...

  3. mysql客户端 navicat 本地导入sql文件出错

    以前遇到过这个问题,找了半天度娘没解决,然后就放弃了. 因为是自己 demo 的项目 所以就自己手动建表了. 现在实习了,去到公司下载下代码来,拿上sql 导入发现还是报错, 根本没法整,然后自己都不 ...

  4. Kubernetes 时代的安全软件供应链

    点击下载<不一样的 双11 技术:阿里巴巴经济体云原生实践> 本文节选自<不一样的 双11 技术:阿里巴巴经济体云原生实践>一书,点击上方图片即可下载! 作者 汤志敏  阿里云 ...

  5. UICollectionView 相关方法

    最近闲来无事,整理一下UICollectionView的相关方法以备使用 UICollectionViewFlowLayout和UICollectionViewLayout UICollectionV ...

  6. 字典dict的深入学习(item() / items() 一致的)

    字典Dict的跟进学习: 一. items()方法的遍历:items()方法把字典中每对key和value组成一个元组,并把这些元组放在列表中返回. dict = {"name" ...

  7. HDU5394 Bomb

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5934 There are NN bombs needing exploding. Each bomb ha ...

  8. XCode项目配置

    此设置优先级在playersetting之上,如果为空或者格式不正确或者文件不存在将不会设置,请注意 一.设置面板 二.对应Xcode中设置 1.TeamID  登录苹果开发者网站,查看个人信息,就有 ...

  9. Demo00

    Demo00 std::transform在指定的范围内应用于给定的操作,并将结果存储在指定的另一个范围内.要使用std::transform函数需要包含头文件. 以下是std::transform的 ...

  10. 当placeholder的字体大小跟input大小不一致时,实现placeholder垂直居中

    如图:搜索和图标不是垂直居中着实难受 最终通过如下代码实现: input::-webkit-input-placeholder { transform: translate(0, 2px); }