导读
我是在没有接触分布式知识的时候学的Zookeeper,在学习的过程中有种不知所云、虚无缥缈的感觉,脑海中一直充斥的两个问题就是,Zookeeper到底是用来做什么的?什么是分布式协调呢?直到学完第二遍,看了一些Zookeeper的使用场景才对Zookeeper有了一个简单相对清晰的概念,因此抽时间做了这次的总结,希望能够帮到打算学习或正在学习Zookeeper的你。
什么是Zookeeper&什么是分布式协调
在学习Zookeeper的时候,听到的第一句对Zookeeper的描述就是:Zookeeper是做分布式协调用的,它在分布式场景中应用非常广。Zookeeper和分布式协调一起冒出来,难解难分,Zookeeper还不知道是什么呢又出来分布式协调了,对于初学的我来说听到这样的描述非常的困惑,根本不能理解它,在这我用我自己的理解来做一个简单的描述,在描述之前先对它的主要特点进行一个简单的说明:
1、Zookeeper是一个内存类型的能存储数据的一个东西(刚开始可以简单的理解为一个数据库),它有着客户端和服务端,服务端类似数据库,能够存储数据。
2、它的非常非常大的一个特点就是能够对存储在它上的数据进行监控,只要客户端对存储的数据设置了监控,那么当数据发生变化时,服务端就会主动的给监控它的客户端发出提醒,这样客户端就可以做出对应的处理。
3、它所存储的数据也可以在客户端失去连接后自动的消亡,而不需要我们主动的去做任何的删除操作。
在了解了上边的三个特点之后,我想你对Zookeeper也有了一个简单的认识,原来Zookeeper就是一个有着特殊功能的能存储数据的东西,它能对存储的数据进行自动删除与监控,这就是我理解的Zookeeper,或许不是那么的准确,但它或许可以帮你快速的对Zookeeper有一个大概的了解。
解释了什么是Zookeeper,那么什么是分布式协调呢?要说分布式协调那么就得先说下分布式了,分布式其实就是将我们原来的单机单应用变成多机多服务,原来我们将一个应用整体开发部署,分布式将应用拆分成一个个的服务,部署在不同的机器上,多个服务部署成一个集群的环境,这就是一种分布式环境。在这种多机多服务的环境中,各个服务间还有着千丝万缕的联系,组织和管理这个大的集群变的非常的困难,而分布式协调就是帮助人们组织管理整个集群、使集群的组织管理变的方便简单,这就是分布式协调,它为管理集群助力,这样说对于初学者来说还是有点懵,但在这只需要有个概念就可以了,在你真正的了解了分布式架构之后,你就会了解到底需要协调什么了。
Zookeeper正是一个分布式协调的工具,它可以帮助我们在集群中传递消息,协调各服务之间的关系。
注意
千万不要把Zookeeper当做数据库来用,虽然可以,但千万不要,上述的介绍只是为了方便理解Zookeeper。
Zookeeper适用于读多写少的场景,做分布式协调用。
Zookeeper的数据模型
数据以节点的形式存储。
在Zookeeper中采用的是分层的数据模型,类似于我们文件系统的目录结构,但又有所不同;至于数据模型是什么,其实概念并不重要,我们知道它说的是什么意思就可以了,简单来说就是数据的存储形式,例如redis的数据模型就是key - value型的,而Zookeeper是上图所示那样,它是由一个个的节点(Node)组成,我们进行数据存储时需要创建节点(在创建节点的时候就需要将数据放进去),而数据就存在我们创建的节点中,每个节点下又可以创建许多的子节点,也称为孩子节点。Zookeeper有着对应的API来进行创建、修改、删除节点数据的操作。
节点类型
Zookeeper的节点分为不同的类型,有着各自的特点。
当客户端连接到Zookeeper服务器的时候,客户端与服务器之间会有一个session生成,连接时可以指定session的失效时间(即当客户端与服务器断开连接多长时间session失效)在执行创建节点命令时,如果加参数声明创建临时节点,那么这个临时节点会与session绑定,当session失效时,这个临时节点将会自动消失。
与临时节点对应的就是持久节点,它不与任何session绑定,独立存在,除非调用命令删除,否则它会一直存在。
序列节点是一种特殊的节点它不会单独存在,要么是持久序列节点,要么是临时序列节点,它只是在创建持久或临时节点时加对应的参数将持久或临时节点创建为持久序列节点或临时序列节点;它的特点就是创建的节点会有标号,这种设计使得在不同的客户端可以在同一个父级目录下创建"同名节点",服务器端会自动的在它们节点名字后添加标号,而客户端却毫无感觉,不会造成命名冲突。
每个节点包含两部分数据:元数据信息和节点存储的数据信息。上图中get命令获取节点信息,显示的信息中的含义解释如下:
“hello” -> 节点存储的数据信息,下面的都是节点的元数据信息。
cZxid/ctime -> 创建该节点时生成的编号(id)与时间。
mZxid/mtime -> 修改该节点时生成的编号(id)与时间。
pZxid -> 该节点的子节点到目前最近创建或删除时生成的编号(id)。
cversion -> 版本号。
ephemeralOwner -> 临时节点所属sessionID 0x0代表不是临时节点。
对于编号,在Zookeeper中任何的操作都有一个编号生成而且不会重复
Zookeeper的监控 watch
Zookeeper的watch可以说是Zookeeper最强大的功能之一,它支持监控session与所有的节点,当与session相关的客户端连接建立或断开,或是节点状态变化都会产生对应的事件,我们只要做好了对应的方法处理,当事件产生时就会触发我们的调用。
当我们要监控某个节点的变化时,我们首先要在我们请求的方法中配置watch(相当于在节点上安装了一个监控器)与回调方法,当节点信息发生变化时,会触发事件,事件的发生会触发我们的回调方法,这样我们的监控目的就达到了,具体的使用代码可以参考Zookeeper的客户端API,这里只做原理介绍。
Zookeeper特征
顺序一致性:Zookeeper的集群中只leader才有写的权限,而一个集群中只有一个leader,因此它具有顺序一致性。
原子性:Zookeeper可以保证数据的操作要么成功要么失败,没有其他结果。
统一视图:Zookeeper集群中每个服务器都是相同的
可靠性:一旦应用了更新,整个集群都保持一致,直到下一次客户端覆盖更新
及时性:系统保证客户视图在特定的时间范围内是最新的。
Zookeeper的集群
Zookeeper是高可用的,一般情况下它都是集群使用的,不会单独使用,它的集群结构类似于redis的主存复制结构。
在Zookeeper的集群中,有三类角色,leader、follower与observer,leader是集群中的老大,提供读写操作,follower提供读操作与进行leader选举,observer只提供读操作,observer可以说是Zookeeper角色的扩展,在集群中大部分的服务器为observer角色,这样就可以保证在leader挂掉后,follower很快的就可以选出新的leader。
一个集群中只有leader可以写数据,在Zookeeper中任何一个操作leader都会生成一个自增的编号(id).
Zookeeper集群中任何一台服务器都与其他服务器连通。
Zookeeper集群具备高可用是因为当leader挂掉后,它集群内部自己就可以非常快的选出新leader。
Zookeeper集群在启动时就确定好了集群中Zookeeper服务器的数量与等级(等级其实就是一个数据,在选leader时用到,这些都是在配置文件中配置)。
Zookeeper选举时采用过半机制,在选举时比较两个参数:第一,自己的数据是否是最全的;第二,自己配置文件中的等级,等级越高越好。
Zookeeper使用的算法协议
Zookeeper使用ZAB算法协议,它是对paxos一致性协议的简化与改进,二者的大体思想是一致的。ZAB中A代表原子性,B代表广播,是一个原子广播协议,下面我用客户端新增一个节点来简要概述下ZAB的协议思想(过半+2阶段提交):
1、客户端与Zookeeper集群中的任意一台服务器建立连接,假如是follower。
2、客户端发出一个创建节点请求,follower会将请求发送到leader,leader接到请求后会向集群广播说我要创建一个编号是XXX的节点。
3、集群各服务器收到广播后进行写日志文件,写完后返回给leader,我同意创建。
4、当leader收到过半的同意后,就会再发一条广播,我已经建好了XXX节点。
5、各集群节点将在自己的内存中创建XXX节点,然后返回ok给leader。
6、leader给当初要收到客户端请求的服务器返回创建结束,然后该服务器再返回给客户端。
注:leader端为每个其他链接维护了一个队列,leader发出的请求会放在对应的队列里
响应式编程
Zookeeper支持异步响应式编程。
Zookeeper的使用场景
分布式配置
在分布式环境中,当我么需要进行配置文件修改时,可以使用Zookeeper来进行,这样就可以不用我们手动的去为每一台机器做配置文件修改。
思路:将配置文件信息记录在Zookeeper服务器上,各服务机去监控服务器上存配置文件的节点,当我们要更改配置时,只需要更改Zookeeper服务器上的配置文件,当做了修改后,会触发事件进而会回调各服务机上的回调方法,我们只需要在回调方法里去读新的配置文件,然后应用即可。
利用Zookeeper的watch功能。
分布式锁
在单机环境下,对于竞态资源我们可以利用各种锁机制来协调各线程使用,但在分布式场景中这些锁都不再起作用,我们需要一把能在分布式环境下使用的锁来应对分布式环境(锁在一个公共的地方)。而Zookeeper正是一个很好的选择。
思路:每个需要锁的人可以监控Zookeeper的一个节点(节点就是锁),当一个人用完之后可以将其删除,删除后其他监控的人就可以得到通知,它来创建一个节点,相当于又获得了锁。
Zookeeper做分布式锁:
利用节点做锁;
首先它支持创建临时节点,这保证了它不会死锁;
其次watch可以保证当锁释放后其他人能得到通知,及时的获取锁;
还有sequence可以将需要锁的人串成一个链,这样当锁释放后不会造成所有人去争抢锁,造成系统的压力。
sequence的使用,我们可以在同一个父目录下让每个需要锁的人都创建一个临时sequence节点,排序后,最小的先获得锁,让后面的节点watch前面的节点,当前面的节点释放锁后,通知会传给后面的节点,这样相当于依次获得锁