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依赖

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.6.0</version>
</dependency>
  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<String> 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<String> nodeList = zooKeeper.getChildren(PATH, null);
            // 创建临时列表存放服务器地址
            List<String> 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: 监听指定节点下的所有子节点的数据和子节点改变

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容