1 ZooKeeper
它是一个为分布式协调服务,那么它提供了一个分布式锁服务,用以协调其他分布式的应用。
它是Google的Chubby一个开源的实现,Google的项目一般不开源,因为开源后别人也用不了,它的服务节点都非常之大
ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在zookeeper-3.4.3\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。
2 在不知道 ZooKeeper 内部机制的情况下,很多源码是看不懂的
很多技术都依赖于zookeeper 动物管理员,而其他技术都有动物与之对应
做分布式移植性、高可用
案例1 基于zookeeper rmi高可用
案例2 基于zookeeper redis高可用 redis 3.0后有了自己的高可用
案例3 分布式锁的实现
3 大部分分布式应用都需要 1 主控 2 协调器(控制器)来管理分布的子进程(资源即存储--例如一般元数据
为了协调一致性,一般都放到zookeeper里,任务分配即计算)
keepalived是对lvs做高可用的,是前端的高可用。后台大部分用ZooKeeper做高可用
keepalived不同于zookeeper的最大的缺点是keepalived 可扩展性差
4 优点
1 最终一致性。
例如:有3台机器安装了zookeeper,每一个客户端登录时,看到的是同一个视图
当有其中一台数据被更新时,其他两台会定时的去同步数据,注意不是实时的,因此叫做最终一致性
如果需要最新的数据,应该在读取数据之前调用 sync()接口
2 各个客户端相互独立
3 原子性,对于整个集群来说(3台),要么全部更新成功,要么全部更新失败,不会有中间状态
4 顺序性,对于client端,先请求,先处理。因zk采用了递增的事务id号(zxid)来保证
5 zookeeper工作原理
1 zookeeper在启动时从磁盘加载到内存,为提高处理速度 每个server都存有一份数据
2 zookeeper启动时,将从实例中选举一个leader(主),其他的就变为了follower。怎么选举呢,paxos
3 leader负责数据的更新操作 由leader发起投票
4 当大多数server在内存中修改数据成功,才算操作成功
6 先说一下 Paxos
它是一个基于消息传递的致性算法,还被认为是 到目前止唯一的分布式致性算法,其他的算法也都是他基础上改进或简化
Paxos 所要注意的点
1 当提案的编号大于我手上的编号就默认同意,投赞成票
2 有一个议员发 了一个提议:将电费设定为 1元/度。
他会先看自己的记事本,嗯当前提议编号是0度。那么 我给我自己投一票,我的编号就修改为1了
编号就是 1,于是他给所有议员发消息,其他议员收到消息后,发现记录本上的数字为0,则赞成 回复给发消息的人,然后修改为1
注意,当发送提案的议员收到半数以上的回复时,它就会给所有人发通知,1号议案成立。其他人就会修改编号。
小岛--ZK Server Cluster 议员--zkServer 提议--ZNode(文件夹)增删改,注意 查看则不是提议
提议编号(PID)--Zxid(Zookeeper Transaction Id)自增 发起提议时会对应一个PID 正式法令--所有ZNode及数据 ,更新内存中的数据
7 Leader角色
解决冲突(两个PID相同,必然产生冲突) 例如 当两个人同时提出 1(PID)号提议时,由上面分析,只是生效了1个提议,而另一个人的提议就被拒绝了
那么,由于人人平等,这时就产生一个“活锁” (大家都没死都在动,但一直解决不了冲突问题)。必须有个总统leader
来提出
8 在选举Leader的时候,所有服务器都处于安全模式
9 paxos是zookeeper的灵魂,当然Zeekpeeper还有自己的东西:Session,Watcher,Version等
10 zookeeper的leader挂了后,它会自己选举出 leader,解决单点故障。自己对自己是高可用
当我有偶数台时,例如:
3台(LFF)(半数是1.5台) 也就说得保证有2台正常运行
4台(LFFF)(半数是两台),也就说得保证有3台正常运行
发现,当同样都是L挂掉后,从高可用角度分析2者可以实现样的效果,所以没必要多出一台
11 在zookeeper的配置文件 zoo.cfg 中
server.1=server1:2888:3888
server.A=B:C:D:
2888 表示的是这个服务器与集群中的Leader 服务器交换信息的端口;例如监控 flower的状态
3888 表示的是万一集群中的Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口
tickTime:发送心跳的间隔时间,tickTime=2000 单位:毫秒
dataDir:zookeeper保存数据的目录。启动时加载到内存,定期存储到磁盘 dataDir=/Users/zdandljb/zookeeper/data
clientPort:客户端连接Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。 clientPort=2181
initLimit:这个配置项是用来配置Zookeeper在初始化时去连接Follower,若发现隔了5*2000=10 秒秒了还没有收到Follower服务器心跳的心跳,那么我就认为连接失败。
syncLimit:这个配置项标识Leader 与Follower 之间发送消息,若在请求和应答时,过了2*2000=4 秒还没有收到,则认为Leader挂掉了。那么它就会去选举,但是这时
其他flower连接正常,那么其他flower事不会去选举的
12 查看zookeeper的角色 /home/zookeeper-3.4.6/bin zkServer.sh status
注意 谁是leader 谁先启动起来
zookeeper集群自己是高可用的
我们leader kill掉后,剩余两台,在这两台里谁先发现 leader挂掉后,它就会投票推举自己当 leader
13 zookeeper 的角色
1 Leader角色
2 follower 从
3 Observer 记者 ,不参与投票,Observer可以接受客户端的读取数据请求,增删改的请求要转发给leader,
若只通过增加follower的数量来水平扩展的话,虽然提高了读取速度,但增加了选举时间(因为当leader发起一个议案,要等
半数以上机器修改数据成功才算投票完成),所以zookeeper后来创建了observer这个角色
所以,zookeeper数量少的话可以不要 observer
同步leader状态,提高客户端读取速度,扩展系统功能。队列保持 client端的顺序性。
Observer是zookeeper的新特性,但用的不多
若一个集群里出现10台 zookeeper时,那么说明 及群里会有上千台服务节点来运算
集群里的服务器(不包括observer)要超过有效(不包括observer)服务器的半数 活着 这个集群正常
注意有3个角色,
leader里也会维护一个队列,所有的议案都会放到队列里,是否发出议案由 leader决定,每一个议案都有一个PID,
每一个PID对应一个 zxid 事物id(自增)
14 zookeeper 集群一般会独立存在,其他集群会 1用zookeeper来实现高可用 2 用zookeeper是存放元数据
(因为zookeeper本身也是高可用,而且又是分布式的,可以保持数据的最终一致性)
15 Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。
实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。
当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,
且大多数Server完成了和leader的状态同步以后,
恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
paxos协议是为了实现选举,选举的目的是为了解决冲突,议案转为都由leader发出
zookeeper 的选主过程使用paxos,数据复制使用Zab
16 如果网络不段的情况下 leader去更新 follower中数据一般都会成功的
当被挂的机子重新起来的时候,会先去同步leader中的数据
17 为什么必须要有半数以上的机器把数据更新成功才算成功,因为它是为了保证,万一leader挂掉后,至少还有半数以上机器拥有最新的数据,
而新的leader的数据必须是最新的。如果拥有旧数据的人要求当leader的话,由于他的PID小,会被拒绝,也可以这么认为:就是说
拥有最新数据的机器多点,也就是候选人多。
18 zxid 是为了 在znode在增删改时事务。zookeeper里管每条数据为znode节点
ZooKeeper状态的每一次改变, 都对应着一个递增的Transaction id, 该id称为zxid.
由于zxid的递增性质, 如果如果事务1 zxid1和事务2 zxid2同时要求leader修改数据znode,且zxid1小于zxid2, 那么zxid1会先进行.
创建任意节点, 或者更新任意节点的数据,
或者删除任意节点, 都会导致Zookeeper状态发生改变, 从而导致zxid的值增加.
19 注意znode和目录的区别是 它本身也可以写数据
cli.sh ls / -h
create /20160422 helloword!!!
get /20160422
cZxid = 0x4900000002 create的时候,create这个事务的id 0x49表示leader
ctime = Fri Apr 22 11:33:59 CST 2016
mZxid = 0x4900000002 修改的事务id
mtime = Fri Apr 22 11:33:59 CST 2016
pZxid = 0x4900000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 0
create /20160422/curry currycontent!!
get /20160422
set /20160422/curry currsafsa!!
cZxid = 0x4900000003 事务id加1
20 注意leader重新选择后 mZxid统统改成0
21 Znode有两种类型,短暂的(ephemeral)和持久的(persistent),默认是 persistent
创建节点时的两种类型 断开连接超过 设置的时间后,如果该节点是 ephemeral,则删除
Znode是zookeeper的一种数据节点类型
22 zookeeper的watcher会监控 zookeeper的znode节点,一个znode对应一个watcher
zkfc就相当于zookeeper的客户端,当namenode启动后,zkfc会在zookeeper中建立一个临时 znode节点,
当namenode挂掉后,zookeeper会删除这个临时节点,同时被watcher监控到zookeeper节点发生了变化,给其他namenode发送消息
23 yarn是mapreduce的资源调度框架。resourceManager和zkfc是zookeeper客户端
24 案例
用zookeeper对3个 基于RMI协议服务的高可用的底层实现。
思路:
把3个server的url地址存到zookeeper中
client 不去直接找server,它去找zookeeper,去里面去可用的url
client再去连 server
Dubbox里面无非就是封装了各种各样的协议,然后全帮做了一个高可用
zookeeper做分布式移植性
26 案例 zookeeper的分布式锁
假设有100服务器,user1 user2 两个用户。谁获得了分布式锁,谁就可以控制这个集群
用zookeeper来实现分布式锁
zookeeper作用
1 自己高可用
2 znode节点(4中类型)
3 在znode节点上提供一个watcher
4 提供了一个java类
zookeeper的选举只是自己leader的选举,而不是对其客户端的选举
对于分布式锁,zookeeper创建一个分布式锁,然后让客户端按顺序的来获取分布式锁
1 客户端要排队,首先应在zookeeper中创建一个节点,证明你来排队了
2 获取到这个队列,判断是否在第一个,是就得到锁,不是则监控排在自己前面的那个节点(也就是往它身上放个 watcher
然后就是等待watcher给你发消息)
watcher发送消息只会发一次,如果你想要一直检测则需要在发消息时再注册一次