ZooKeeper安装和使用

安装

  1. 下载地址: https://zookeeper.apache.org/releases.html
    本文使用3.6.0版本, 在linux系统中操作
  2. 解压到/usr/local目录中 tar -zxvf apache-zookeeper-3.6.0-bin.tar -C /usr/local
  3. 修改ZooKeeper目录名称方便管理 mv apache-zookeeper-3.6.0-bin apache-zookeeper-01
    zookeeper目录.png

单机配置

  1. 启动ZooKeeper服务需要有配置文件, 在conf目录下新建zoo.cfg文件, 添加如下配置
# 心跳时间间隔(ms)
tickTime=2000
# 集群启动的时间限制, 10个心跳间隔时间, 10 * 2000ms
initLimit=10
# 数据同步的时间限制, 5个心跳间隔时间, 5*2000ms
syncLimit=5
# 数据存放目录
dataDir=/usr/local/apache-zookeeper-01/data
# 客户端连接端口
clientPort=2181
# 集群中的服务列表
# server后面的1 代表服务的id, 必须和myid文件(在data目录)中的内容一致, 数值在1-154之间
# 服务IP地址, 最好是内网IP, 内网传输速度快. 
# 2601 是几个服务的数据同步端口
# 3601 是几个服务的选举端口, leader服务挂掉后剩余服务通过该端口通信选举新的leader
server.1=192.168.88.88:2601:3601
  1. 在ZooKeeper根目录下创建数据存放目录data,
    cd /usr/local/apache-zookeeper-01 && mkdir data

  2. 在data目录下创建myid文件,并写入该服务的id为1, 必须和步骤1中集群服务列表的id一致
    cd data && echo 1 > myid

  3. 进入bin目录使用zkServer.sh可执行文件启动ZooKeeper.
    ./zkServer.sh start
    该命令会使用默认配置文件zoo.cfg, 如果要指定配置文件启动 需要加上 --config
    ./zkServer.sh status 查看当前状态
    ./zkServer.sh stop 关闭ZooKeeper服务
    ./zkCli.sh -server 192.168.88.88:2181 连接ZooKeeper服务, 不写-server默认连接本机2181端口

    zookeeper启动成功.png

集群配置

前面已经成功启动了一个ZooKeeper服务, 下面再添加2个ZooKeeper节点做一个集群

  1. 拷贝2份apache-zookeeper-01�目录分别为apache-zookeeper-02和apache-zookeeper-03
    cp -R apache-zookeeper-01 apache-zookeeper-02
    cp -R apache-zookeeper-01 apache-zookeeper-03

  2. 修改配置文件zoo.cfg
    加入新添加的ZooKeeper服务的IP地址, 3个ZooKeeper都要添加
    apache-zookeeper-02: server.2=192.168.88.88:2602:3602
    apache-zookeeper-03: server.3=192.168.88.88:2603:3603
    修改数据存放目录的路径
    apache-zookeeper-02: dataDir=/usr/local/apache-zookeeper-02/data
    apache-zookeeper-03: dataDir=/usr/local/apache-zookeeper-03/data
    由于我这是用同一台电脑操作, IP地址一样,所以需要改一下客户端连接端口
    apache-zookeeper-02: clientPort=2182
    apache-zookeeper-03: clientPort=2183
    由于我这是用同一台电脑操作, 所以IP地址一样

  3. 修改myid文件中的服务id

  4. 启动这3个ZooKeeper服务. 如果之前已经启动了apache-zookeeper-01, 由于修改了配置文件, 需要关闭重新启动.启动完成后通过./zkServer.sh status看到Mode是一个leader两个follower, 到此集群就已经完成


    zookeeper集群.png

项目中使用ZooKeeper

项目集成ZooKeeper常用的有JDK自带zkclient框架和Apache开源的Curator框架, 这里使用zkclient.

  1. 首先在ZooKeeper中创建一个节点/server, 步骤2中注册服务器信息就在这个节点下面添加子节点
    连接ZooKeeper服务 ./zkCli.sh -server 192.168.88.88:2181
    创建server节点 create /server

  2. 添加zookeeper依赖


    org.apache.zookeeper
    zookeeper
    3.6.0

  1. 配置ZooKeeper添加到spring容器中, 并向ZooKeeper注册服务器信息
import org.apache.zookeeper.*;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;

import java.io.IOException;

@SpringBootConfiguration
public class ZookeeperConfig {
    // zookeeper服务地址
    private static final String ZK_SERVER_ADDR = "192.168.88.88:2181, 192.168.88.88:2182, 192.168.88.88:2183";
    // 会话超时时间
    private static final int SESSION_TIMEOUT = 30000;
    // 存储服务地址节点的路径
    private static final String PATH = "/server";
    // 注册服务器信息的节点名称
    private static final String SUB_PATH = "/testServer";
    // 服务器信息
    private static final String HOST  = "192.168.88.88:8888";

    private ZooKeeper zooKeeper;

    @Bean
    public ZooKeeper zooKeeper() throws IOException {
        zooKeeper = new ZooKeeper(ZK_SERVER_ADDR, SESSION_TIMEOUT, new Watcher() {
            // 监听连接事件
            @Override
            public void process(WatchedEvent event) {
                if(event.getState() == Event.KeeperState.SyncConnected) {
                    System.out.println("zookeeper连接成功");

                    // 注册服务器信息
                    try {
                        // 创建临时节点存储服务器信息
                        zooKeeper.create(PATH + SUB_PATH, HOST.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        return  zooKeeper;
    }
}

项目启动完成后/server节点下面就会多一个子节点


zookeeper注册服务器信息.png
  1. 客户端启动后从ZooKeeper获取注册的服务器信息存储到本地, 给/server节点添加永久监听事件, 如果/server节点的子节点数量有变化会自动获取最新数据
import org.apache.zookeeper.*;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;

import java.util.ArrayList;
import java.util.List;

@SpringBootConfiguration
public class ZKClientConfig {
    // zookeeper服务地址
    private static final String ZK_SERVER_ADDR = "192.168.88.88:2181, 192.168.88.88:2182, 192.168.88.88:2183";
    // 会话超时时间
    private static final int SESSION_TIMEOUT = 30000;
    // 存储服务地址节点的路径
    private static final String PATH = "/server";

    private ZooKeeper zooKeeper;

    private static List serverList;

    @Bean
    public ZooKeeper zooKeeper() throws Exception {
        zooKeeper = new ZooKeeper(ZK_SERVER_ADDR, SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getState() == Event.KeeperState.SyncConnected) {
                    System.out.println("ZooKeeper连接成功");
                    // 获取服务器地址数据
                    getServerList();
                    // 监听/server下的子节点变化
                    try {
                        zooKeeper.addWatch(PATH, new Watcher() {
                            @Override
                            public void process(WatchedEvent event) {
                                // 获取最新的服务器地址数据
                                getServerList();
                            }
                        }, AddWatchMode.PERSISTENT);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        return zooKeeper;
    }

    private void getServerList() {
        try {
            // 获取/server下面的所有子节点
            List nodeList = zooKeeper.getChildren(PATH, null);
            // 创建临时列表存放服务器地址
            List tempList = new ArrayList<>();
            // 取出/server所有子节点的数据
            for (String node : nodeList) {
                byte[] addr = zooKeeper.getData(PATH + "/" + node, null, null);
                tempList.add(new String(addr));
            }
            serverList = tempList;

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ZooKeeper分布式锁

ZooKeeper可以通过创建顺序临时节点来实现分布式锁, 思路如下图


zookeeper分布式锁.png

特性

  1. 一个leader, 多个follower

  2. 数据全局一致
    在任何节点读取到的数据都是一样的, 如果从follower节点读数据, 而该数据还没有从leader同步过来, 那么读数据的请求就会暂时阻塞,等待数据同步过来后再读取.

  3. 分布式读写
    follower读数据, leader写数据

  4. 更新请求顺序执行
    更新数据的请求按顺序执行

  5. 数据更新具有原子性
    leader接收到更新数据的请求,会以广播的形式通知所有的follower, 当接收到超过一半follower的确认信息后, 再次发送广播通知follower提交数据, 不足一半确认则更新失败

  6. 实时性
    数据同步时间是毫秒级别. 官方建议写数据每次最好不超过1M. 写的数据过大会影响同步时间

数据结构

zookeeper是以树状结构存储数据, 每个节点都可以存储一条数据. 常用的ZooKeeper节点有4种类型
PERSISTENT 持久化节点
PERSISTENT_SEQUENTIAL 持久化顺序节点
EPHEMERAL 临时节点
EPHEMERAL_SEQUENTIAL 临时顺序节点
创建的临时节点与当前会话绑定, 会话断开就会删除, 并且不允许有字节点

常用命令

  1. create [-s] [-e] [-c] [-t ttl] path [data] [acl]
    -s 创建顺序节点
    -e 创建临时节点
    -c 创建容器节点. 容器节点在指定时间内(默认1分钟)没有子节点就会自动删除
    -t 指定生存时间

设置生存时间-t 默认禁用, 需要在/bin/zkServer.sh中配置-Dzookeeper.extendedTypesEnabled=true -Dzookeeper.emulate353TTLNodes=true 开启

zookeeper开启ttl功能.png

  1. ls [-s] [-w] [-R] path
    -s 显示节点状态信息
    -w 监听子节点改变, 只监听一次.通过printwatches on|off 开启/关闭事件监听
    -R 递归查看子节点

  2. set [-s] [-v version] path data
    -s 显示节点状态信息
    -v 更新节点的版本号. 如果版本号不一致, 就不做处理

4.addWatch [-m mode] path# optional mode is one of [PERSISTENT, PERSISTENT_RECURSIVE] - default is PERSISTENT_RECURSIVE
-m 监听的模式.
PERSISTENT: 只监听当前节点的数据和子节点改变,
PERSISTENT_RECURSIVE: 监听指定节点下的所有子节点的数据和子节点改变

你可能感兴趣的:(ZooKeeper安装和使用)