协调
组件客户端第一次请求发给服务器2,将flag值修改为false,第二次请求被负载均衡到服务器1,访问到的flag也会是false
一旦有节点发生改变,就会通知所有监听方改变自己的值,保持数据的一致性(watch机制) => 会不会改变的太频繁了
后面讲述
比如我的登录信息,单独放在哪一台主机都不合适,这时,就可以将登录信息放在zookeeper中
dataDir
: zookeeper的数据存储在内存中,为防止数据丢失,需持久化
到磁盘
与Redis不同,这两种模式zk都默认开启了,在恢复时,先恢复快照文件中的数据到内存中,再用日志文件做增量恢复。
类似Linux的文件目录:
zk的数据存储基于节点,这种节点叫做Znode,但不同于树的节点,Znode节点的引用方式是路径引用,类似文件路径:/动物/猫
创建节点:create /test1
,create /test1/sub1
存储数据(如上文说的存储session信息):create /test2 session
获取数据: get /test2
包含4部分:
元数据
(get -s /tets2
可查看,创建时间、版本号等)zk节点创建(本文都是通过zkCli客户端创建,java有一个curator客户端,这里不做记录)
create /test3
会提示/test3
已存在create -s /test3
会创建/test300000000001
节点服务注册与发现(注册中心)
的效果临时节点不断发送会话续约心跳,当停止发送心跳后,zk服务器的定时任务会发现这些未续约的session并删除:
服务注册与发现:服务停供者P连接zk,创建临时的znode,这样客户端C就可以访问到这个服务了,但如果这个服务P下线了,临时znode节点被删除,客户端就不能访问服务P了
临时节点创建:create -e /test4
create -e -s /tets4
delete -v 0 /test2
,删除前不上锁,删除时如果发现版本号变化,则删除失败分布式锁:一个请求发送服务器1,zk服务器对资源A上锁,后续当请求负载均衡到服务器2,服务器2
上的资源A也需要被锁
读锁:上读锁的前提是资源A没有上写锁
写锁:上写锁的前提是资源A没有上任何锁
如:
如果有100个并发,都在上写锁,那么后面的99个节点都要监听第一个节点,等1号节点释放了,另外的98个节点又要监听2号节点
解决:链式监听,当前节点监听上一个节点是否释放
watch机制:可以理解成注册再特定znode上的触发器,当这个znode改变时,也就是调用了create
、delete
、setDate
等命令时,会触发znode上注册的对应事件,请求watch的客户端会接受到异步通知。
具体交互:
主要讲述集群的选举和数据同步
搭建4个节点,其中一个observer
注意配置三类端口:
如果只填一个zk服务器,那就和单机集群没区别了,zk服务器挂了后,就不会连其他zk服务器了
原子广播协议:zk为了保证数据的一致性,使用了ZAB(Zookeeper Atomic Broadcast)协议,这个协议解决了zk崩溃恢复和主从数据同步的问题
zk集群的主节点一般不给客户端直接连,而是用于服务器数据同步
节点成为leader的条件,投票箱中有超半数的投票,所以zk集群中的结点数量一般是奇数个。
如三台zk服务器,主要票数达到2就能完成leader选举,而如果是4台,则需要票数达到3。
对于上文的集群配置,第二台服务器会成为leader,选举过程如下:
leader建立完成后,leader周期性地向follower发送心跳(ping命令),当leader崩溃后,follower发现通道已关闭,
于是进入到looking状态,重新进行选举,此时集群不能对外提供服务
客户端连接一个zk服务器(follower),向该服务器写了一个数据DA,那么这个数据需要同步到所有服务器
按步骤解释:
DA
存储到自己的磁盘,而不能直接写内存,要写就所有
服务器一起写
所有
是指集群一切正常的情况下DA
,有的却没有,造成数据不同步半数以上
,才能将数据写到内存
两阶段提交
:写数据文件,再写内存的方式,防止有的服务器有数据,有的却没有
强一致性
: 如果集群中的一台服务器与leader的通信出现故障了,那么这台服务器将暂时无法同步数据DA
,但是等通信恢复了,数据DA
还是会同步到这台服务器,现实各个服务器数据的顺序一致性
这三项最多只能满足两项:
如我向银行服务A存储了5k元,在服务器B同步服务器A的过程中(如网络阻塞),我查询资金的请求可能经过分布式网关被转发到了服务B:
CAP的一致性是强一致性,而Base理论的核心思想是即使无法做到强一致性,但可以采用适合的方式达到最终一致性
zk追求的是CP,在进行选举时,集群不对外开放,选举完成后要进行数据同步,这一不可用的过程通常在30s~120s之间。
zk在收到半数以上的ack后(如果要收到全部follower的ack,会降低集群的同步效率),就会写内存,因此会造成部分follower没有同步数据:
zk在数据同步时,追求的并不是强一致性,而是顺序一致性(事务id的单调递增),如集群启动后,写如第一个数据后,写成功的服务器事务id为1,而写失败的服务器事务id为0,等再写第二个数据时,
网络阻塞的服务器一定会同步第一个数据,再同步第二个数据。也就是如果一个事务A在事务B之前执行,那么任何情况下,事务A都必须在事务B之前执行。
如果不保证顺序执行,同步失败过的服务器SA可能会产生旧值,比如第一个同步a=1失败,后续同步a=2,此时各个服务的a都应该是2,但SA由于错序执行,a又变成1了。
个人猜想:在分布式锁中,当服务在同步/znode时,各个服务器都加了锁/znode/write001,然后又释放了,如果SA同步失败后,又乱序执行,会导致这台服务器永远也访问不了这个数据了。
早期zk用NIO,后面的版本用netty
NIO:同步非阻塞的网络模型(类似多路复用)
BIO: