Zookeeper基础知识:成功分布式系统的关键

文章目录

      • 一、引言
      • 二、Zookeeper介绍
      • 三、Zookeeper安装
      • 四、Zookeeper架构【`重点`】
        • [4.1 Zookeeper树形结构](#4.1 Zookeeper树形结构)
        • [4.2 znode类型](#4.2 znode类型)
        • [4.3 Zookeeper的监听通知机制](#4.3 Zookeeper的监听通知机制)
      • 五、Zookeeper常用操作
        • [5.1 zk常用命令](#5.1 zk常用命令)
        • [5.2 Java连接Zookeeper](#5.2 Java连接Zookeeper)
        • [5.3 Java操作Znode节点](#5.3 Java操作Znode节点)
        • [5.4 监听通知机制](#5.4 监听通知机制)
      • 六、Zookeeper集群【`重点`】
        • [6.1 Zookeeper集群架构图](#6.1 Zookeeper集群架构图)
        • [6.2 Zookeeper集群中节点的角色](#6.2 Zookeeper集群中节点的角色)
        • [6.3 Zookeeper数据同步](#6.3 Zookeeper数据同步)
        • [6.4 Zookeeper选举](#6.4 Zookeeper选举)
        • [6.5 搭建Zookeeper集群](#6.5 搭建Zookeeper集群)
        • [6.6 Zookeeper过半数存活原则](#6.6 Zookeeper过半数存活原则)
        • [6.7 为什么Zookeeper需要设计一个过半数存活机制?](#6.7 为什么Zookeeper需要设计一个过半数存活机制?)
      • 七、Java连接Zookeeper集群
      • 八,Zookeeper应用场景
        • [8.1 配置文件管理](#8.1 配置文件管理)
        • [8.2 集群管理](#8.2 集群管理)
        • [8.3 分布式锁](#8.3 分布式锁)
        • [8.4 命名服务(Dubbo监控中心原理)](#8.4 命名服务(Dubbo监控中心原理))
        • [8.5 发现服务](#8.5 发现服务)

一、引言


在分布式环境下,如果舍弃SpringCloud,使用其他的分布式框架,那么注册心中,配置集中管理,集群管理,分布式锁,分布式任务,队列的管理想单独实现怎么办。

分布式系统面临的问题

  1. 分布式系统如何实现对同一资源的访问,保证数据的强一致性?
  2. 集群中的Master挂了,传统做法是什么?zookeeper又是如何做的?

二、Zookeeper介绍


ZooKeeper是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。

简单来说zookeeper=文件系统+监听通知机制

ZooKeeper最为主要的使用场景,是作为分布式系统的分布式协同服务。在学习zookeeper之前,先要对分布式系统的概念有所了解。

ZooKeeper

三、Zookeeper安装


docker-compose.yml

yml 复制代码
version: "3.1"
services:
  zk:
   image: daocloud.io/daocloud/zookeeper:latest
   restart: always
   container_name: zk
   ports:
     - 2181:2181

四、Zookeeper架构【重点


4.1 Zookeeper树形结构

每个子目录项如 NameService 都被称作为 znode(目录节点),和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。

Zookeeper的架构图
4.2 znode类型
  • PERSISTENT-持久化目录节点

    ​ 客户端与zookeeper断开连接后,该节点依旧存在

  • PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点

    ​ 客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

  • EPHEMERAL-临时目录节点

    ​ 客户端与zookeeper断开连接后,该节点被删除

  • EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点

    ​ 客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号

4.3 Zookeeper的监听通知机制

zookeeper提供了节点watch的功能,zookeeper的client监控zookeeper上的节点,当节点变动的时候,client会收到变动事件和变动后的内容,基于zookeeper的这个特性,我们可以给服务器集群中的所有机器都注册watch事件,监控特定znode,节点中存储部署代码的配置信息,需要更新代码的时候,修改znode中的值,服务器集群中的每一台server都会收到代码更新事件,然后触发调用,更新目标代码。也可以很容易的横向扩展,可以随意的增删机器,机器启动的时候注册监控节点事件即可。

监听通知机制

五、Zookeeper常用操作

5.1 zk常用命令

Zookeeper针对增删改查的常用命令

5.2 Java连接Zookeeper

创建Maven工程
导入依赖

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.6.0</version>
    </dependency>

      <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>2.12.0</version>
        </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

编写连接Zookeeper集群的工具类

java 复制代码
public class ZkUtil {

    public static CuratorFramework cf(){
        // 超时时间是3s,重试2次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                  .connectString("192.168.193.88:2181")
                .retryPolicy(retryPolicy)
                .build();

        cf.start();

        return cf;
    }

}
5.3 Java操作Znode节点

查询

java 复制代码
public class Demo2 {

    CuratorFramework cf = ZkUtil.cf();

    // 获取子节点
    @Test
    public void getChildren() throws Exception {
        List<String> strings = cf.getChildren().forPath("/");

        for (String string : strings) {
            System.out.println(string);
        }
    }

    // 获取节点数据
    @Test
    public void getData() throws Exception {
        byte[] bytes = cf.getData().forPath("/qf");
        System.out.println(new String(bytes,"UTF-8"));
    }

}

添加

java 复制代码
@Test
public void create() throws Exception {
    cf.create().withMode(CreateMode.PERSISTENT).forPath("/qf2","uuuu".getBytes());
}

修改

java 复制代码
@Test
public void update() throws Exception {
    cf.setData().forPath("/qf2","oooo".getBytes());
}

删除

java 复制代码
@Test
public void delete() throws Exception {
    cf.delete().deletingChildrenIfNeeded().forPath("/qf2");
}

查看znode的状态

java 复制代码
@Test
public void stat() throws Exception {
    Stat stat = cf.checkExists().forPath("/qf");
    System.out.println(stat);
}
5.4 监听通知机制
java 复制代码
public class Demo3 {

    CuratorFramework cf = ZkUtil.cf();

  @Test
    public void tesetListener() throws Exception{
        CuratorFramework cf = ZkClinet.cf();

        TreeCache nodeCache = new TreeCache(cf,"/demo");

        nodeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
                ChildData eventData = event.getData();
                switch (event.getType()) {
                    case NODE_ADDED:
                        System.out.println("/demo" + "节点添加" + eventData.getPath() + "\t添加数据为:" + new String(eventData.getData()));
                        break;
                    case NODE_UPDATED:
                        System.out.println(eventData.getPath() + "节点数据更新\t更新数据为:" + new String(eventData.getData()) + "\t版本为:" + eventData.getStat().getVersion());
                        break;
                    case NODE_REMOVED:
                        System.out.println(eventData.getPath() + "节点被删除");
                        break;
                    default:
                        break;
                }
            }
        });
        nodeCache.start();
        System.out.println("监听开始。。。");
        System.in.read();
    }
}

六、Zookeeper集群【重点


6.1 Zookeeper集群架构图
集群架构图
6.2 Zookeeper集群中节点的角色
  • Leader(Master):事务请求的唯一处理者,也可以处理读请求。
  • Follower(Slave):可以直接处理客户端的读请求,并向客户端响应;但其不会处理事务请求,其只会将客户端事务请求转发给Leader来处理,同步 Leader 中的事务处理结果;Leader 选举过程的参与者,具有选举权与被选举权。(就好像正式工)
  • Observer(Slave):可以理解为不参与 Leader 选举的 Follower,在 Leader 选举过程中没有选举权与被选举权;同时,对于 Leader 的提案没有表决权。用于协助 Follower 处理更多的客户端读请求。Observer 的增加,会提高集群读请求处理的吞吐量,但不会增加事务请求的通过压力,不会增加 Leader 选举的压力。(就好像临时工)
6.3 Zookeeper数据同步

​ ZooKeeper 集群的所有机器通过一个 Leader来完成写服务(也可以完成读)。Follower只提供读服务,不能提供写服务。

  1. 如果有机器要对节点做更新,这个机器先告诉Leader。

  2. Leader收到后请求后广播给所有的节点进行写操作,每个角色都在自己的机器中写。

  3. 每个机器写完后都给Leader汇报是否写入成功

  4. 如果有一半的机器写成功了Leader就下发第二个指令

  5. 提交事务,以广播的形式发出。

6.4 Zookeeper选举
  • 每一个Zookeeper服务都会被分配一个全局唯一的myid,··是一个数字。
  • Zookeeper在执行写数据时,每一个节点都有一个自己的FIFO的队列。保证写每一个数据的时候,顺序是不会乱的,Zookeeper还会给每一个数据分配一个全局唯一的zxid,数据越新zxid就越大。
  • 选举Leader:
    • 选举出zxid最大的节点作为Leader。
    • 在zxid相同的节点中,选举出一个myid最大的节点,作为Leader。
6.5 搭建Zookeeper集群

1、2181:对cline端提供服务

2、3888:选举leader使用

3、2888:集群内机器通讯使用(Leader监听此端口)

yml 复制代码
version: "3.1"
services:
  zk1:
    image: zookeeper
    restart: always
    container_name: zk1
    ports:
      - 2181:2181
    environment:
      ZOO_MY_ID: 1
      ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888;2181
  zk2:
    image: zookeeper
    restart: always
    container_name: zk2
    ports:
      - 2182:2181
    environment:
      ZOO_MY_ID: 2
      ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888;2181
  zk3:
    image: zookeeper
    restart: always
    container_name: zk3
    ports:
      - 2183:2181
    environment:
      ZOO_MY_ID: 3
      ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888;2181

查看节点状态 zkServer.sh status

6.6 Zookeeper过半数存活原则

​ 在zookeeper集群中,当存活的机器数量超过总机器的一半的时候,整个集群才能正常工作,否则拒绝访问。基于过半数存活原则,zookeeper的集群机器数量一定是奇数台,因为2N+1和2N+2的容灾能力是一样的,基于成本考虑2N+1台的选择方案更优。

6.7 为什么Zookeeper需要设计一个过半数存活机制?

*脑裂问题*

集群中的节点监听不到leader节点的心跳, 就会认为leader节点出了问题, 此时集群将分裂为不同的小集群, 这些小集群会各自选举出自己的leader节点, 导致原有的集群中出现多个leader节点。

为了防止网络脑裂,保证数据的强一致性,因为整个集群中,有可能因为网络问题"脑裂",导致整个集群分为2个甚至多个集群,如果没有过半数存活机制,那么整个zookeeper会变成多个集群,那么zookeeper提供的数据无法再保证数据一致性;

七、Java连接Zookeeper集群

编写连接Zookeeper集群的工具类

java 复制代码
public class ZkUtil {

    public static CuratorFramework cf(){
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString("192.168.199.109:2181,192.168.199.109:2182,192.168.199.109:2183")
                .retryPolicy(retryPolicy)
                .build();

        cf.start();

        return cf;
    }
}

测试类

测试

八,Zookeeper应用场景

8.1 配置文件管理

将需要统一管理的配置全部放到zookeeper上去,保存在 Zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中。

8.2 集群管理

所有机器约定在父目录GroupMembers下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除。新机器加入也是类似,

我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好。

8.3 分布式锁

我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock 节点就释放出锁

8.4 命名服务(Dubbo监控中心原理)

在日常开发中,我们会遇到这样的场景:服务A需要访问服务B,但是服务B还在开发过程中(未完成),那么服务A(此时已完成)就不知道如何获取服务B的访问路径了,使用zookeeper的服务就可以简单解决:服务B部署成功后,可以先到zookeeper注册服务(即在zookeeper添加节点/service/B和节点数据)。服务A开发结束后,部署到服务器,然后服务A监控zookeeper服务节点/service/B,如果发现节点有数据了,那么服务A就可以访问服务B了。

8.5 发现服务

注册一个持久节点/service/business/what,他下面的每个子节点都是一个可用服务,保存了服务的地址端口等信息,服务调用者通过zookeeper获取/service/business/what所有子节点信息来得到可用的服务。下面的节点都是临时节点,服务器启动的时候会过来注册一个临时节点,服务器挂掉之后或主动关闭之后,临时节点会自动移除,这样就可以保证使用者获取的what服务都是可用的,而且可以动态的扩容缩容。

后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹

相关推荐
hrrrrb40 分钟前
【Spring Security】Spring Security 概念
java·数据库·spring
小信丶41 分钟前
Spring 中解决 “Could not autowire. There is more than one bean of type“ 错误
java·spring
翰林小院2 小时前
【RabbitMQ】 RabbitMQ Overview
分布式·rabbitmq
周杰伦_Jay2 小时前
【Java虚拟机(JVM)全面解析】从原理到面试实战、JVM故障处理、类加载、内存区域、垃圾回收
java·jvm
摇滚侠3 小时前
Spring Boot 3零基础教程,IOC容器中组件的注册,笔记08
spring boot·笔记·后端
程序员小凯5 小时前
Spring Boot测试框架详解
java·spring boot·后端
豐儀麟阁贵5 小时前
基本数据类型
java·算法
_extraordinary_6 小时前
Java SpringMVC(二) --- 响应,综合性练习
java·开发语言
程序员 Harry6 小时前
深度解析:使用ZIP流式读取大型PPTX文件的最佳实践
java
程序员小凯7 小时前
Spring Boot缓存机制详解
spring boot·后端·缓存