Zookeeper 分布式可靠协调系统

一、认识Zookeeper

官网地址:https://zookeeper.apache.org/
Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。

Zookeeper=文件系统+通信机制 (工作机制:观察者模式)
特点:
  1)Zookeeper:一个领导者(Leader),多个跟随者(Follower)组成的集群。
  2)集群中只要有半数以上节点存活,Zookeeper集群就能正常服务。
  3)全局数据一致:每个Server保存一份相同的数据副本,Client无论连接到哪个Server,数据都是一致的。
  4)更新请求顺序进行,来自同一个Client的更新请求按其发送顺序依次执行。
  5)数据更新原子性,一次数据更新要么成功,要么失败。
  6)实时性,在一定时间范围内,Client能读到最新数据。

应用场景:

  • 统一命名服务
  • 统一配置管理
  • 统一集群管理
  • 服务器节点动态上下线
  • 软负载均衡

选举机制:

  • 半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
  • Zookeeper虽然在配置文件中并没有指定Leader和Follower。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的。
(1)服务器1启动,发起一次选举。服务器1投自己一票,此时服务器1票数一票,不够半数以上(3票),选举无法完成,服务器1状态保持为LOOKING;

(2)服务器2启动,再发起一次选举。服务器1和2分别投自己一票并交换选票信息,此时服务器1发现服务器2的ID比自己目前投票推举的(服务器1)大,则更改选票为推举服务器2,此时服务器1票数0票,服务器2票数2票,没有半数以上结果,选举无法完成,服务器1,2状态保持LOOKING;

(3)服务器3启动,发起一次选举。此时服务器1和2都会更改选票为服务器3,此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票,此时服务器3的票数已经超过半数,服务器3当选Leader。服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;

(4)服务器4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息,交换选票信息结果:服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3,并更改状态为FOLLOWING;

(5)服务器5启动,于4的过程一样,更改选票信息为服务器3,并更改状态为FOLLOWING。

小结:投票选举的过程与mtime-znode最后修改时间和配置id有关,先会找最后修改的Server当Leader,若一样,则会找配置id大的。

节点类型:
在这里插入图片描述

监听器原理:
在这里插入图片描述

写数据流程:
在这里插入图片描述

二、本地模式安装(以windows系统为例,与linux安装相同)

1、确保已经安装JDK后,从官网下载安装包,解压到指定目录。
2、配置修改。

将conf路径下的zoo_sample.cfg修改为zoo.cfg

打开zoo.cfg文件,修改dataDir路径。(路径可以自己设置,方便查看,别忘记创建设置的文件夹)
dataDir=../data

3、操作Zookeeper

windows直接执行bin目录下的zkServer.cmd启动服务端,执行zkCli.cmd启动客户端

linux
  启动服务端:bin/zkServer.sh start
  查看启动状态:bin/zkServer.sh status
  启动客户端:bin/zkCli.sh

三、分布式安装(以Linux为例)

1、从官网下载压缩包,解压到指定目录下。

tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/module/

2、同步内容到其他服务器。

xsync zookeeper-3.4.10/

3、配置服务器编号。

/opt/module/zookeeper-3.4.10/目录下创建zkData:
mkdir -p zkData

在zkData目录下创建一个myid文件:
touch myid
vi myid
添加与server对应的编号:2

拷贝配置好的zookeeper到其他机器上(注意修改编号,确保唯一):
xsync myid

4、配置zoo.cfg文件

mv zoo_sample.cfg zoo.cfg
vi zoo.cfg
修改数据存储路径配置:
dataDir=/opt/module/zookeeper-3.4.10/zkData

增加如下配置:
#####################cluster############################
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888

server.(几号服务器)= (服务器ip地址):(服务器与集群中的Leader服务器交换信息的端口):(选举时服务器相互通信的端口)

四、shell命令操作

(1)显示所有操作命令。

help

(2)查看当前znode中所包含的内容。

ls /

(3)查看当前节点详细数据。

ls2 /

(4)创建普通节点。

create /ceshi "ceshi"

(5)获得节点的值。

get /ceshi

(6)创建短暂节点。

create -e /ceshi "ceshi"

(7)创建带序号的节点。(如果原来没有序号节点,序号从0开始依次递增。如果原节点下已有2个节点,则再排序时从2开始,以此类推)

create -s /ceshi "ceshi"

(8)修改节点数据值。

set /ceshi "ceshi"

(9)节点的值变化监听。

get /ceshi watch

(10)节点的子节点变化监听(路径变化)。

ls /ceshi watch

(11)删除节点。

delete /ceshi 

(12)递归删除节点。

rmr /ceshi 

(13)查看节点状态。

stat /ceshi 

五、代码实现

1、引入依赖

 <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.12</version>
    </dependency>
    
    <!--zookeeper依赖-->
    <dependency>
      <groupId>org.apache.zookeeper</groupId>
      <artifactId>zookeeper</artifactId>
      <version>3.7.0</version>
    </dependency>
  </dependencies>

2、添加日志配置文件
在项目resources目录下新建log4j.properties文件。

log4j.rootLogger=INFO,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.conversionPattern=%d %p [%c] - %m%n

3、编写ZookeeperTest测试类

package com.yzs;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.List;

public class ZookeeperTest {
    
    
    private String connectString = "127.0.0.1:2181";
    private int sessionTimeout = 2000;
    private ZooKeeper zooKeeper;

    //连接zookeeper服务端
    @Before
    public void connectZookeeper() throws IOException {
    
    
        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
    
    
            @Override
            public void process(WatchedEvent watchedEvent) {
    
    
                List<String> children = null;
                try {
    
    
                    children = zooKeeper.getChildren("/", true);
                    for (String child : children){
    
    
                        System.out.println(child);
                    }
                } catch (KeeperException e) {
    
    
                    e.printStackTrace();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        });
    }

    //创建子节点
    @Test
    public void createNode() throws KeeperException, InterruptedException {
    
    
        zooKeeper.create("/sanguo","sanguo".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
    }

    //获取子节点并监听数据变化
    @Test
    public void getChildrenAndWatch() throws KeeperException, InterruptedException {
    
    
        List<String> children = zooKeeper.getChildren("/", true);
        for (String child : children){
    
    
            System.out.println(child);
        }
        //延时阻塞
        Thread.sleep(Long.MAX_VALUE);
    }

    //判断是否存在节点
    @Test
    public void exist() throws KeeperException, InterruptedException {
    
    
        Stat stat = zooKeeper.exists("/sanguo", false);
        System.out.println(stat == null?"not exist":"exist");
    }

    //获取节点上的数据
    @Test
    public void getNode() throws KeeperException, InterruptedException {
    
    
        byte[] data = zooKeeper.getData("/sanguo", false, null);
        System.out.println(new String(data));
    }
}

猜你喜欢

转载自blog.csdn.net/fish332/article/details/118156605
今日推荐