zookeeper总结

zookeeper是什么

分布式协调框架

zookeeper主要功能

  1. 类似于文件系统的节点存储和查询功能,且写入节点保证数据的一致性
  2. 能够监听节点的数据变化,接收变更的通知

基于以上两点功能,实现负载均衡、分布式锁、master选举、分布式队列、配置中心等

zookeeper的特性

  • 顺序一致性:客户端发起的事务,会严格根据顺序逐一执行
  • 原子性:所有机器的数据处理结果是一致的,要么都成功,要么都失败
  • 单一视图:客户端连接集群中任一节点,数据都是一致的
  • 可靠性:数据能够可靠地保存在集群中
  • 实时性:一旦事务被成功应用,就能够读取刚刚应用的事务数据(接近实时)

zookeeper的概念

数据模型

  • 概念:zookeeper的数据模型与一个文件系统类似,每一个节点称为znode,每个节点上都可保存数据和挂载数据,结构图如下
    在这里插入图片描述
  • 节点种类:
    1. 持久化节点:创建后会一直存在,除非主动删除
    2. 持久化有序节点:每个节点都会维护一个顺序
    3. 临时节点:生命周期与客户端会话一致,客户端会话失效,节点自动清理
    4. 临时有序节点:多了有序性
  • 节点属性:
    • cversion = 0 子节点的版本号
    • aclVersion = 0 表示acl的版本号,修改节点权限
    • dataVersion = 1 表示的是当前节点数据的版本号
    • czxid 节点被创建时的事务ID
    • mzxid 节点最后一次被更新的事务ID
    • pzxid 当前节点下的子节点最后一次被修改时的事务ID
    • ctime = Sat Aug 05 20:48:26 CST 2017
    • mtime = Sat Aug 05 20:48:50 CST 2017

会话

  • 概念:zookeeper的会话是客户端与服务端进行的连接会话,主要的状态过程如下

在这里插入图片描述

Watcher

  • 概念:类似于java中的监听机制,客户端可以向服务端注册一个watcher监听,当服务端的节点触发指定事件的时候,会通知注册的watcher。

ACL

  • 概念:zookeeper提供了节点可设置访问权限的功能,保证节点的安全性
  • 权限分类:
    • CREATE:增
    • READ:查
    • WRITE:写
    • DELETE:删(对子节点的删除)
    • ADMIN:管理
  • 身份认证的方式
    • world:默认方式,相当于全世界都能访问
    • auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)
    • digest:即用户名:密码这种方式认证,这也是业务系统中最常用的
    • ip:使用Ip地址认证

zookeeper安装

单机

  1. 下载zookeeper的安装包
    http://apache.fayea.com/zookeeper/stable/zookeeper-3.4.10.tar.gz
  2. 解压zookeeper
    tar -zxvf zookeeper-3.4.10.tar.gz
  3. cd 到 ZK_HOME/conf , copy一份zoo.cfg
    cp zoo_sample.cfg zoo.cfg
  4. sh zkServer.sh
    {start|start-foreground|stop|restart|status|upgrade|print-cmd}
  5. sh zkCli.sh -server ip:port

集群

  1. 修改配置文件
    server.id=host:port:port
    id的取值范围: 1~255; 用id来标识该机器在集群中的机器序号
    2181是zookeeper的端口; //3306
    3181表示leader选举的端口
server.1=192.168.11.129:2181:3181
server.2=192.168.11.131:2181:3181
server.3=192.168.11.135:2181:3181
  1. 创建myid
    在每一个服务器的dataDir目录下创建一个myid的文件,文件就一行数据,数据内容是每台机器对应的server ID的数字

  2. 启动zookeeper

zookeeper常用API

  • 原生zookeeper:较为原生,未做封装,不是很好用
  • zkclient:异常处理不好
  • curator:异常处理较好,监听机制可以永久监听,且封装了常用的工具,如分布式锁,master选举,队列等,引入包如下:
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.8</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>2.12.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-client</artifactId>
            <version>2.12.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.12.0</version>
        </dependency>

zookeeper使用场景和原理

master选举

  • 概念:多个服务启动后,需要选举出一个master(如定时任务的执行,系统高可用,master-slave模式),当master宕机后,需要再次选举新的master
  • 原理:多个服务写入同一个临时节点,写入成功的则为master,并监听该临时节点,如果master宕机,临时节点也就删除,则其他服务节点接听到删除事件后,再次写入,再次生成一个master
  • 代码实现:
public class LeaderExample {
    // 选举的路径
    private static final String PATH = "/examples/leader";

    public static void main(String[] args) throws Exception {

        List<LeaderLatch> leaderExamples = new ArrayList<LeaderLatch>();
        List<CuratorFramework> curatorFrameworks = new ArrayList<CuratorFramework>();

        for (int i = 0; i < 20; i++) {
            // 创建zookeeper客户端
            CuratorFramework curatorFramework = CuratorTest.newInstance();
            // 创建LeaderLatch
            LeaderLatch leaderLatch = new LeaderLatch(curatorFramework, PATH, "Client #" + i);
            curatorFrameworks.add(curatorFramework);
            leaderExamples.add(leaderLatch);
            // 启动
            curatorFramework.start();
            leaderLatch.start();
        }

        // 启动需要等待
        TimeUnit.SECONDS.sleep(2);

        LeaderLatch currentLeader = null;

        for (LeaderLatch leaderExample : leaderExamples) {
            if (leaderExample == null) {
                continue;
            }
            // 获取领导权的,打印
            if (leaderExample.hasLeadership()) {
                currentLeader = leaderExample;
                System.out.println(leaderExample.getId() + " 作为leader,执行定时任务");
            }
        }
        // 获取领导权的,断开连接
        currentLeader.close();

        // 启动需要等待
        TimeUnit.SECONDS.sleep(2);

        // 新的领导权已分配
        for (LeaderLatch leaderExample : leaderExamples) {
            if (leaderExample == null) {
                continue;
            }
            if (leaderExample.hasLeadership()) {
                System.out.println(leaderExample.getId() + " 作为leader,执行定时任务");
            }
        }

        // 关闭
        for (LeaderLatch leaderExample : leaderExamples) {
            CloseableUtils.closeQuietly(leaderExample);
        }
        for (CuratorFramework curatorFramework : curatorFrameworks) {
            CloseableUtils.closeQuietly(curatorFramework);
        }
    }
}

跟着实例学习ZooKeeper的用法: Leader选举

分布式锁

  • 概念:多个服务对于共享资源的使用需要进行加锁的操作,而这边的锁需要是多个服务共同竞争的分布式锁,原因是进程中的锁无法在服务间共享可见。
  • 原理:分布式锁的加锁操作,同时写入一个zookeeper的节点,写入成功,则获取锁成功;释放锁操作,删除zookeeper的节点,删除完成,则释放锁,其他服务监听到删除时,重新竞争锁。
  • 代码实现:
// 共享资源
public class SharedResource {
    private final AtomicBoolean resource = new AtomicBoolean(false);

    // 使用资源
    public void use(String clientName) {
        // 资源已被修改,则报错
        if (!resource.compareAndSet(false, true)) {
            throw new RuntimeException("resource has been used by client");
        } else {
            // 打印成功使用资源的客户端
            System.out.println(clientName + " use the resource");
        }

        try {
            // 休眠能够产生同步问题
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            resource.set(false);
        }

    }
}
public class DistributedLock {
    private final InterProcessMutex lock;// 锁
    private final String clientName;// 客户端名称
    private final SharedResource sharedResource;// 共享资源

    public DistributedLock(CuratorFramework curatorFramework, String clientName, SharedResource sharedResource, String pathName) {
        // 根据客户端和锁的路径构造锁
        this.lock = new InterProcessMutex(curatorFramework, pathName);
        this.clientName = clientName;
        this.sharedResource = sharedResource;
    }

    public void doWork(long time, TimeUnit unit) throws Exception {
        // 竞争锁失败,则报错
        if (!lock.acquire(time, unit)) {
            throw new RuntimeException(clientName + " can't acquire the lock");
        }
        // 获取锁成功,则使用
        try {
            sharedResource.use(this.clientName);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            // 释放锁
            System.out.println("release lock");
            lock.release();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        // 共享资源
        final SharedResource sharedResource = new SharedResource();
        // 锁路径
        final String path = "/examples/lock";
        for (int i = 0; i < 10; i++) {
            final int p = i;
            new Thread(new Runnable() {
                public void run() {
                    // 创建客户端
                    CuratorFramework client = CuratorTest.newInstance();
                    // 启动客户端
                    client.start();
                    try {
                        // 创建锁
                        DistributedLock lock = new DistributedLock(client, "客户端 " + p, sharedResource, path);
                        // 争抢锁
                        lock.doWork(2, TimeUnit.SECONDS);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    } finally {
                        try {
                            TimeUnit.SECONDS.sleep(2);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        // 关闭客户端
                        CloseableUtils.closeQuietly(client);
                    }
                }
            }).start();
        }
        TimeUnit.SECONDS.sleep(10);
    }


}

跟着实例学习ZooKeeper的用法: 分布式锁

配置中心

  • 概念:分布式服务的配置文件较多,手动维护每个服务的配置成本较大,配置中心可以统一管理所有服务的配置项,配置有变动,服务也可根据推拉模型进行配置的获取
  • 原理:配置中心负责写入配置项到zookeeper的节点中,而服务节点则向zookeeper注册关心的节点,并对其进行监听,监听到变动后,查询zookeeper的数据,然后对本地配置项进行更新

负载均衡

  • 概念:请求/数据分摊多个计算机单元上。
  • 原理:服务生产者启动时,将服务写入zookeeper的临时节点,当服务宕机时,临时节点被删除;服务消费者在使用时,获取临时节点,通过负载均衡算法来获取调用的生产者节点,并监听生产者节点的删除操作,删除时,从本地缓存列表移除节点

zookeeper学习(六)ZooKeeper实现软负载均衡

分布式队列

  • 概念:分布式队列是一个服务间共享的队列
  • 原理:队列入队,通过写入有序的节点;队列出队,通过查询节点,并对节点进行删除;zookeeper的节点能够存储的数据大小存在限制,为1M,且存储大数据量的节点,会导致性能下降,故不要存储的节点数据量过大

跟着实例学习ZooKeeper的用法: 队列

zookeeper集群选举

集群角色

  • Leader:集群的核心
    1. 事务请求的唯一调度者和处理者,保证集群事务处理的顺序性
    2. 集群内部各个服务器的调度者
  • Follower:跟随者
    1. 处理客户端的非事务请求,并将事务请求转发给leader服务器
    2. 参与事务请求提议(proposal)的投票(客户端的一个事务请求,需要半数的服务器投票通过后,才能通知Leader commit;leader会发起一个提案,要求follower投票)
    3. 参与leader选举的投票
  • Observer:观察者
    1. 观察zookeeper集群的最新状态变化,并同步到observer
    2. 处理客户端的非事务请求(不影响集群的事务请求,提高集群的非事务处理能力)

集群服务器组成

由2n+1台服务器(该服务器不包含Observer)组成,不是偶数台的原因是2n-1和2n台的服务器,2n台的服务器并不能增加高可用,因为2n-1宕掉n台不可用,而2n也是宕掉n台后不可用,且2n台需要n+1台服务器投票通过,才能提交事务请求,效率更慢,故服务器是奇数台。

集群选举过程

目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:

  1. 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking。
  2. 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
  3. 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
  4. 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
  5. 服务器5启动,后面的逻辑同服务器4成为小弟。

在这里插入图片描述

理解zookeeper选举机制

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值