a

ansolidll

V1

2022/11/06阅读:42主题:默认主题

zookeeper学习

Zookeeper

1、zookeeper 的java Api介绍

1.1、ZooKeeper常用Java API
  • 原生Java API(不推荐使用)

​ ZooKeeper 原生Java API位于org.apache.ZooKeeper包中

​ ZooKeeper-3.x.x. Jar (这里有多个版本)为官方提供的 java API

  • Apache Curator(推荐使用)

​ Apache Curator是 Apache ZooKeeper的Java客户端库。

​ Curator.项目的目标是简化ZooKeeper客户端的使用。

​ 另外 Curator为常见的分布式协同服务提供了高质量的实现。 ​ Apache Curator最初是Netflix研发的,后来捐献了 Apache基金会,目前是 Apache的顶级项目

  • ZkClient(不推荐使用)

​ Github上一个开源的ZooKeeper客户端,由datameer的工程师Stefan Groschupf和Peter Voss一起开发。 zkclient-x.x.Jar也是在源生 api 基础之上进行扩展的开源 JAVA 客户端。

1.2、创建java 工程,导入依赖
1572839451720
1572839451720
1572839476251
1572839476251
1572839494820
1572839494820

导入依赖

<!--zookeeper的依赖-->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.7</version>
</dependency>
<!-- zookeeper CuratorFramework 是Netflix公司开发一款连接zookeeper服务的框架,通过封装的一套高级API 简化了ZooKeeper的操作,提供了比较全面的功能,除了基础的节点的操作,节点的监听,还有集群的连接以及重试。-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.0.1</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.0.1</version>
</dependency>
<!--测试-->
<dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.12</version>
</dependency>
1.3、创建节点
//1. 创建一个空节点(a)(只能创建一层节点)
//2. 创建一个有内容的b节点(只能创建一层节点)
//3. 创建持久节点,同时创建多层节点
//4. 创建带有的序号的持久节点
//5. 创建临时节点(客户端关闭,节点消失),设置延时5秒关闭(Thread.sleep(5000))
//6. 创建临时带序号节点(客户端关闭,节点消失),设置延时5秒关闭(Thread.sleep(5000))
/**
 *  RetryPolicy: 失败的重试策略的公共接口
 *  ExponentialBackoffRetry是 公共接口的其中一个实现类
 *      参数1: 初始化sleep的时间,用于计算之后的每次重试的sleep时间
 *      参数2:最大重试次数
        参数3(可以省略):最大sleep时间,如果上述的当前sleep计算出来比这个大,那么sleep用这个时间
 */

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3,10);
//创建客户端
/**
 * 参数1:连接的ip地址和端口号
 * 参数2:会话超时时间,单位毫秒
 * 参数3:连接超时时间,单位毫秒
 * 参数4:失败重试策略
 */

CuratorFramework client =  CuratorFrameworkFactory.newClient("127.0.0.1:2181",3000,1000,retryPolicy);
//开启客户端(会阻塞到会话连接成功为止)
client.start();
/**
 * 创建节点
 */

//1. 创建一个空节点(a)(只能创建一层节点)
// client.create().forPath("/a");
//2. 创建一个有内容的b节点(只能创建一层节点)
// client.create().forPath("/b", "这是b节点的内容".getBytes());
//3. 创建多层节点
// (creatingParentsIfNeeded)是否需要递归创建节点
// withMode(CreateMode.PERSISTENT)  创建持久性 b节点
// client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/g");
//4. 创建带有的序号的节点
//  client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/e");
//5. 创建临时节点(客户端关闭,节点消失),设置延时5秒关闭
// client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/f");
//6. 创建临时带序号节点(客户端关闭,节点消失),设置延时5秒关闭
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/f");
Thread.sleep(5000);
//关闭客户端
client.close();
1.4、修改节点数据
//创建失败策略对象
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
//
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181",1000,1000,retryPolicy);
client.start();
//修改节点
client.setData().forPath("/a/b""abc".getBytes());
client.close();
1.5、节点数据查询
RetryPolicy retryPolicy = new ExponentialBackoffRetry(10001);
CuratorFramework client = 
        CuratorFrameworkFactory.newClient("127.0.0.1",1000,1000, retryPolicy);
client.start();
// 查询节点数据
byte[] bytes = client.getData().forPath("/a/b");
System.out.println(new String(bytes));
3.3.6、删除节点
//重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
//创建客户端
CuratorFramework client =
        CuratorFrameworkFactory.newClient("127.0.0.1",1000,1000, retryPolicy);
//启动客户端
client.start();
//删除一个子节点
  client.delete().forPath("/a");
// 删除节点并递归删除其子节点
  client.delete().deletingChildrenIfNeeded().forPath("/a");
//强制保证删除一个节点
//只要客户端会话有效,那么Curator会在后台持续进行删除操作,直到节点删除成功。
// 比如遇到一些网络异常的情况,此guaranteed的强制删除就会很有效果。
client.delete().guaranteed().deletingChildrenIfNeeded().forPath("/a");
//关闭客户端
client.close();

【小结】

  1. Curator是Appache封装操作Zookeeper的客户端, 操作zookeer数据变得更简单

  2. 使用步骤:

    • 创建重试策略

    • 创建客户端 ip:port sessionTimeout, connectionTimeout, retryPolicy

    • 启动客户端 start

    • 使用客户端对节点操作

      -- create forPath, creatingparent........, withMode(CreateMode.持久,临时,有序)

      -- setData 修改数据

      -- getData 查询数据

      -- delete 删除数据, deletingChildrenIfNeeded递归删除

    • 关闭客户端, 测试临时数据时要睡眠一下

2、watch机制【机制重点】

回顾:Zookeeper的应用场景中配置中心,其中看到watch机制

1573197592193
1573197592193
2.1、什么是watch机制

​ zookeeper作为一款成熟的分布式协调框架,订阅-发布功能是很重要的一个。所谓订阅发布功能,其实说白了就是观察者模式。观察者会订阅一些感兴趣的主题,然后这些主题一旦变化了,就会自动通知到这些观察者。

​ zookeeper的订阅发布也就是watch机制,是一个轻量级的设计。因为它采用了一种==推(服务端通知应用)拉(客户端获取服务端数据)==结合的模式。一旦服务端感知主题变了,那么只会发送一个事件类型和节点信息给关注的客户端,而不会包括具体的变更内容,所以事件本身是轻量级的,这就是所谓的“推”部分。然后,收到变更通知的客户端需要自己去拉变更的数据,这就是“拉”部分。watche机制分为添加数据和监听节点。

​ Curator在这方面做了优化,Curator引入了Cache的概念用来实现对ZooKeeper服务器端进行事件监听。Cache是Curator对事件监听的包装,其对事件的监听可以近似看做是一个本地缓存视图和远程ZooKeeper视图的对比过程。而且Curator会自动的再次监听,我们就不需要自己手动的重复监听了。

Curator中的cache共有三种

  • NodeCache(监听和缓存根节点变化) 只监听单一个节点(变化 添加,修改,删除)
  • PathChildrenCache(监听和缓存子节点变化) 监听这个节点下的所有子节点(变化 添加,修改,删除)
  • TreeCache(监听和缓存根节点变化和子节点变化) NodeCache+ PathChildrenCache 监听当前节点及其下的所有子节点的变化

下面我们分别对三种cache详解

2.2、 NodeCache
  • 介绍

    NodeCache是用来监听节点的数据变化的,当监听的节点的数据发生变化的时候就会回调对应的函数。

  • 增加监听
//创建重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
//创建客户端
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181"10001000, retryPolicy);
//开启客户端
client.start();
System.out.println("连接成功");
//创建节点数据监听对象
final NodeCache nodeCache = new NodeCache(client, "/hello");
//开始缓存
/**
 * 参数为true:可以直接获取监听的节点,System.out.println(nodeCache.getCurrentData());为ChildData{path='/aa', stat=607,765,1580205779732,1580973376268,2,1,0,0,5,1,608
, data=[97, 98, 99, 100, 101]}
 * 参数为false:不可以获取监听的节点,System.out.println(nodeCache.getCurrentData());为null
 */

nodeCache.start(true);
System.out.println(nodeCache.getCurrentData());
//添加监听对象
nodeCache.getListenable().addListener(new NodeCacheListener() {
    //如果节点数据有变化,会回调该方法
    public void nodeChanged() throws Exception {
        String data = new String(nodeCache.getCurrentData().getData());
        System.out.println("数据Watcher:路径=" + nodeCache.getCurrentData().getPath()
                + ":data=" + data);
    }
});
System.in.read();
  • 测试

修改节点的数据

1573266836331
1573266836331

控制台显示

1573266903227
1573266903227
2.3、 PathChildrenCache
  • 介绍

    PathChildrenCache是用来监听指定节点 的子节点变化情况

  • 增加监听

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181"10001000, retryPolicy);
client.start();
//监听指定节点的子节点变化情况包括͹新增子节点 子节点数据变更 和子节点删除
//true表示用于配置是否把节点内容缓存起来,如果配置为true,客户端在接收到节点列表变更的同时,也能够获取到节点的数据内容(即:event.getData().getData())ͺ如果为false 则无法取到数据内容(即:event.getData().getData())
PathChildrenCache childrenCache = new PathChildrenCache(client,"/hello",true);
/**
 * NORMAL:  普通启动方式, 在启动时缓存子节点数据
 * POST_INITIALIZED_EVENT:在启动时缓存子节点数据,提示初始化
 * BUILD_INITIAL_CACHE: 在启动时什么都不会输出
 *  在官方解释中说是因为这种模式会在start执行执行之前先执行rebuild的方法,而rebuild的方法不会发出任何事件通知。
 */

childrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
System.out.println(childrenCache.getCurrentData());
//添加监听
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
    @Override
    public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
        if(event.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED){
            System.out.println("子节点更新");
            System.out.println("节点:"+event.getData().getPath());
            System.out.println("数据" + new String(event.getData().getData()));
        }else if(event.getType() == PathChildrenCacheEvent.Type.INITIALIZED ){
            System.out.println("初始化操作");
        }else if(event.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED ){
            System.out.println("删除子节点");
            System.out.println("节点:"+event.getData().getPath());
            System.out.println("数据" + new String(event.getData().getData()));
        }else if(event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED ){
            System.out.println("添加子节点");
            System.out.println("节点:"+event.getData().getPath());
            System.out.println("数据" + new String(event.getData().getData()));
        }else if(event.getType() == PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED ){
            System.out.println("连接失效");
        }else if(event.getType() == PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED ){
            System.out.println("重新连接");
        }else if(event.getType() == PathChildrenCacheEvent.Type.CONNECTION_LOST ){
            System.out.println("连接失效后稍等一会儿执行");
        }
    }
});
System.in.read(); // 使线程阻塞
2.4、TreeCache
  • 介绍

    TreeCache有点像上面两种Cache的结合体,NodeCache能够监听自身节点的数据变化(或者是创建该节点),PathChildrenCache能够监听自身节点下的子节点的变化,而TreeCache既能够监听自身节点的变化、也能够监听子节点的变化。

  • 添加监听

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181"10001000, retryPolicy);
client.start();
TreeCache treeCache = new TreeCache(client,"/hello");
treeCache.start();
System.out.println(treeCache.getCurrentData("/hello"));
treeCache.getListenable().addListener(new TreeCacheListener() {
    @Override
    public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
            if(event.getType() == TreeCacheEvent.Type.NODE_ADDED){
                System.out.println(event.getData().getPath() + "节点添加");
            }else if (event.getType() == TreeCacheEvent.Type.NODE_REMOVED){
                System.out.println(event.getData().getPath() + "节点移除");
            }else if(event.getType() == TreeCacheEvent.Type.NODE_UPDATED){
                System.out.println(event.getData().getPath() + "节点修改");
            }else if(event.getType() == TreeCacheEvent.Type.INITIALIZED){
                System.out.println("初始化完成");
            }else if(event.getType() ==TreeCacheEvent.Type.CONNECTION_SUSPENDED){
                System.out.println("连接过时");
            }else if(event.getType() ==TreeCacheEvent.Type.CONNECTION_RECONNECTED){
                System.out.println("重新连接");
            }else if(event.getType() ==TreeCacheEvent.Type.CONNECTION_LOST){
                System.out.println("连接过时一段时间");
            }
    }
});
System.in.read();

【总结】

1: Zookeeper:分布小文件存储系统,树形层级数据结构,路径唯一

  • 动物管理员,管的是分布中的服务
  • 应用:注册中心(解决服务单点问题)、配置中心(修改配置麻烦问题,统一管理配置)、分布式锁(解决资源抢用)、分布式队列(解决惊群,资源公平使用)、负载均衡(相对均衡)

2: 安装:没有中文没有空格,jdk1.8(JAVA_HOME), 创建data,创建log 配置zoo.cfg dataPath, dataLogDir 2181

3: 节点类型:持久化(有序与无序 -s),临时(-e 临时, -s 有序与无序) crud

4: api: Curator 创建重试策略->客户端(url ip:port)->启动->使用client操作crud->关闭

5: Watch: 监听节点数据变化,一旦生变更(添加、修改、删除)时,服务端通知客户端(addListener)

NodeCache: 听监听单一节点变化

PathChildrenCache: 监听节点下的子节点

TreeCache: NodeCache+PathChildrenCache

3、zookeeper集群搭建

3.1、了解集群

3.1.1、集群介绍

​ Zookeeper 集群搭建指的是 ZooKeeper 分布式模式安装。 通常由 2n+1台 servers 组成 奇数???。 这是因为为了保证 Leader 选举(基于 Paxos 算法的实现) 能或得到多数的支持,所以 ZooKeeper 集群的数量一般为奇数。

3.1.2、集群模式

ZooKeeper集群搭建有两种方式:

  • 伪分布式集群搭建:

    所谓伪分布式集群搭建ZooKeeper集群其实就是在一台机器上启动多个ZooKeeper,在启动每个ZooKeeper时分别使用不同的配置文件zoo.cfg来启动,每个配置文件使用不同的配置参数(clientPort端口号、dataDir数据目录.

    在zoo.cfg中配置多个server.id,其中ip都是当前机器,而端口各不相同,启动时就是伪集群模式了。

    这种模式和单机模式产生的问题是一样的。也是用在测试环境中使用。

  • 完全分布式集群搭建:

    多台机器各自配置zoo.cfg文件,将各自互相加入服务器列表,每个节点占用一台机器

3.2、架构图

1573475887256Leader:Zookeeper(领导者):

集群工作的核心 事务(ZAB>50%)请求(写操作) 的唯一调度和处理者,保证集群事务(id: zxid 16进制自增)处理的顺序性; 集群内部各个服务器的调度者。 对于 create, setData, delete 等有写操作的请求,则需要统一转发给leader 处理, leader 需要决定编号、执行操作,这个过程称为一个事务。ZAB的事务2pc, 3pc(TCC) Follower(跟随者)

处理客户端非事务(读操作) 请求,转发事务(增删改)请求给 Leader;参与集群 Leader 选举投票 2n-1台可以做集群投票。

Observer:观察者角色

针对访问量比较大的 zookeeper 集群, 还可新增观察者角色。观察 Zookeeper 集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给 Leader服务器进行处理。 不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。

3.3、搭建过程

3.3.1、准备三台电脑(虚拟机)
  • 安装三个linux虚拟机

    1573473107153
    1573473107153
  • 配置虚拟网卡(三台电脑配置一个虚拟网卡)(虚拟网卡ip为:192.168.174.1)

    1573473145051
    1573473145051
  • 为每台虚拟机配置静态ip地址

1573473341207`

重启网络:service network restart

三台虚拟机分别是:192.168.174.128、192.168.174.129、192.168.174.130

关闭防火墙:

systemctl stop firewalld

service iptables stop

3.3.2、安装jdk(略)
  • 查看是否已经安装jdk

命令:rpm -qa|grep java

1573473661840
1573473661840
  • 卸载已安装的jdk

命令:rpm -e --nodeps

1573473710517
1573473710517
  • 上传jdk到linux

    1573473789230
    1573473789230
  • 解压jdk

命令: tar -vxf jdk-8u181-linux-i586.tar.gz

  • 配置环境变量

命令:cd / 退回根目录

命令:cd etc

命令:vim profile (编辑etc/profile文件,将如下配置粘贴到文件中)

#set java environment
JAVA_HOME=/usr/local/jdk1.8.0_181
CLASSPATH=.:$JAVA_HOME/lib.tools.jar
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME CLASSPATH PATH 
  • 重新加载profile文件,即激活profile文件

命令:source profile

3.3.3、上传zookeeper到虚拟机并解压
  • 上传到虚拟机
1573474157684
1573474157684
  • 解压

命令:tar -zxvf apache-zookeeper-3.5.5-bin.tar.gz /usr/local(解压)

命令:cd usr/local (进入local路径)

命令:mv apache-zookeeper-3.5.5-bin zookeeper (重命名文件夹)

3.3.4、配置zookeeper
  • 创建data目录

命令:cd zookeeper (进入zookeeper目录)

命令:mkdir data

  • 修改conf/zoo.cfg

命令:cd conf (进入conf目录)

命令:cp zoo_sample.cfg zoo.cfg(复制zoo_sample.cfg,文件名为zoo.cfg)

1573474979566
1573474979566

命令:vim zoo.cfg (修改zoo.cfg)

内容:

修改:

dataDir = /usr/local/zookeeper/data

添加:

server.1=192.168.174.128:2182:3182 server.2=192.168.174.129:2182:3182 server.3=192.168.174.130:2182:3182

1573475069756
1573475069756

参数详解(了解)

1)tickTime:通信心跳数,Zookeeper服务器心跳时间,单位毫秒
Zookeeper使用的基本时间,服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个tickTime时间就会发送一个心跳,时间单位为毫秒。
它用于心跳机制,并且设置最小的session超时时间为两倍心跳时间。(session的最小超时时间是2*tickTime)
2)initLimit:Leader and Follwer初始通信时限
集群中的follower跟随者服务器(F)与leader领导者服务器(L)之间初始连接时能容忍的最多心跳数(tickTime的数量),用它来限定集群中的Zookeeper服务器连接到Leader的时限。
投票选举新leader的初始化时间
Follower在启动过程中,会从Leader同步所有最新数据,然后确定自己能够对外服务的起始状态。
Leader允许F在initLimit时间内完成这个工作。
3)syncLimit:Leader and Follower同步通信时限
集群中Leader与Follower之间的最大响应时间单位,假如响应超过syncLimit * tickTime,
Leader认为Follwer死掉,从服务器列表中删除Follwer。
在运行过程中,Leader负责与ZK集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态。
如果L发出心跳包在syncLimit之后,还没有从F那收到响应,那么就认为这个F已经不在线了。
4)dataDir:数据文件目录+数据持久化路径
保存内存数据库快照信息的位置,如果没有其他说明,更新的事务日志也保存到数据库。
5)clientPort:客户端连接端口
监听客户端连接的端口
6)集群中服务的列表
server.1=192.168.174.128:2182:3182
server.2=192.168.174.129:2182:3182
server.3=192.168.174.130:2182:3182
server.* 后面的数据对应myid中的数字 服务标识(选举时可能要用到)
ip地址对应每台虚拟机的ip地址
2182:表示的是这个服务器与集群中的 Leader 服务器交换信息的端口
3182:表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口
  • 在data目录创建创建myid

命令:cd .. (退出conf目录)

命令: cd data (进入data目录)

命令:vim myid(修改myid文件,分别设置为1(128),2(129),3(130))

3.3.5、启动zookeeper

命令:cd .. (退出data目录)

命令:cd bin(进入bin路径)

命令: ./zkServer.sh start

1573475395307
1573475395307
3.3.6、查看zookeeper状态

命令:./zkServer.sh status

1573475640041
1573475640041
1573475588983
1573475588983
1573475570125
1573475570125

3.4、leader选举【面试时读】

在领导者选举的过程中,如果某台ZooKeeper获得了超过半数的选票,则此ZooKeeper就可以成为Leader了。

  • 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。

  • 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,

    每个Server发出一个投票由于是初始情况,1和2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时1的投票为(1, 0),2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。 接受来自各个服务器的投票集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器

    处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下

    · 优先检查ZXID。ZXID比较大的服务器优先作为Leader。

    · 如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器

    由于服务器2的编号大,更新自己的投票为(2, 0),然后重新投票,对于2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可,此时集群节点状态为LOOKING。

    统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息

  • 服务器3启动,进行统计后,判断是否已经有过半机器接受到相同的投票信息,对于1、2、3而言,已统计出集群中已经有3台机器接受了(3, 0)的投票信息,此时投票数正好大于半数,便认为已经选出了Leader,

    改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING

    所以服务器3成为领导者,服务器1,2成为从节点。

ZXID: 即zookeeper事务id号。ZooKeeper状态的每一次改变, 都对应着一个递增的Transaction id, 该id称为zxid

  1. 各自投自己一票
  2. 如果票数相同,则进入第二轮投票
  3. 改投zxid为大的。
  4. zxid相同,投myid为大的
  5. 统计得到的票数>50%就成为leader

3.5、测试集(练习)

第一步:3台集群,随便挑选一个启动,执行命令./zkCli.sh

001
001

第二步:添加节点数据

002
002

第三步:找另一台机器,测试查询,可以获取hello节点的数据

003
003

也可以使用代码测试

@Test
public void createNode() throws Exception {
   RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
   CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.174.128:2181"30003000, retryPolicy);
   client.start();
   client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/bbb/b1","haha".getBytes());
   client.close();
}

@Test
public void updateNode() throws Exception {
   //创建失败策略对象
   RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
   CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.174.129:2181",1000,1000,retryPolicy);
   client.start();
   //修改节点
   client.setData().forPath("/bbb/b1""fff".getBytes());
   client.close();
}

@Test
public void getData() throws Exception {
   RetryPolicy retryPolicy = new ExponentialBackoffRetry(10001);
   CuratorFramework client =
    CuratorFrameworkFactory.newClient("192.168.174.130:2181",1000,1000, retryPolicy);
   client.start();
   // 查询节点数据
   byte[] bytes = client.getData().forPath("/bbb/b1");
   System.out.println(new String(bytes));
   client.close();
}

@Test
public void deleteNode() throws Exception {
   RetryPolicy retryPolicy = new ExponentialBackoffRetry(10001);
   CuratorFramework client =
    CuratorFrameworkFactory.newClient("192.168.174.130:2181",1000,1000, retryPolicy);
   client.start();
   // 递归删除节点
   client.delete().deletingChildrenIfNeeded().forPath("/bbb");
   client.close();
}

测试使用任何一个IP都可以获取

测试如果有个机器宕机,(./zkServer.sh stop),会重新选取领导者。

【总结】

  1. 集群?多台相同功能服务器组成的集群

  2. 为什么?解决单点问题,高并发,高可用(故障转移)

  3. zookeeper有哪些角色?

    • leader 领导者,读写数据,同步数据到其它服务器(追随者,观察者)
    • follower追随者,读,写时转给leader,同时参与投票与选举
    • observer观察者。读,写时转给leader,不参与投票与选举
  4. 集群的搭建

    • 三台虚拟机,分别修改网络与ip地址 mac地址, systemctl restart network

    • 安装zookeeper

      • 上传安装包
      • 解压,重命名,移动到/usr/local
      • 创建data目录, data目录下创建myid内容为server.id
      • 到conf复制zoo.cfg 修改存储data目录,添加集群节点信息
    • 启动 ./zkServer.sh start

  5. 选举机制

    • 投自己一票
    • 事务id log文件里 最后一行zxid 16进制
    • 看myid
    • 谁大改投谁
    • 票数过半,票数/集群数量>50%成为leader
    • 避免选不了leader 集群数量为2n+1
    • 机器性能比较好的myid要调大一点

https://www.cnblogs.com/stateis0/category/1206895.html ZAB Zookeeper原子广播

2pc,3pc

分类:

后端

标签:

后端

作者介绍

a
ansolidll
V1