ZooKeeper学习

ZooKeeper是什么

Apache ZooKeeper是一个高可用的分布式协调中间件。Goole Chubby的一个开源实现,主要解决分布式一致性问题,提供分布式锁服务。

分布式一致性问题

典型的拜占庭将军问题

分布式锁服务

Chubby提供了一种粗粒度的分布式锁服务,通过创建文件的形式实现,server向Chubby中创建文件表示加锁,创建成功则表示抢到锁。

由于Chubby没有开源,因此雅虎基于Chubby思想开发了一个类似的分布式协调组件Zookeeper,后捐献给了Apache。

zab协议 过半提交
epoch myid,zxid

2PC提交

当一个事务操作需要跨院多个分布式节点的时候,为了保持事务处理ACID特性,就需要引入一个“协调者”(TM)来同意调度所有分布式节点的执行逻辑,这些被调度的分布式节点被称为AP。TM负责调度AP的行为,并最终决定这些AP是否提交事务,整个过程分为两个阶段提交,因此叫2PC。

  • 阶段一提交事务请求
    • 事务询问,协调者向所有参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者响应
    • 各参与者执行事务操作,并将Undo和Redo信息记录到日志中,尽量把提交过程中所有消耗时间的操作和准备都提前完成确保后面100%成功提交事务
    • 参与者向协调者反馈事务询问的响应,即如果成功执行返回yes,表示事务可以执行;如果执行失败返回no,表示事务不可以执行。
  • 执行事务提交
    • 协调者根据参与者的反馈情况来决定最终是否可以进行提交事务。

角色

Leader角色

领导角色,负责发起投票,处理事务请求

Follow角色

跟随者,用于接受客户端请求并返回结果,投票过程中参与投票,处理非事务请求

Observer角色

zookeeper3.3引入一个全新的服务器角色,用于观察zookeeper集群中的最新状态变化并将这些变化同步到observer服务器上。不参与任何形式的投票,且observer服务器只提供feign事务请求服务。

Zookeeper的安装部署

单机部署

将conf目录下的zoo_smaple.cfg文件复制一份重命名为zoo.cfg,修改文件中dataDir

集群部署

  1. 修改配置文件

server.1=IP1:2888:3888

server.2=IP2:2888:3888

server.3=IP3:2888:3888

2888表示zookeeper的端口,3888表示选举leader的端口

  1. 新建dataDir目录,设置myid

在每台zookeeper机器上,dataDir目录下创建myid文件,文件内容为服务器serverID,且需要确保每台都不重复

  1. 启动

服务命令

  • 启动--./bin/zkServer.sh start
  • 查看--./bin/zkServer.sh status
  • 停止--./bin/zkServer.sh stop
  • 重启--./bin/zkServer.sh restart
  • 连接服务端--zkCli.sh -timeout - -r -server ip:port

应用

节点

zookeeper节点称为ZNode是zookeeper的最小单元。每个ZNode上都可以保存数据和挂载子节点。构成一个层次性的树形结构。

ZNode包含stat-状态信息包含数据变化的时间和版本,value-值。

  • 持久节点

创建后会一直存在zookeeper服务器上,直到主动删除

  • 持久有序节点

每个节点都会为它的一级子几点维护一个顺序

  • 临时节点

生命周期和客户端会话绑定,当客户端失效则自动清理

  • 临时有序节点

在临时节点基础上增加排序

  • 容器节点

容器节点下最后一个节点被删除,容器节点自动删除

  • TTL节点

设置一个存活时间,存活时间内如果没有任何修改并且没有任何子节点自动删除

Watcher 发布订阅

zookeeper允许客户端向服务端注册一个watcher监听,当服务端的一些指定事件触发了watcher,向客户端发送一个通知。watcher是一次性的,即一旦触发后,watcher就失效。

ZooKeeper提供三种机制针对Znode进行注册监听:

  • getData(),获取指定节点value信息,并且可以注册监听,当节点创建、修改、删除时会触发相应的事件通知。
  • getChildren(),获取指定节点的所有节点,并且可以注册监听,当节点创建、修改、删除时会触发相应的事件通知。
  • existis(),判断节点是否存在,同样可以注册指定节点的监听,监听类型同getData()

权限

  • IP,通过IP地址粒度来进行权限控制。
  • Digest,通过类似于username:password
  • World,最开放的模式,World:anyone
  • Super,超级用户

分布式锁

唯一性实现

利用Zookeeper节点的特性实现分布式锁,即同级节点的唯一性,多个进程往Zookeeper的指定节点下相同名称的节点只能有一个成功,失败的节点通过Watcher机制监听,一旦监听到节点的删除事件则再次触发所有进场去写锁。

该方式容易产生惊群效应,简单来说就是如果存在很多客户端等待获取锁,当成功获取到锁的进程释放该节点后,所有处于等待的客户端都会被唤醒,此时大量子节点会被唤醒,会对Zookeeper服务器性能产生影响。

有序节点实现

每个客户端都在指定的节点下注册一个临时的有序节点,越早创建的节点编号越小,依据最小的节点获取锁。该方式只需要每个节点监听编号比自己小的节点,当比自己小的节点删除,客户端收到watcher事件,此时再判断自己的节点是否是最小的,如果是则获得锁,否则不断重复该过程。

curator分布式锁封装

curator对锁进行了封装,提供了InterProcessMutex api。除此以外还提供了leader选举,分布式队列等功能。

    public static void main(String[] args) throws Exception {
        CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().
                connectString("127.0.0.1:2181").sessionTimeoutMs(50000000).
                retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
        curatorFramework.start();

        final InterProcessMutex lock=new InterProcessMutex(curatorFramework,"/locks");
        for(int i=0;i<10;i++){
            new Thread(()->{
                try {
                    lock.acquire(); //阻塞竞争锁
                    System.out.println(Thread.currentThread().getName()+"->成功获得了锁");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                try {
                    Thread.sleep(400000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        lock.release(); //释放锁
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            },"Thread-"+i).start();
        }
    }

Leader选举

几个参数

  1. 服务器ID(myid)

  服务器编号,编号越大在选举算法中的权重越大

  1. zxid事务id

  zxid即事务id,高32位epoch表示Leader周期变化,每一个新的Leader选举后epoch+1,低32位表示消息计数,值越大说明数据越新,在选举算法中的权重也越大

  1. 选举状态

  LOOKING-竞选状态

  FOLLOWING-随从状态,同步Leader状态,参与投票

  OBSERVING-观察状态,同步Leader状态,不参与投票

  LEADING-领导状态

选举流程

  1. 初始状态每个Server发出一个投票,投票包括myid、zxid,第一次将票投给自己并发送给集群中的其他服务器。
  2. 接受到各自服务器的投票,首先判断有效性,如是否是本轮投票(检查epoch)、是否是LOOKING状态的服务器。
  3. 选举,优先比较epoch,其次检查zxid,zxid大的优先作为Leader,如果zxid相同,比较myid,myid大的作为Leader
  4. 统计投票,判断是否存在过半的机器受到相同的投票
  5. 更改服务器状态

Curator支持两种选举方式:Leader Latch和LeaderSelector Election

Leader Latch

参与选举的节点会创建一个顺序节点,其中最小的一个做为master节点,没抢到的节点都监听前一个节点的删除事件,当master节点手动删除,或者挂了,后续节点抢占master。spark就是使用这种方式。

LeaderSelector

与Leader Latch的区别在于,LeaderSelector释放领导权后,还可以继续竞争

LeaderSelectorClient leaderSelectorClient=new LeaderSelectorClient("ClientA");
LeaderSelector leaderSelector=new LeaderSelector(curatorFramework,"/leader",leaderSelectorClient);
leaderSelector.autoRequeue();//放弃leader后还要重新参与选举
leaderSelectorClient.setLeaderSelector(leaderSelector);
leaderSelectorClient.start(); //开始选举

数据同步

zookeeper通过三个角色组成了高性能集群,在zookeeper中客户端会随机连接到任意一个节点,如果是读请求则读取数据,如果是写请求则转交给leader提交事务,然后leader会广播事务,只要有超过半数节点写入成功,则写请求提交。

ZAB协议

Zookeeper Atomic Broadcast协议是为分布式协调服务专门设计的一种支持崩溃回复的原子广播协议。包括两种基本模式:消息广播和崩溃恢复。

  • 消息广播
    过半的Follower节点完成和Leader状态同步以后,那么整个集群就会进入消息广播模式。这个时候如果有新服务器加入到集群,那么该服务器会进入数据恢复模式,且和Leader节点进行数据同步。

  实现原理:

  1. Leader收到消息请求后,将消息赋予一个全局唯一的64位id(zxid),通过zxid大小的比较实现有序特征
  2. Leader为每个follower准备了一个FIFO的队列,将带有zxid的消息做为proposal分发给所有follower
  3. follower收到proposal,先写入磁盘,然后向Leader回复ack
  4. Leader收到半数节点的ACK后,Leader就会发送commit命令,同时本地执行该消息
  5. follower收到commit命令后会提交该消息

与2PC事务不一样的地方在于,ZAB协议不会终止事务,follower要么回复ACK要么抛弃命令,Leader只要保证消息过半数就会提交,虽然在某一个时刻follower和leader状态不一致,但提升了整体性能。这种数据不一致,由ZAB协议的恢复模式同步数据。

  • 崩溃恢复
    当集群启动或者Leader节点出现网络中断宕机的时候,ZAB协议就会进入恢复模式并选举新的Leader,当Leader选举后,且集群中过半机器和该Leader节点完成数据同步,协议退出恢复模式。

  实现原理:
ZAB协议确保已经被Leader提交的事务Proposal能够提交、同时丢弃已经被跳过的事务Proposal。

  1. 如果Leader选举算法能够保证新选举出来的Leader服务器拥有集群中所有机器最高编号(zxid最大)的事务Proposal,那么就可以保证这个新选举出来的Leader具有已经提交的Proposal。
  2. zxid是64位,高32位是epoch编号,没经过一次Leader选举产生一个新的Leader则新的Leader会将epoch+1,低32位是消息计数器,没接受一个消息值+1,新Leader选举后值重置为0。这样设计的目的是确保Leader挂了之后它不会被选举。当老Leader做为follower接入新的Leader后,新的Leader会将它拥有的epoch号中未被commit的proposal清除。

Zookeeper一致性

根据zab协议的同步流程,zookeeper集群内部数据副本是基于过半提交策略,意味着它是最终一致性。加上zookeeper提供的分布式锁服务,因此它是顺序一致性。

顺序一致性是在分布式环境中实现分布式锁的基本要求,即当多个程序抢锁,未抢到锁的程序看到锁的状态是抢到锁的程序状态。

你可能感兴趣的:(学习笔记,java,zookeeper)