zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,

(一)zookeeper数据模型

树形结构

  • 每个节点里面保存信息
  • 节点拥有子节点
  • 节点是临时的也可以是持久的
    zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第1张图片

四大节点
zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第2张图片

PERSISTENT-持久化目录节点
客户端与zookeeper断开连接后,该节点依旧存在

PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

EPHEMERAL-临时目录节点
客户端与zookeeper断开连接后,该节点被删除

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

(二)zookeeper服务端常用命令并且 集成springboot

zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第3张图片

zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第4张图片
常用客户端命令

常用命令详情

docker 下操作 zoopkeeper

docker zookeeper 启动成功 ,简单操作zoopkeeper ,节点不能重复创建

[zk: localhost:2181(CONNECTED) 3] create /t1 ck
Created /t1
[zk: localhost:2181(CONNECTED) 4] create /t2 zk
Created /t2
[zk: localhost:2181(CONNECTED) 5] get /t1
ck
[zk: localhost:2181(CONNECTED) 6] set /t1 aaaa
[zk: localhost:2181(CONNECTED) 7] get /t1
aaaa
[zk: localhost:2181(CONNECTED) 8] delete /t1
[zk: localhost:2181(CONNECTED) 9] get /t1
Node does not exist: /t1
[zk: localhost:2181(CONNECTED) 10] create /t2 zk
Node already exists: /t2


#子节点的操作
[zk: localhost:2181(CONNECTED) 12] create /t1/y1 bbb
Created /t1/y1
[zk: localhost:2181(CONNECTED) 13] ls /t1 
[y1]
[zk: localhost:2181(CONNECTED) 14] delete /t1/y1
[zk: localhost:2181(CONNECTED) 15] delete /t1
[zk: localhost:2181(CONNECTED) 16] get /t1
Node does not exist: /t1

临时创建

// -e :创建:临时节点 
[zk: localhost:2181(CONNECTED) 0] create -e /app1 
Created /app1

// 退出客户端后 临时节点 app1 消失了
[zk: localhost:2181(CONNECTED) 4] quit 

[zk: localhost:2181(CONNECTED) 1] ls /
[t2, zookeeper]

创建顺序节点

[zk: localhost:2181(CONNECTED) 2] create -s /app1
Created /app10000000004
[zk: localhost:2181(CONNECTED) 3] create -s /app1
Created /app10000000005
[zk: localhost:2181(CONNECTED) 4] create -s /app1
Created /app10000000006
[zk: localhost:2181(CONNECTED) 5] create -s /app1
Created /app10000000007
[zk: localhost:2181(CONNECTED) 6] ls /
[app10000000004, app10000000005, app10000000006, app10000000007, t2, zookeeper]

创建临时顺序节点

Created /app20000000008
[zk: localhost:2181(CONNECTED) 8] create -es /app2
Created /app20000000009
[zk: localhost:2181(CONNECTED) 9] create -es /app2
Created /app20000000010
[zk: localhost:2181(CONNECTED) 10] ls /
[app10000000004, app10000000005, app10000000006, app10000000007, app20000000008, app20000000009, app20000000010, t2, zookeeper]
[zk: localhost:2181(CONNECTED) 11] quit

ls2 :命令

我是再2022年 docker 下安装的zookeeper ,没有 ls2 命令 !!!
用以下命令

ls -s /

[zk: localhost:2181(CONNECTED) 4] ls -s /
[app10000000004, app10000000005, app10000000006, app10000000007, t2, zookeeper]
cZxid = 0x0
ctime = Thu Jan 01 00:00:00 UTC 1970
mZxid = 0x0
mtime = Thu Jan 01 00:00:00 UTC 1970
pZxid = 0x17
cversion = 16
dataVersion = 0
aclVersion = 0
#是否临时
ephemeralOwner = 0x0
dataLength = 0
#有几个子节点
numChildren = 6

Curator

  • 高版本的可以操作低版本的zookeeper,反过来不行

导入坐标并且配置连接

     <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.2.0</version>
        </dependency>
        <!-- curator-recipes -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.2.0</version>
        </dependency>


zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第5张图片

重试策略

zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第6张图片
zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第7张图片
RetryUntil elapsed(v. (时间)消逝,流逝;) : 重试总共时间,到了结束不重试

创建客户端进行连接

   @Test
    void contextLoads() {
        //重试策略
        RetryPolicy retry = new ExponentialBackoffRetry(3000, 10);
        //创建连接
    /*   CuratorFramework client = CuratorFrameworkFactory.newClient("124.222.131.252:2181", 60000, 60000, retry);

        //开启连接
        client.start();
    */
            //namespace :起到隔离的作用  你 create /app1 ==  create /cunk/app1
        CuratorFramework client = CuratorFrameworkFactory.builder().connectString("124.222.131.252:2181")
                .retryPolicy(retry).namespace("cunk").build();

    }

前置知识 测试生命周期:

测试实例生命周期
为了允许隔离执行单个的测试方法,并避免由于可变测试实例状态而产生的意外副作用,JUnit在执行每个测试方法之前创建每个测试类的新实例(请参阅下面的讲解,何为测试方法)。这个”per-method”测试实例生命周期是JUnit Jupiter中的默认行为,类似于JUnit以前的所有版本。

如果您希望JUnit Jupiter在同一个测试实例上执行所有测试方法,只需使用@TestInstance(Lifecycle.PER_CLASS)对您的测试类进行注解即可。当使用这种模式时,每个测试类将创建一个新的测试实例。因此,如果您的测试方法依赖于存储在实例变量中的状态,则可能需要在@BeforeEach或@AfterEach方法中重置该状态。

“per-class”模式比默认的”per-method”模式有一些额外的好处。具体来说,使用”per-class”模式,可以在非静态方法和接口默认方法上声明@BeforeAll和@AfterAll(否则@BeforeAll与@AfterAll必须是注解在static的方法上才能生效)。因此,”per-class”模式也可以在@Nested测试类中使用@BeforeAll和@AfterAll方法。

如果使用Kotlin编程语言编写测试,则可能会发现,通过切换到”per-class”测试实例生命周期模式,可以更轻松地实现@BeforeAll和@AfterAll方法。
来源于: https://blog.csdn.net/HD243608836/article/details/101000429
推荐一个宝藏网站 : https://www.bookstack.cn/

对节点进行操作

节点类型枚举 
@Public
public enum CreateMode {
    PERSISTENT(0, false, false, false, false),
    PERSISTENT_SEQUENTIAL(2, false, true, false, false),
    EPHEMERAL(1, true, false, false, false),
    EPHEMERAL_SEQUENTIAL(3, true, true, false, false),
    CONTAINER(4, false, false, true, false),
    PERSISTENT_WITH_TTL(5, false, false, false, true),
    PERSISTENT_SEQUENTIAL_WITH_TTL(6, false, true, false, true);

创建一个节点

@SpringBootTest
class ZookeeperApplicationTests {

    CuratorFramework client ;

    //在任何test方法执行之气那执行
          //相对于after
    @BeforeEach
    void  contextLoads() {
        //重试策略
        RetryPolicy retry = new ExponentialBackoffRetry(3000, 10);
        //创建连接
    /*   CuratorFramework client = CuratorFrameworkFactory.newClient("124.222.131.252:2181", 60000, 60000, retry);

        //开启连接
        client.start();
    */
            //namespace :起到隔离的作用
        client  = CuratorFrameworkFactory.builder().connectString("***.***.***.***:2181")
                .retryPolicy(retry).namespace("cunk").build();
        client.start();
        //创建(create)节点 持久 临时 顺序 数据
        // 1.创建基本
    }


   @Test
   public void create() throws Exception {
       String path = client.create().forPath("/ck1");
       System.out.println(path);
   }
   
   res:
[zk: localhost:2181(CONNECTED) 7] ls /
[app10000000004, app10000000005, app10000000006, app10000000007, cunk, t2, zookeeper]
[zk: localhost:2181(CONNECTED) 8] ls /cunk
[ck1]
[zk: localhost:2181(CONNECTED) 9] get /cunk/ck1
***.***.***   === 默认是有数据 的 是当前机器的ip地址 


    @AfterEach
    public void close(){
        if (client!=null){
            client.close();
        }
    }
}

创建临时节点

zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第8张图片

 @Test
    public void createWithDateAndEPHEMERAL() throws Exception {

        //创建临时节点 ,当前会话一但结束 ,节点瞬间消失!!!
        /*
        *   临时节点消失策略 : 会话结束边消失
        *                   1.在linux 上调用客户端 然后退出客户端 ,节点消失
        *                   2.在java api上创建节点 ,然后关闭java客户端 ,连接终端 ,节点消失
        * */
        
        client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3","hi~".getBytes(StandardCharsets.UTF_8)) ;
    }

while(true){} :可以一直保持连接
然程序空转会一直保持连接
[zk: localhost:2181(CONNECTED) 12] ls /cunk
[app3, ck1]

假如我在linux 退出zk 会怎样呢 ? 临时节点还会存在吗?



WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0] ls /cunk
[app3, ck1]
^[[A[zk: localhost:2181(CONNECTED) 0] get /cunk/app3
hi~

可以发现 ,在linux 客户端启动 zk 然后退出 并不会 对 java api与zk 建立连接后创建的临时节点有任何影响,说明他们是两次不同的会话,不同会话产生的临时节点的消失 ,代表着 相对应会话的消失

此时关闭java客户端再查 app3

[zk: localhost:2181(CONNECTED) 4] get /cunk/app3
Node does not exist: /cunk/appc

创建多级节点

    //创建多级节点
    @Test
    public void createWithParent() throws Exception{
        //creatingParentContainersIfNeeded() :调用这个方法即使父节点不存在也可以创建多级节点
        client.create().creatingParentContainersIfNeeded().forPath("/app4/cunk1");
    }

查询节点

    1. get 查数据
  • 2.ls 子节点
  • 3.ls -s 查询状态信息
  • zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第9张图片

    @Test
    public void checkWithParent() throws Exception {
        byte[] bytes = client.getData().forPath("/cunk2");
        System.out.println("========================================================");
        System.out.println("查询结果是: 》》"+new String(bytes)+" <<<<<");
    }

    @Test
    public void checkForParh() throws Exception {
        List<String> list = client.getChildren().forPath("/app4");
        System.out.println("========================================================");
        System.out.println("查询结果是: 》》"+list+" <<<<<");

    }

    @Test
    public void checkForParh3() throws Exception {
        //创建一个stat  查询出来的节点信息会封装进 stat
        Stat stat = new Stat();
        byte[] bytes = client.getData().storingStatIn(stat).forPath("/cunk2");
        System.out.println("========================================================");
        System.out.println("查询结果是: 》》"+stat+" <<<<<");
    }
========================================================
查询结果是: 》》59,59,1660367996068,1660367996068,0,0,0,0,10,0,59

更新节点

1.普通版

  @Test
    public void pathset() throws Exception {
        //修改app4 节点
        client.setData().forPath("/app4", "new data".getBytes(StandardCharsets.UTF_8));

        //再次查询app4节点的数值
        byte[] bytes = client.getData().forPath("/app4");
        System.out.println("========================================================");
        System.out.println("查询结果是: 》》"+new String(bytes)+" <<<<<");
    }

2.乐观锁版

前置知识 乐观锁,为什么更新需要带上版本号:乐观锁链接


  /*  当查询值为 1 时候 准备让值加 1 ,但是多线程情况下别的线程会对该值加一,总共会加 2
      修改数据一般需要带上版本号
      查询出来的版本号和 当前版本号相同才进行修改  (乐观锁)
  */
 @Test
    public void pathsetForVersion() throws Exception {
     //查询版本号
         Stat stat = new Stat();
        byte[] bytes = client.getData().storingStatIn(stat).forPath("/app4");

    //根据版本号就行修改
        int version = stat.getVersion();
      client.setData().withVersion(version).forPath("/app4","version Data".getBytes(StandardCharsets.UTF_8));

        //再次查询app4节点的数值
        System.out.println("========================================================");
        System.out.println("查询结果是: 》》"+new String(bytes)+" <<<<<");

    }
    
linux 查询数据:
[zk: localhost:2181(CONNECTED) 11] get /cunk/app4
versionDate

删除节点

```css
//删除节点
    // 1. 删除单个节点   2. 删除子节点的节点  3.必须删除成功  4. 回调
    @Test
    public void pathsetDelete() throws Exception {
        //删除单个节点
        client.delete().forPath("/app4") ;
    }

    //删除带有子节点的节点
    @Test
    public void pathsetDeletea() throws Exception {
        client.delete().deletingChildrenIfNeeded().forPath("/app4");
    }

    //必须删除成功 ,不断重试删除节点 。类似于rabbitmq 的消息发不出去的重试机制
    //在测试前可以关闭zookeeper,启动测试,过一段时间再连接发现还能删除。
    @Test
    public void mustpathsetDeletea() throws Exception {
        client.delete().guaranteed().forPath("/app4/cunk1") ;
    }


    //回调
    @Test
    public void returnDeletea() throws Exception {
     client.delete().guaranteed().inBackground(new BackgroundCallback() {
         //回调函数
         @Override
         public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
             System.out.println("删除成功!!!!!");
             System.out.println(curatorEvent);
         }
     }).forPath("/app4") ;

    }

res :
exiting
删除成功!!!!!
CuratorEventImpl{type=DELETE, resultCode=0, path='/app4', name='null', children=null, context=null, stat=null, data=null, watchedEvent=null, aclList=null, opResults=null}
2022-08-13 14:25:07.980  INFO 26036 --- [ain-EventThread] org.apache.zookeeper.ClientCnxn          : EventThread shut down for session: 0x1005b20cf740027
2022-08-13 14:25:07.980  INFO 26036 --- [           main] org.apache.zookeeper.ZooKeeper           : Session: 0x1005b20cf740027 closed

事件监听

类似于观察者模式+发布订阅 。 当一个节点信息改变 他会告诉其他的节点。
zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第10张图片
zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第11张图片

java api --事件监听

  • 监听某个节点
    @Test
    public void lensen() throws Exception {
        //创建 NodeCache            监听(/cunk/ck1)上的变动
        NodeCache nodeCache = new NodeCache(client, "/ck1");

        // 注册监听
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                System.out.println("节点变化了 ~~~");
                //获取修改后的节点数据
                byte[] data = nodeCache.getCurrentData().getData();
                System.out.println(new String(data));
            }
        });

        // 开启监听  buildInitial:true ,开启监听时候开启缓存
        nodeCache.start(true);

        //保证一直监听
        while (true){
        }
    }

  • 监听孩子节点
 @Test
    public void lensenC() throws Exception {
        //创建 NodeCache            监听(/cunk/ck1)上的变动
        PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/ck1", true);

        // 注册监听
     pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
         @Override
         public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
             System.out.println("儿子们变化了 ~~~");
             System.out.println(pathChildrenCacheEvent);
             // 监听孩子们 的数据变更
             PathChildrenCacheEvent.Type childrenCacheEventType = pathChildrenCacheEvent.getType();

             //判断孩子节点是不是更新
          if (childrenCacheEventType.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
              log.info("有个孩子节点更新咯 ~~");
              byte[] data = pathChildrenCacheEvent.getData().getData();
              System.out.println(new String(data));
          }
         }
     });

· 监听孩子们的事件类型
zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第12张图片

  • 监听自己和他的孩子们

  
    //监听孩子们节点
    @Test
    public void lensenAll() throws Exception {
        //创建 NodeCache            监听(/cunk/ck1)上的变动
        TreeCache treeCache = new TreeCache(client, "/ck1");

        // 注册监听
        treeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
                log.info("节点变化了 ~~");
                log.info("变化情况是:{}",treeCacheEvent);
                TreeCacheEvent.Type type = treeCacheEvent.getType();
                if (type.equals(TreeCacheEvent.Type.NODE_ADDED)){
                    log.info("新增了个节点~~");
                }

            }
        });

        // 开启监听  buildInitial:true ,开启监听时候开启缓存
        treeCache.start();

        //保证一直监听
        while (true){

        }

    }

zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第13张图片

zook的分布式锁的实现(实现比理论简单)

在这里插入图片描述
要详细了解分布式锁 请移步:redis实现分布式锁…
zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第14张图片
分布式锁解决:跨机器进程之间的数据同步问题例如 抢票,秒杀。。。。

redis做分布式锁的弊端:虽然性能高 ,但是 redis挂了 ,所有人都能获得锁

zk概述

核心思想: 当客户想要获得锁,先创建节点,使用完毕锁,删除该节点

    1. 客户端获取锁时,再lock 节点下创建 临时顺序节点 临时:防止客户端宕机锁得不到释放,顺序:找最小节点

zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第15张图片
为什么创建的是临时顺序节点: 假如创建的是默认持久的节点 ,然后Clint1获取锁便 宕机了 ,Clien1与zk 失去连接 ,
但是/lock/1得不到释放。锁得不到释放!!!
这种情况再redis中的解决方案是:设置TTL 过期时间 。
临时节点的作用:当客户端 异常跟zk断联, 临时节点会自动释放 。其他节点就可以获得锁

    1. clin1 …3 在/lock 下分别对应的 /lock/1…3 谁的序号最小谁获得锁 故client1 获得锁
  • 3 锁的删除
    zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第16张图片

如果发现自己创建的节点不是lock所有节点序号中最小的(此时顺序节点的作用体现出来咯),
说明还没获得锁,此时客户端找到比自己小的
那个节点(找一个),同时对该节点注册监听,假如该节点删除,自己删除
zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第17张图片
lock1 删完 lock2 删 lock2 删完locak3 删

java zk锁api

这些概念请异步至 redis实现分布式锁…

zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第18张图片

模拟卖票

创建买票客户端


@Slf4j
public class TickCLinet implements Runnable{
    private static int tick = 10 ; //10 张票
    private InterProcessMutex lock ;

    //创建锁
    public TickCLinet(InterProcessMutex lock ){
        this.lock = lock ;
    }


    @Override
    public void run( ) {
         while (true){
             //获取锁
             try {
                 // 获取失败等待3 秒
                 lock.acquire(3,TimeUnit.SECONDS);
                 if (tick > 0){
                   // log.info("线程:"+Thread.currentThread().getName()+"获得 了票 ");

                     this.wait();
                     tick -- ;
                     //log.info("当前票数:{}",tick);
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             }finally {
                 try {
                     //防止程序发生异常锁得不到释放 加 finnaly 释放锁
                     lock.release();
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
             }

         }
    }
}

创建多个线程去获取票

@Slf4j
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
class TickTests  {
    private InterProcessMutex lock ;
    CuratorFramework client ;

    private int tickets = 10 ; //一共十张票

    //在任何test方法执行之气那执行
          //相对于after
    @BeforeEach
    void  contextLoads() {
        RetryPolicy retry = new ExponentialBackoffRetry(3000, 10);
        client  = CuratorFrameworkFactory.builder().connectString("124.222.131.252:2181")
                .retryPolicy(retry).namespace("cunk").build();
        client.start();
        //创建分布式锁
        lock = new InterProcessMutex(client, "/cunk");
    }

    @Test
    public void selltick() throws Exception{

        // 10个客户端去强票
        for (int i=0;i < 10;i++){
               new Thread(new TickCLinet(lock),"ClientApp_"+i).start();
          }

        //防止主线程先挂了
        while (true){

        }

    }


    @AfterEach
    public void testNodeclose(){
        if (client!=null){
            client.close();
        }
    }

}


[zk: localhost:2181(CONNECTED) 26] ls  /cunk/cunk
## 这是 zk自动创建的锁 
[_c_0581849d-005e-499c-a77f-6e246726da08-lock-0000000481, _c_0858fb78-c0fc-4e8d-9726-aab3ed5fe4d5-lock-0000000488, _c_0f1afac3-c590-405d-b663-eac5eabb1cf2-lock-0000000489, _c_45fe5ebc-ff86-4904-95a4-a3fd7f931950-lock-0000000484, _c_5fc2fd51-e9a8-4fa2-8a14-036d599a14dd-lock-0000000486, _c_a8089b0b-aff9-416d-8c5e-fa90378145b5-lock-0000000485, _c_d36115ef-da09-4a94-a36e-3f0503c248f4-lock-0000000482, _c_eeb5830a-cd9d-41f3-82cb-ab3bc7a267af-lock-0000000487, _c_f43096f8-ea9a-4fe6-8068-d11a0793e5b1-lock-0000000490, _c_f54c84c4-cabe-4b59-894d-8a6b424888d2-lock-0000000483]

# 当线程执行结束 或者异常关闭之后 锁得到释放 
[zk: localhost:2181(CONNECTED) 27] ls  /cunk/cunk
Node does not exist: /cunk/cunk

这个报错是正常现象(内部机制问题)…
zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第19张图片

有个小的知识点: 当程序异常结束的时候 , zk 释放锁不是瞬间释放的 ,是过了至少十秒才释放 ,意思是 一个服务宕机了 ,另外一个客户端不能瞬间获得锁 ,, 虽然这个研究有点无聊哈哈哈…
我有个问题 ,假如redis 和zk 做分布式锁 同时宕机了,redis由于watchdog 自动释放锁 , zk由于客户端断开连接 下一个获得锁的 谁快一些呢 ????

zookeeper集群搭建

领导选举

zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第20张图片
zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第21张图片
zookeeper-常用命令,集成springboot,分布式锁实现和原理 ,dock集群zookeeper搭建,_第22张图片

具体怎么搭建 :dockers下搭建 zk集群

  • 怎么创建网卡…可以看我docker专栏 ,说白了 十个zk集群需要在同一个网络下面
  • 需要一个投票选举接口
  • 停掉只剩一个zk ,集群会不可用,因为leader需要半数投票选举出来

你可能感兴趣的:(zookeeper,分布式,java-zookeeper,zookeeper,大数据)