Zookeeper之基础知识

Zookeeper

基本操作:

create /xxx abc: 创建持久节点,值为abc
create -e /xxx: 创建临时节点
create -s /xxx: 创建持久序号节点
create -s -e /xxx: 创建临时序号节点
create -c /xxx: 创建容器节点,当容器中没有任何子节点,该容器会被定期删除(60s)

ls / : 获得所有结点
ls /xxx: 获得xxx的子节点
ls -R /xxx : 递归查询,获得xxx所有的子孙节点

get /xxx: 获得xxx的数据
get -s /xxx: 获得xxx的详细数据
//删除
delete /xxx: 删除xxx
//乐观锁删除
delete -v [version] /xxx : 删除xxx

zk的数据持久化

zk的数据使运行在内存中,zk提供了两种持久化机制:

- 事务日志

​ zk把执行的命令以日志形式保存在dataLogDir指定的路径中的文件中

**- 数据快照 **

​ zk会在一定的时间间隔内做一次内存数据的快照,把该时刻的内存数据保存在快照文件中

zk在恢复数据时,先恢复快照文件中的数据到内存中,再用日志文件中的数据做增量恢复

权限设置:

注册当前会话的账号和密码:

addauth digest xiaowang:123456

创建结点并设置权限:

create /test-node abc auth:xiaowang:123456:cdwra

在另一个会话中,必须先使用账号密码,才能拥有操作该节点的权限

Curator客户端的使用

Curator是一套zookeeper客户端框架,封装了大部分zookeeper的功能,比如leader选举、分布式锁…

1、maven引入依赖

<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.12.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>2.12.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions> //若不冲突可不加
    <version>3.7.0</version>
</dependency>

配置文件

curator.retryCount=5
curator.elapsedTimeMs=5000 
curator.connectString=192.168.132.129:2181
curator.sessionTimeoutMs=60000
curator.connectionTimeoutMs=5000

配置类:

@Data
@Component
@ConfigurationProperties(prefix = "curator")
public class WrapperZK {
    private int retryCount;
    private int elapsedTimeMs;
    private String connectString;
    private int sessionTimeoutMs;
    private int connectionTimeoutMs;
}
@Configuration
public class CuratorConfig {
    @Autowired
    WrapperZK wrapperZK;
    @Bean(initMethod = "start")
    public CuratorFramework curatorFramework(){
        return CuratorFrameworkFactory.newClient(
                wrapperZK.getConnectString(),
                wrapperZK.getSessionTimeoutMs(),
                wrapperZK.getConnectionTimeoutMs(),
                new RetryNTimes(wrapperZK.getRetryCount(),wrapperZK.getElapsedTimeMs())
        );
    }

}

test类

@Slf4j
@SpringBootTest
public class test {
    @Autowired
    CuratorFramework curatorFramework;
    @Test
    void createNode() throws  Exception{
        //添加持久结点
        String path1 = curatorFramework.create().forPath("/curator-node","abc".getBytes());
        //添加临时序号节点
//        String path2 = curatorFramework.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/curator-node","abc".getBytes());
        System.out.println(String.format("curator create node :%s successfully,",path1));
        System.in.read();
    }
    @Test
    void testGetData() throws Exception {
        byte[] bytes = curatorFramework.getData().forPath("/curator-node");
        System.out.println(new String(bytes));
    }
    @Test
    void testSetData() throws Exception {
        curatorFramework.setData().forPath("/curator-node","changed".getBytes());
        byte[] bytes = curatorFramework.getData().forPath("/curator-node");
        System.out.println(new String(bytes));
    }
    @Test
    void testCreatWithParent() throws Exception{
        String pathWithParent = "/node-parent/sub-node-1";
        String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent);
        System.out.println(String.format("curator create node :%s successfully,",path));
    }
    @Test
    void testDelete() throws Exception{
        String pathWithParent = "/node-parent"; 
 									curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(pathWithParent);
    }
}

zk实现分布锁

1.zk中锁的种类:

​ 读锁:大家都可以读。要想上读锁的前提:之前的锁没有写锁。

​ 写锁: 只有得到写锁才能写。要想上写锁的前提:之前没有任何锁

2.zk如何上读锁

  • 创建一个临时序号节点,节点的数据是read,表示是读锁

  • 获取当前zk中序号比自己小的所有节点

  • 判断最小节点是否是读锁:

    1. 如果不是读锁的话,则上锁失败,为最小结点设置监听。阻塞等待,zk的watch机制会当最小节点发生变化时通知当前节点,于是再执行第二步的流程

    2. 如果是读锁,则上锁成功

3.zk如何上写锁

  • 创建一个临时序号节点,节点的数据是write,表示是写锁
  • 获取当前zk中的所有节点
  • 判断自己是否是最小的节点:
    1. 如果是,则上写锁成功
    2. 如果不是,说明前面还有锁,则上锁失败,监听最小的节点,如果最小节点有变化,则返回第二步

4.羊群效应

如果用上述的上锁方式,只要有节点发生变化,就会触发其他节点的监听事件,这样对zk的压力非常大,–羊群效应。

可以调整为链式监听,解决这个问题。

zk的watch机制:

把watch理解为注册在特定Znode上的触发器。当这个Znode发生改变,也就是调用了create,delete,setData方法的时候,将会触发Znode上注册的对应事件,请求watch的客户端会接收到异步通知。

具体交互过程如下:

​ **get -w /xxx **

在这里插入图片描述
在这里插入图片描述

2.zkCli客户端使用watch

create /test xxx
get -w /test 一次性监听节点
ls -w /test	监听目录,创建和删除子节点会收到通知。子节点中新增节点不会收到通知
ls -R -w /test 对于子节点中子节点的变化,但内容的变化不会收到通知

3.Curator使用watch

@Test
    public void addNodeListener() throws Exception {
        NodeCache nodeCache = new NodeCache(curatorFramework, "/curator-node");
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                log.info("{} path nodeChanged:","/curator-node");
                printNodeData();
            }
        });
        nodeCache.start();
        System.in.read();
    }
    public void printNodeData() throws Exception {
        byte[] bytes = curatorFramework.getData().forPath("/curator-node");
        log.info("data:{}",new String(bytes));
    }

5.Curator实现读写锁

1)获取读锁
	@Test
    public void testGetReadLock() throws Exception {
        //读写锁
        InterProcessReadWriteLock interProcessReadWriteLock = new InterProcessReadWriteLock(curatorFramework,"/lock1");
        //获取读锁对象
        InterProcessLock interProcessLock = interProcessReadWriteLock.readLock();
        System.out.println("等待获取读锁对象!");
        //获取锁
        interProcessLock.acquire();
        for (int i = 0; i < 100; i++) {
            Thread.sleep(3000);
            System.out.println(i);
        }
        //释放锁
        interProcessLock.release();
        System.out.println("等待释放锁");
    }
2)获取写锁
    @Test
    public void testGetWriteLock() throws Exception {
        //读写锁
        InterProcessReadWriteLock interProcessReadWriteLock = new InterProcessReadWriteLock(curatorFramework,"/lock2");
        //获取读锁对象
        InterProcessLock interProcessLock = interProcessReadWriteLock.writeLock();
        System.out.println("等待获取写锁对象!");
        //获取锁
        interProcessLock.acquire();
        for (int i = 0; i < 100; i++) {
            Thread.sleep(3000);
            System.out.println(i);
        }
        //释放锁
        interProcessLock.release();
        System.out.println("等待释放锁");
    }

zk集群

1.zk集群角色:

  • leader:处理集群的所有事务请求,集群只有一个leader
  • follower:只能处理读请求,参与leader选举
  • observer:只能处理读请求,提升集群读的性能,但不能参与leader选举

2.集群搭建

1)创建4个节点的myid,并设值
在这里插入图片描述

2)在conf文件中创建4个zoo.cfg

其中zoo1.cfg配置如下:

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/opt/zookeeper/zkdata/zk1
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=192.168.132.129:2001:3001
server.2=192.168.132.129:2002:3002
server.3=192.168.132.129:2003:3003
server.4=192.168.132.129:2004:3004:observer

3)启动服务器

[root@192 bin]# ./zkServer.sh start ../conf/zoo1.cfg
[root@192 bin]# ./zkServer.sh start ../conf/zoo2.cfg
[root@192 bin]# ./zkServer.sh start ../conf/zoo3.cfg
[root@192 bin]# ./zkServer.sh start ../conf/zoo4.cfg

4)查看集群状态

[root@192 bin]# ./zkServer.sh status ../conf/zoo1.cfg
ZooKeeper JMX enabled by default
Using config: ../conf/zoo1.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
[root@192 bin]# ./zkServer.sh status ../conf/zoo2.cfg
ZooKeeper JMX enabled by default
Using config: ../conf/zoo2.cfg
Client port found: 2182. Client address: localhost.
Mode: leader
[root@192 bin]# ./zkServer.sh status ../conf/zoo3.cfg
ZooKeeper JMX enabled by default
Using config: ../conf/zoo3.cfg
Client port found: 2183. Client address: localhost.
Mode: follower
[root@192 bin]# ./zkServer.sh status ../conf/zoo4.cfg
ZooKeeper JMX enabled by default
Using config: ../conf/zoo4.cfg
Client port found: 2184. Client address: localhost.
Mode: observer

可见zk2为leader,zk4为observer,其他为follower

5)连接Zookeeper集群

./bin/zkCli.sh -server 192.168.132.129:2181,192.168.132.129:2182,192.168.132.129:2183

3.ZAB协议

1)什么是ZAB协议?

​ zk作为非常重要的分布式协调组件,需要进行集群部署,集群中会以一主多从的形式进行部署。zk为了保证数据的一致性,使用了ZAB协议,这个协议解决了zk的崩溃恢复和主从数据同步的问题。

2)ZAB协议定义的四种节点状态

  • Looking:选举状态
  • Following:Follower节点所处状态
  • Leading:Leader节点所处状态
  • Observing:观察者节点所处状态

3)集群上线时的Leader选举过程:
在这里插入图片描述

4)崩溃恢复时的Leader选举过程:

leader建立完后,leader周期性不断向follower发送心跳(ping命令,没有内容的socket)。当leader崩溃后,follower发现socket通道已关闭,于是follower开始进入到looking状态,重新回到上一节中的leader选举状态,此时集群不能对外提供服务。

5)主从服务器之间的数据同步:
在这里插入图片描述

6)NIO和BIO的应用

NIO:

​ 用于被客户端连接的2181端口,使用的是NIO模式与客户端建立连接

​ 客户端开启Watch时,也使用NIO,等待Zookeeper服务器的回调

BIO:

​ 集群在选举时,多个节点之间的投票通信端口,使用BIO进行通信

CAP理论

1.CAP定理:

一个分布式系统最多只能同时满足一致性,可用性和分区容错性这三项中的两项。

  • 一致性

    更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致。

  • 可用性

    服务一直可用,而且是正常响应时间。

  • 分区容错性(必须)

    分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性或可用性的服务。——避免单点故障,就要进行冗余部署,冗余部署相当于是服务的分区,这样的分区就具有容错性。

2.BASE理论

BASE理论是对CAP理论的延申,核心思想是即使无法做到强一致性(CAP理论中的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性。

  • 基本可用

    基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。

    电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。

  • 软状态

    允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication 的异步复制就是一种体现。

  • 最终一致性

    系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

满足一致性或可用性的服务。——避免单点故障,就要进行冗余部署,冗余部署相当于是服务的分区,这样的分区就具有容错性。

2.BASE理论

BASE理论是对CAP理论的延申,核心思想是即使无法做到强一致性(CAP理论中的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性。

  • 基本可用

    基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。

    电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。

  • 软状态

    允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication 的异步复制就是一种体现。

  • 最终一致性

    系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值