1 zookeeper概念
ZooKeeper是一个开源的分布式协调服务. ZooKeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。
1.1 节点介绍
永久节点, 永久排序节点, 临时节点, 临时排序节点
排序节点自动生成序列号,如节点名称为"node-",自动添加为"node-1",顺序添加为"node-2".
1.2 架构
官方架构图:
图中的server中只有一个是leader, 其它的都是learner, learner包括follwer和observer.
客户端可以和任意一个server连接. 其中leader可以处理读写请求, 但是follower只能处理读请求, 当接到写请求的时候转发给leader.
由于客户端连接可能是follwer,并且leader写入是在大多数都成功的情况下就会成功, 导致少部分follwer可能没有及时更新, 这些follwer上的client获取的数据可能不是最新的. 如果client要获取最新数据, 可以调用sync接口.
顺序性/数据一致性: 全局顺序; client顺序;
2 zk选主流程
2.1选主作用
在开发带有状态的服务的时候,由于数据之间有关联性, 只能允许有一个主服务出现, 然而一个服务可靠性没那么高, 需要其它从服务备份或者说监控主服务, 一旦发现主挂了或者说出现了问题, 从就及时升级从为主, 不影响整个系统的正常运行. 在一些数据服务中, 如果对一致性要求不高的情况下从也可以提供查询的请求, 从更新数据可以由主发过来, 或者约定共同的文件来读取.
2.2 正常流程
正常流程分为如下三步:
① 各个服务都去抢主,在zk表现为都去注册一个临时节点, 比如叫做 /leader
② 只有其中一个抢主成功,成功的服务执行主的行为
③ 其它从监听这个临时节点,一旦发现临时节点消失, 所有从再重新执行第一步.
2.3 异常流程
正常流程是主挂了,从监听到了, 从升为主继续提供服务. 然而在分布式设计当中, 网络分区情况需要谨慎考虑.
假如主失去了和zk集群之间的连接, 在规定的session timeout 时间内还没恢复的话, 临时节点在zk里面就会被删除, 其它从就会监控到这个消息, 立马发生抢主的事件, 而主这个时候由于网络的问题还没收到session timeout事件, 这就造成其中一个从升为了主, 老的主还存在的情况. 出现两个主, 俗称脑裂. 在一些对双主敏感的数据服务中, 对双主是禁止同时出现的. 比如两个主都会在后台操作一个网络文件, 那么这个文件就很可能会出现数据错乱问题.
那么如何解决双主问题,方案一, 容易想到的是在新主启动时候, 先通知老主挂掉或者切从, 不过通知老主的时候, 有可能老主是正常挂掉, 那么是始终通知不到的, 那么新主这边是不知道因为网络问题没通知到还是因为老主是挂掉的, 所以通过网络来通知老主也不是特别靠谱.
另外一种方案,也是考虑在新主这边去封禁公用的资源文件, 让老主无法在这些文件上继续操作, 那么老主就名不符实, 所有的操作都会失败. 然而这个方案需要新主能知道老主正在写入哪些文件的能力, 并且确保老主之后不会新增文件或者删除文件.
上面这种方案看起来也各种约束,并且如果老主在操作的文件过多, 去封禁这些文件耗时过久, 影响了系统可用性时间. 并且封禁并不是原子操作, 可能部分成功, 部分失败. 封禁过程, 也可能在某个时间, 一部分文件不能操作,一部分文件可操作, 容易出现数据不一致的问题.
这样看来让新主去通知老主这个方案并不是特别可靠,那么是否可以让老主自己通知自己挂掉呢.
在主与zk发生网络分区的时候, 主是可以马上收到disconnect事件的, 如果在session timeout 时间段内, 还没重连成功, 主也没法及时收到session timeout 事件, 这个时候主认为自己还是主就会出现双主. 问题在于如何在session timeout时间段内让主感知到然后挂掉,这样通过在时间上保证了先后顺序, 就不会出现双主. 那么是在disconnect的时候就挂掉么,这样很容易出现网络波动, 导致经常主被挂. 可以制定一个续约时间, 这个续约时间小于session timeout, 在这个需要续约时间内还没恢复就让主自己挂掉.
这次写的比较仓促,还有很多问题没写清楚, 比如是否会miss掉disconnect事件, 比如直连的zk节点是好的, 但是这个zk节点和主节点发生了分区, 这个时候会不会收不到disconnect事件.
后面还想分析下zk的一致性属性. 待后续更新.
网友评论