zookeeper学习(占坑)

本文介绍了Zookeeper的学习背景,分布式系统的基本概念,包括分布性、对等性、并发性和故障发生的可能性。接着,文章深入探讨了Zookeeper在分布式架构中的作用,以及其作为分布式协调服务的特性。详细讲解了Zookeeper的基础知识,如部署、数据模型、节点类型、会话和ACL保障。最后,提到了Zookeeper的命令行操作和Java客户端的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为什么要学习zk?

分布式,dubbo、kafka

分布式系统是什么?

不同硬件、软件、网络、计算机,仅仅通过消息进行协调和通讯

分布性

一个项目拆分成多个服务

对等性

服务对等、数据对等

并发性

多线程、多JVM

缺乏全局时钟

每个节点都有自己的时间系统

故障随时发生

节点发生故障可能性大

分布式架构

应用层、服务层、基础设施、数据库层

zookeeper的作用,类比为交警,每个节点是路上的车,为了交通的可用性,zk需要知道每个节点的健康状态(如公交车出故障了,需要及时排出新的公交车(服务注册与发现),在很窄的路只允许单独一个方向的车通过(分布式锁)

分布式系统的方法论

问题

通讯异常:网络异常

网络分区:脑裂,比如一个交警接管一篇区域的交通,如果突然出现停电或者其他因素,导致某些道路接收不到交警的指令,需要一个临时工,片警来指挥交通。同一个区域产生两个互相冲突的负责人就会出现脑裂的现象。

三态:成功、失败、超时态

节点故障:集群节点的宕机

CAP方法论

一致性:Consistency,分布式环境中,节点之前的数据保持一致性,即一个数据改变,所有节点都能马上读取最新的值

可用性:Available,服务一直处于可用状态,在有限时间内,返回结果。有限时间:加缓存,负载;返回结果:出现宕机,马上有副节点顶替。

分区容错性:Partition Fault Tolerance,分布式系统遇到任何网络分区故障时,仍然需要保持一致性和可用性,不能出现脑裂的现象。

CAP理论不能同时满足,一般只能满足其中的两项。

AC:满足可用性和一致性,很简单,就把数据和服务放在一个节点上。

PC:满足分区容错性和一致性,当节点故障或网络故障时,受影响的服务需要等待一段时间,在等待的时间内,服务是不可用的。

AP:满足可用性和分区容错性,不能保证数据实时一致性,但能保证最终一致性。

很显然,我们一般不会抛弃P,所以只能在A和C之间寻找平衡

BASE玄学

基本可用:basically avaliable,当系统出现一些故障,允许损失部分可用性,保证系统基本可用,比如双十一淘宝页面卡顿。

软状态:soft state,允许不同节点之间的数据副本之间同步存在延时。12306买火车票,请求会进入排队队列。

最终一致性:eventually consistent 所有数据在一段时间的数据同步后,达到最终一致性。如金融产品中的余额短时间不一致。

Zookeeper前述

分布式协调服务coordination service

特性

简单数据结构:共享树形结构,类似文件系统

集群:避免单点故障,超过半数工作正常即可

顺序访问:每一个读请求,zk会分配一个全局唯一的递增编号

高性能:基于内存,使用读场景为主的业务场景

用途

数据发布订阅

负载均衡

命名服务

master选举

集群管理

分布式队列

分布式锁

需要学什么

应用场景

paxos和zk协议

选举算法

zk节点类型

zk的watch是永久的吗

zk的部署方式,集群的角色有哪些,需要多少节点

如果集群只有三态,挂掉其中一台能否正常工作?

集群支持动态加机器?

Zk基础

部署

win和linux的安装差不多,这里主要讲linux,创建data和log目录,把conf目录下的zoo_sample.cfg复制在当前目录,改名zoo.cfg。zk有三种安装模式:单机、集群和集群伪分布式。

  • 单机

  • 集群安装

install zookeeper cluster

修改zoo.cfg

dataDir=/develop/..../data
server.0=192.....134:2888:3888
server.1=192.....135:2888:3888
server.2=192.....136:2888:3888

创建服务器标识

cd /develop/..../data
echo 0 > myid

复制zookeeper文件夹到其他服务器节点

# 复制
scp -r /soft root@zk2:/

修改myid为1、2

启动zk

./zkServer.sh start
./zkServer.sh status
目录结构

bin 脚本:zkCli.sh、zkServer.sh

conf 配置文件:zoo.cfg,端口默认2181

contrib zk附加功能

dist-maven maven仓库文件

docs zk文档

lib 依赖的第三方库

recipes 经典场景代码

src zk源码

zk特性

会话

会话状态:connecting、connected、reconnecting、reconnected、close

创建zk对象后,connecting,然后尝试连接,连接成功后,connected,如果出现网络故障等原因而断开,就reconnecting,重新尝试连接,连接成功,reconnected,会话关闭,close

数据模型

数据节点,znode,每个znode必须有值,同时可以有子节点,这点和传统的文件夹结构不一样

节点类型

持久节点

临时节点

持久顺序节点

临时顺序节点

节点的状态属性

czxid:节点被创建的zxid值

mzxid:节点被修改的zxid值

pzxied:子节点最后一次被修改时的事务ID

ctime:节点被创建的时间

mtime:节点最后一次被修改的时间

version:节点被修改的版本号

cversion:节点所拥有的子节点被修改的版本号

aversion:节点的acl被修改的版本号

emphemeralOwner:如果该节点是临时节点,那么它的值为这个节点拥有者的会话ID;or,它的值为0

dataLength:节点数据域的长度

numChildren:节点拥有的子节点的个数

ACL保障数据安全

命令行

服务端常用命令

启动 bin/zkServer.sh start

查看状态 bin/zkServer.sh status

停止 bin/zkServer.sh stop

重启 bin/zkServer.sh restart

客户端常用命令

客户端连接服务 zkCli.sh -server 127.0.0.1:2181

# 显示zk的/目录下的内容 
ls /
# 显示zk的/目录下的内容,并显示更新次数等数据
ls2 /
# 创建一个节点,并初始化,-e -s
create /zk "test"
# 获取节点的内容
get /zk
# 修改节点的内容
set /zk "zk"
# 删除节点
delete /zk
# 递归删除,同时会删除子节点
rmr /zk
# 退出客户端
quit
ACL常用命令

acl用来控制权限,分为三个维度:scheme、id、permission,通常用scheme:id:permission表示权限

scheme:授权策略,可选world/auth/digest/ip

id:表示用户,当为world时,anyone;当为auth时,username/password,当为digest时,username/base64(sha1(password));当为ip时,为ip,如192.168.1.1/16,表示匹配前16个bit的ip段

其中auth需要使用该命令添加用户

addauth digest user:pwd

permission:表示权限,crwda

create 创建 delete 删除 read读取 write修改 admin设置子节点权限的权限

# world
# 获取权限
create /cbl xxx
getAcl /cbl
# 设置权限
setAcl /cbl world:anyone:crwa
getAcl /cbl
# 创建子节点并删除,由于没有d权限,所有无法删除
create /cbl/cbl xxx
delete /cbl/cbl

## auth
# 添加授权信息
add auth digest cbl:root
# 设置权限
setAcl /cbl auth:cbl:root:crwa
# 退出客户端,没有权限无法访问
ls /cbl

## digest
setAcl /cbl digest:cbl:sdfsdfsdfdsfdfs:crwa
# 加密算法
java -Djava.ext.dirs=/soft/zookeeper-3.4.12/lib -cp /soft/zookeeper-3.4.12/zookeeper-3.4.12.jar org.apache.zookeeper.server.auth.DigestAuthenticationProvider cbl:root

如果没有权限删除节点,可以启用super权限

// 获取密码
DigestAuthenticationProvider.generateDigest("super:admin")

在zkServer启动脚本添加

-Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBvst5y6rkB6 HQs=

启用客户端用管理员登录

addauth digest super:admin
常用命令
# 首先安装nc
yum install nc
# 查看那个节点被作为follower或者leader
echo stat|nc 127.0.0.1 2181
# 测试是否启动了该server,若imok表示已经启动
echo ruok|nc 127.0.0.1 2181
# 列出未经处理的会话和临时节点
echo dump|nc 127.0.0.1 2181
# 关掉server
echo kill|nc 127.0.0.1 2181
# 输出相关服务配置的详细信息
echo conf|nc 127.0.0.1 2181
# 列出所有连接到服务器的客户端的完全的连接/会话的详细信息
echo cons|nc 127.0.0.1 2181
# 输出关于服务环境的详细信息(区别于conf命令)
echo envi|nc 127.0.0.1 2181
# 列出未经处理的请求
echo reqs|nc 127.0.0.1 2181
# 列出服务器watch的详细信息
echo wchs|nc 127.0.0.1 2181
# 通过session列出服务器watch的详细信息,它的输出是一个与watch相关的会话的列表
echo wchc|nc 127.0.0.1 2181
# 通过路径列出服务器watch的详细信息。它输出一个与session相关的路径
echo wchp|nc 127.0.0.1 2181
zk日志可视化

java -cp /soft/zookeeper-3.4.12/zookeeper-3.4.12.jar:/soft/zookeeper-3.4.12/lib/slf4j-api-1.7.25.jar

org.apache.zookeeper.server.LogFormatter log.1

java -cp /soft/zookeeper-3.4.12/zookeeper-3.4.12.jar:/soft/zookeeper-3.4.12/lib/slf4j-api-1.7.25.jar

org.apache.zookeeper.server.SnapshotFormatter log.1

java客户端

pom

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.12</version>
    </dependency>
    <dependency>
        <groupId>com.101tec</groupId>
        <artifactId>zkclient</artifactId>
        <version>0.10</version>
    </dependency>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>4.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-recipes</artifactId>
        <version>4.0.0</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>
创建会话
public class TestCreateSession {
    private static final String SERVER = "192.168.3.50:2181";
    private final int SESSION_TIMEOUT = 30000;

    /**
     * 获取session的方式,可能在zk还没有获取连接的时候就已经对zk进行访问了
     * @throws Exception
     */
    @Test
    public void testSession1() throws Exception {
        ZooKeeper zk = new ZooKeeper(SERVER, SESSION_TIMEOUT, null);
        System.out.println(zk);
        System.out.println(zk.getState()); // CONNECTING
    }

    // 发令枪
    private CountDownLatch countDownLatch = new CountDownLatch(1);

    /**
     * 对获得Session的方式进行优化,在zk初始化完成之前先等待,等待完成后再进行后续的操作
     * @throws Exception
     */
    @Test
    public void testSession2() throws Exception {
        ZooKeeper zk = new ZooKeeper(SERVER, SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                    // 如果监听到连接状态,计数器减一
                    countDownLatch.countDown();
                    System.out.println("已经获得了连接");
                }
            }
        });

        //等待
        countDownLatch.await();
        System.out.println(zk.getState()); //connected
    }
}
客户端基本操作
public class TestJavaApi implements Watcher {

    private static final int SESSION_TIMEOUT = 10000;
    private static final String CONNECTION_STRING = "192.168.3.50:2181";
    private static final String ZK_PATH = "/leader";
    private ZooKeeper zk = null;

    private CountDownLatch connectedSemaphore = new CountDownLatch(1);

    /**
     * 创建zk连接
     * @param connectionString zk服务器地址列表
     * @param sessionTimeout 超时时间
     */
    public void createConnection(String connectionString, int sessionTimeout) {
        this.releaseConnection();
        try {
            zk = new ZooKeeper(connectionString, sessionTimeout, this);
            connectedSemaphore.await();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭zk连接
     */
    public void releaseConnection() {
        if (this.zk != null) {
            try {
                this.zk.close();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建节点
     *
     * @param path 节点path
     * @param data 初始数据内容
     * @return
     */
    public boolean createPath(String path, String data) {
        try {
            System.out.println("节点创建成功,path:" + this.zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL) + ", content:" +data);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 读取指定节点数据内容
     * @param path 节点path
     * @return
     */
    public String readData(String path) {
        System.out.println("读取数据成功,path:" + path);
        try {
            return new String(this.zk.getData(path, false, null));
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return "";
    }

    /**
     * 更新节点数据内容
     *
     * @param path 节点path
     * @param data 数据内容
     * @return
     */
    public boolean writeData(String path, String data) {
        try {
            System.out.println("更新数据成功,path:" + path + ",stat:" + this.zk.setData(path, data.getBytes(), -1));
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 删除指定节点
     * @param path 节点路径
     */
    public void deleteNode(String path) {
        try {
            this.zk.delete(path, -1);
            System.out.println("删除成功,path:" + path);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void process(WatchedEvent watchedEvent) {
        System.out.println("收到事件通知:" + watchedEvent.getState());
        if (Event.KeeperState.SyncConnected == watchedEvent.getState()) {
            connectedSemaphore.countDown();
        }
    }

    public static void main(String[] args) {
        TestJavaApi example = new TestJavaApi();
        example.createConnection(CONNECTION_STRING, SESSION_TIMEOUT);
        if (example.createPath(ZK_PATH, "我是节点内容")) {
            System.out.println("数据内容:" + example.readData(ZK_PATH));
            example.writeData(ZK_PATH, "更新后的数据");
            System.out.println("数据内容:" + example.readData(ZK_PATH));
            example.deleteNode(ZK_PATH);
            System.out.println("删除后的数据:" + example.readData(ZK_PATH));
        }
        example.releaseConnection();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值