zookeeper 学习笔记 (二)

第一章 Zookeeper Java客户端

添加pom依赖文件

   <dependencies>
        <!--日志依赖-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.8.2</version>
        </dependency>
        <!--curator 框架依赖-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>5.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-x-discovery</artifactId>
            <version>5.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--zookeeper依赖,最好和服务器版本一致-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.5.8</version>
        </dependency>
        <!--测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
        <!--lombook 依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <!--json 序列化依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.3</version>
        </dependency>
    </dependencies>

java 实现zookeeper连接等操作①

@Slf4j
public class zkClient {

    // 注意:逗号左右不能有空格
    private String connectString = "192.168.56.101:2181";
    private int sessionTimeout = 2000;
    private ZooKeeper zkClient;

    @Before
    public void init() throws IOException, InterruptedException {
        zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                // 收到事件之后的回调事件 处理用户自定义的函数
                System.out.println("-------------------------------");

                List<String> children = null;
                try {
                    children = zkClient.getChildren("/", true);

                    for (String child : children) {
                        System.out.println(child);
                    }
					
                    System.out.println("-------------------------------");
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // 因为创建zookeeper客户端的sendThread 是守护进程,可能会存在连接还在没有创建好主线程就推出了,
        // 此时zkClient马上就会自动退出
        // 当然也可以使用 new CountDownLatch(1) 对象,当链接创建好了之后让主线程继续
        Thread.sleep(Long.MAX_VALUE);
    }

    @Test
    public void create() throws KeeperException, InterruptedException {
        String nodeCreated = zkClient.create("/atguigu", "ss.avi".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    @Test
    public void getChildren() throws KeeperException, InterruptedException {
        List<String> children = zkClient.getChildren("/", true);
        for (String child : children) {
            System.out.println(child);
        }
    }

    @Test
    public void exist() throws KeeperException, InterruptedException {
        Stat stat = zkClient.exists("/atguigu", false);
        System.out.println(stat==null? "not exist " : "exist");
    }

	// 异步创建节点
    @Test
    public void createAsycTest() throws InterruptedException {
        zkClient.create("/ZK_test_async", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, new AsyncCallBack(), "I am content");
        Thread.sleep(Integer.MAX_VALUE);
    }

    class AsyncCallBack implements AsyncCallback.StringCallback{
        public void processResult(int rc,  String path,  Object ctx,  String name){
            System.out.println("Create path result:["+rc+","+path+","+ctx+",real path name"+name);
        }
    }
}

zookeeper学习笔记–创建节点

sendThread daemon 守护线程

在这里插入图片描述

countDownLatch demo

@Before
    public void init() throws IOException, InterruptedException {
        CountDownLatch count = new CountDownLatch(1);
        zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                if (watchedEvent.getType()== Event.EventType.None
                        && watchedEvent.getState() == Event.KeeperState.SyncConnected){
                    log.info("连接已建立");
                    count.countDown();
                }
            }
        });
        count.await();
    }

java 创建zknode demo2

   private final static  String CONNECT_STR="192.168.56.101:2181";

    private final static Integer  SESSION_TIMEOUT=30*1000;

    private static ZooKeeper zooKeeper=null;

	// 防止连接还未创建主线程就已经退出了
    private static CountDownLatch countDownLatch=new CountDownLatch(1);

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {

        zooKeeper=new ZooKeeper(CONNECT_STR, SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                    if (event.getType()== Event.EventType.None
                            && event.getState() == Event.KeeperState.SyncConnected){
                        log.info("连接已建立");
                        countDownLatch.countDown();
                    }
            }
        });
        countDownLatch.await();

        MyConfig myConfig = new MyConfig();
        myConfig.setKey("anykey");
        myConfig.setName("anyName");

        ObjectMapper objectMapper=new ObjectMapper();

        byte[] bytes = objectMapper.writeValueAsBytes(myConfig);

        String s = zooKeeper.create("/myconfig", bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        Watcher watcher = new Watcher() {
            @SneakyThrows
            @Override
            public void process(WatchedEvent event) {
                if (event.getType()== Event.EventType.NodeDataChanged
                        && event.getPath()!=null && event.getPath().equals("/myconfig")){
                    log.info(" PATH:{}  发生了数据变化" ,event.getPath());
                    byte[] data = zooKeeper.getData("/myconfig", this, null);
                    MyConfig newConfig = objectMapper.readValue(new String(data), MyConfig.class);
                    log.info("数据发生变化: {}",newConfig);
                }
            }
        };

        byte[] data = zooKeeper.getData("/myconfig", watcher, null);
        MyConfig originalMyConfig = objectMapper.readValue(new String(data), MyConfig.class);
        log.info("原始数据: {}", originalMyConfig);

        TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
    }

原生zookeeper依赖连接操作zookeeper

@Slf4j
public abstract class  StandaloneBase {
    private static final  String  CONNECT_STR="192.168.56.101:2182,192.168.56.101:2183,192.168.56.101:2184";
    private static final  int SESSION_TIMEOUT=30 * 1000;
    private static ZooKeeper  zooKeeper =null;
    private static  CountDownLatch countDownLatch = new CountDownLatch(1);

    private Watcher watcher =new Watcher() {
        @Override
        public void process(WatchedEvent event) {
                    if (event.getState() == Event.KeeperState.SyncConnected
                            && event.getType()== Event.EventType.None){
                        countDownLatch.countDown();
                        log.info("连接建立");
                    }
        }
    };
    
    @Before
    public void init(){
        try {
            log.info(" start to connect to zookeeper server: {}",getConnectStr());
             zooKeeper=new ZooKeeper(getConnectStr(), getSessionTimeout(), watcher);
            log.info(" 连接中...");
            countDownLatch.await();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static ZooKeeper getZooKeeper() {
        return zooKeeper;
    }
    
    @After
    public void test(){
        try {
            TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    protected String getConnectStr(){
        return CONNECT_STR;
    }
    
    protected int getSessionTimeout() {
        return SESSION_TIMEOUT;
    }
}

@Slf4j
public class BaseOperations extends StandaloneBase {

    private String first_node = "/first-node";

    @Test
    public void testCreate() throws KeeperException, InterruptedException {
        ZooKeeper zooKeeper = getZooKeeper();

        String s = zooKeeper.create(first_node, "first".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        log.info("Create:{}", s);
    }

    @Test
    public void testGetData() {
        Watcher watcher = new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getPath() != null && event.getPath().equals(first_node)
                        && event.getType() == Event.EventType.NodeDataChanged) {
                    log.info(" PATH: {}  发现变化", first_node);
                    try {
                        byte[] data = getZooKeeper().getData(first_node, this, null);
                        log.info(" data: {}", new String(data));
                    } catch (KeeperException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        try {
            byte[] data = getZooKeeper().getData(first_node, watcher, null);  //
            log.info(" data: {}", new String(data));
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testSetData() throws KeeperException, InterruptedException {
        ZooKeeper zooKeeper = getZooKeeper();
        
        Stat stat = new Stat();
        byte[] data = zooKeeper.getData(first_node, false, stat);
        // int version = stat.getVersion();
        zooKeeper.setData(first_node, "third".getBytes(), 0);
    }


    @Test
    public void testDelete() throws KeeperException, InterruptedException {
        // -1 代表匹配所有版本,直接删除
        // 任意大于 -1 的代表可以指定数据版本删除
        getZooKeeper().delete("/config", -1);

    }

    @Test
    public void asyncTest() {
        String userId = "xxx";
        getZooKeeper().getData("/test", false, (rc, path, ctx, data, stat) -> {
            Thread thread = Thread.currentThread();

            log.info(" Thread Name: {},   rc:{}, path:{}, ctx:{}, data:{}, stat:{}", thread.getName(), rc, path, ctx, data, stat);
        }, "test");
        log.info(" over .");

    }
}

// 权限操作
@Slf4j
public class AclOperations extends StandaloneBase {

    /**
     *  用 world 模式创建节点
     *
     * @throws KeeperException
     * @throws InterruptedException
     */
    @Test
    public void createWithAclTest1() throws KeeperException, InterruptedException {

        List<ACL> acLList = new ArrayList<ACL>();
        ACL e = new ACL();
        Id m_ = new Id();
        m_.setId("anyone");
        m_.setScheme("world");

        int perms = ZooDefs.Perms.ADMIN  |  ZooDefs.Perms.READ;
        e.setId(m_);
        e.setPerms(perms);
        acLList.add(e);
        String s = getZooKeeper().create("/zk-node-1", "gj".getBytes(), acLList, CreateMode.PERSISTENT);
        log.info("create path: {}",s);
        getZooKeeper().delete("/zk-node-1",-1);
    }

    /**
     *
     * 用授权模式创建节点
     * @throws KeeperException
     * @throws InterruptedException
     */
    @Test
    public void createWithAclTest2() throws KeeperException, InterruptedException {

        // 对连接添加授权信息
        getZooKeeper().addAuthInfo("digest","u400:p400".getBytes());

        List<ACL> acLList = new ArrayList<ACL>();
        ACL e = new ACL();
        Id m_ = new Id();
        m_.setId("u400:p400");
        m_.setScheme("auth");

        int perms = ZooDefs.Perms.ADMIN  |  ZooDefs.Perms.READ;
        e.setId(m_);
        e.setPerms(perms);
        acLList.add(e);
        String s = getZooKeeper().create("/zk-node-2", "gj".getBytes(), acLList, CreateMode.PERSISTENT);
        log.info("create path: {}",s);
        getZooKeeper().delete("/zk-node-2",-1);
    }


    @Test
    public void createWithAclTest3() throws KeeperException, InterruptedException {

        // 对连接添加授权信息
        getZooKeeper().addAuthInfo("digest","u400:p400".getBytes());


        byte[] data = getZooKeeper().getData("/test", false, null);

        log.info("GET_DATA : {}",new String(data));
        getZooKeeper().delete("/test",-1);
    }
/*    public static void main(String[] args) throws NoSuchAlgorithmException {
        String sId = DigestAuthenticationProvider.generateDigest("gj:123");
        System.out.println(sId);
        //  -Dzookeeper.DigestAuthenticationProvider.superDigest=gj:RRCKWv2U2e99M6UmsFaJiQ2xStw=
    }*/
}

第二章 curator 框架

什么是 Curator

Curator 是一套由netflix 公司开源的,Java 语言编程的 ZooKeeper 客户端框架,Curator项目是现在ZooKeeper 客户端中使用最多,对ZooKeeper 版本支持最好的第三方客户端,并推荐使用,Curator 把我们平时常用的很多 ZooKeeper 服务开发功能做了封装,例如 Leader 选举、分布式计数器、分布式锁。这就减少了技术人员在使用 ZooKeeper 时的大部分底层细节开发工作。在会话重新连接、Watch 反复注册、多种异常处理等使用场景中,用原生的 ZooKeeper处理比较复杂。而在使用 Curator 时,由于其对这些功能都做了高度的封装,使用起来更加简单,不但减少了开发时间,而且增强了程序的可靠性。

Curator 实战

我们通过将 Curator 相关的引用包配置到Maven 工程的 pom 文件中,将 Curaotr 框架引用到工程项目里,在配置文件中分别引用了两个 Curator 相关的包,第一个是 curator-framework 包,该包是对 ZooKeeper 底层 API 的一些封装。另一个是 curator-recipes 包,该包封装了一些 ZooKeeper 服务的高级特性,如:Cache 事件监听、选举、分布式锁、分布式 Barrier。

1、会话创建

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3) // 重试策略
CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy);
client.start();

// builder 模式
 RetryPolicy retryPolicy = new ExponentialBackoffRetry(5000, 30);
 CuratorFramework curatorFramework= CuratorFrameworkFactory.builder().connectString("192.168.56.101:2181")
         .retryPolicy(retryPolicy )   // 重试策略
         .sessionTimeoutMs(5000)      // 会话超时时间
         .connectionTimeoutMs(5000)   // 连接超时时间
         .canBeReadOnly(true)
         .namespace("base")    		  // 包含隔离名称
         .build();
// 添加监听
curatorFramework.getConnectionStateListenable().addListener((client, newState) -> {
    if (newState == ConnectionState.CONNECTED) {
        log.info("连接成功!");
    }
});
curatorFramework.start();

这段代码的编码风格采用了流式方式,最核心的类是 CuratorFramework 类,该类的作用是定义一个 ZooKeeper 客户端对象,并在之后的上下文中使用。在定义 CuratorFramework 对象实例的时候,我们使用了 CuratorFrameworkFactory 工厂方法,并指定了 connectionString服务器地址列表、retryPolicy 重试策略 、sessionTimeoutMs 会话超时时间、connectionTimeoutMs 会话创建超时时间。下面我们分别对这几个参数进行讲解:

connectionString:服务器地址列表,在指定服务器地址列表的时候可以是一个地址,也可以是多个地址。如果是多个地址,那么每个服务器地址列表用逗号分隔, 如 host1:port1,host2:port2,host3;port3 。

retryPolicy:重试策略,当客户端异常退出或者与服务端失去连接的时候,可以通过设置客户端重新连接 ZooKeeper 服务端。而 Curator 提供了 一次重试、多次重试等不同种类的实现方式。在 Curator 内部,可以通过判断服务器返回的 keeperException 的状态代码来判断是否进行重试处理,如果返回的是 OK 表示一切操作都没有问题,而 SYSTEMERROR 表示系统或服务端错误。

超时时间:Curator 客户端创建过程中,有两个超时时间的设置。
一个是 sessionTimeoutMs会话超时时间,用来设置该条会话在 ZooKeeper 服务端的失效时间。
另一个是connectionTimeoutMs 客户端创建会话的超时时间,用来限制客户端发起一个会话连接到接收ZooKeeper 服务端应答的时间。
sessionTimeoutMs 作用在服务端,而connectionTimeoutMs 作用在客户端。

策略名称描述
ExponentialBackoffRetry重试一组次数,重试之间的睡眠时间增加
RetryNTimes重试最大次数
RetryOneTime只重试一次
RetryUntilElapsed在给定的时间结束之前重试

2、创建节点、获取节点信息、删除节点等等

@Slf4j
public class CuratorBaseOperations extends CuratorStandaloneBase {
    private static CuratorFramework curatorFramework;
    @Before
    public void init() {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(5000, 30);
        curatorFramework = CuratorFrameworkFactory.builder().connectString(getConnectStr())
                .retryPolicy(retryPolicy)
                .sessionTimeoutMs(5000)
                .connectionTimeoutMs(5000)
                .canBeReadOnly(true)
                .build();
        curatorFramework.getConnectionStateListenable().addListener((client, newState) -> {
            if (newState == ConnectionState.CONNECTED) {
                log.info("连接成功!");
            }
        });
        log.info("连接中......");
        curatorFramework.start();
    }
    
    // 递归创建子节点
    @Test
    public void testCreateWithParent() throws Exception {
        String pathWithParent = "/node-parent/sub-node-1";
        String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent);
        log.info("curator create node :{}  successfully.", path);
    }

    // protection 模式,防止由于异常原因,导致僵尸节点
    @Test
    public void testCreate() throws Exception {
        String forPath = curatorFramework
                .create()
                .withProtection()
                .withMode(CreateMode.EPHEMERAL_SEQUENTIAL).
                        forPath("/curator-node", "some-data".getBytes());
        log.info("curator create node :{}  successfully.", forPath);
    }

    @Test // 获取节点数据
    public void testGetData() throws Exception {
        byte[] bytes = curatorFramework.getData().forPath("/curator-node");
        log.info("get data from  node :{}  successfully.", new String(bytes));
    }


    @Test // 更新节点并获取节点数据
    public void testSetData() throws Exception {
        curatorFramework.setData().forPath("/curator-node", "changed!".getBytes());
        byte[] bytes = curatorFramework.getData().forPath("/curator-node");
        log.info("get data from  node /curator-node :{}  successfully.", new String(bytes));
    }

    @Test // 删除节点数据
    public void testDelete() throws Exception {
        String pathWithParent = "/node-parent";
        curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(pathWithParent);
    }

    @Test // 获取子节点
    public void testListChildren() throws Exception {
        String pathWithParent = "/discovery/example";
        List<String> strings = curatorFramework.getChildren().forPath(pathWithParent);
        strings.forEach(System.out::println);
    }

    @Test 
    public void testThreadPool() throws Exception {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        String ZK_NODE = "/zk-node";
        curatorFramework.getData().inBackground((client, event) -> {
            log.info(" background: {}", event);
        }, executorService).forPath(ZK_NODE); 
    }
}

guaranteed:该函数的功能如字面意思一样,主要起到一个保障删除成功的作用,其底层工作方式是:只要该客户端的会话有效,就会在后台持续发起删除请求,直到该数据节点在ZooKeeper 服务端被删除。

deletingChildrenIfNeeded:指定了该函数后,系统在删除该数据节点的时候会以递归的方式直接删除其子节点,以及子节点的子节点。

Curator 引入了BackgroundCallback 接口,用来处理服务器端返回来的信息,这个处理过程是在异步线程中调用,默认在 EventThread 中调用,也可以自定义线程池。
主要参数为 client 客户端, 和 服务端事件 event inBackground 异步处理默认在EventThread中执行。

3、Curator 监听器

public interface CuratorListener
{
    /**
     * Called when a background task has completed or a watch has triggered
     *
     * @param client client
     * @param event the event
     * @throws Exception any errors
     */
    public void         eventReceived(CuratorFramework client, CuratorEvent event) throws Exception;
}

针对 background 通知和错误通知。使用此监听器之后,调用inBackground 方法会异步获得监听。

代码示例

    // 使用 watcher 机制, 为一次性监听
    @Test
    public void testWatcher() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();
        String path="/test";
        createIfNeed(path);
        byte[] bytes = curatorFramework.getData().usingWatcher(new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getType() == Event.EventType.NodeDataChanged){
                    log.info("node {} data changed!",event.getPath());
                }
            }
        }).forPath(path);
        log.info(" original data: {}",new String(bytes));
        log.info("start to change data.");
        curatorFramework.setData().forPath(path, "test".getBytes());
        log.info("start to change data again! ");
        curatorFramework.setData().forPath(path, "test".getBytes());
    }
    
    @Test
    public void testCuratorListener() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();
        curatorFramework.getCuratorListenable().addListener((CuratorFramework client, CuratorEvent event)->{
            log.info(" things changed1: event: {},  {} ",event.getType().name(),event);
        });

        String path = "/test";
        createIfNeed(path);
        log.info("start to change data for path :{}" ,path);
        curatorFramework.setData().inBackground().forPath(path,"xxx1".getBytes());
        log.info("start to change data for path :{} again" ,path);
        curatorFramework.setData().inBackground().forPath(path,"xxx2".getBytes());
        curatorFramework.create().inBackground().forPath("/test456","test456".getBytes());
    }

4、Curator Caches:

Curator 引入了 Cache 来实现对 Zookeeper 服务端事件监听,Cache 事件监听可以理解为一个本地缓存视图与远程 Zookeeper 视图的对比过程。Cache 提供了反复注册的功能。Cache 分为两类注册类型:节点监听和子节点监听。

4.1 node cache:NodeCache 对某一个节点进行监听,可以通过注册监听器来实现,对当前节点数据变化的处理
   public static final String NODE_CACHE="/node-cache";

    @Test
    public void testNodeCacheTest() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();
        createIfNeed(NODE_CACHE);
        NodeCache nodeCache = new NodeCache(curatorFramework, NODE_CACHE);
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                log.info("{} path nodeChanged: ",NODE_CACHE);
                printNodeData();
            }
        });
        nodeCache.start();
    }
    public void printNodeData() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();
        byte[] bytes = curatorFramework.getData().forPath(NODE_CACHE);
        log.info("data: {}",new String(bytes));
    }
4.2 path cache: PathChildrenCache 会对子节点进行监听,但是不会对二级子节点进行监听。可以通过注册监听器来实现,对当前节点的子节点数据变化的处理。
    public static final String PATH="/path-cache";

    @Test
    public void testPathCache() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();
        createIfNeed(PATH);
        PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework, PATH, true);
        pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                log.info("event:  {}",event);
            }
        });
        // 如果设置为true则在首次启动时就会缓存节点内容到Cache中
        pathChildrenCache.start(true);
    }
4.3 tree cache:TreeCache 使用一个内部类TreeNode来维护这个一个树结构。并将这个树结构与ZK节点进行了映射。所以TreeCache 可以监听当前节点下所有节点的事件。

可以通过注册监听器来实现,对当前节点的子节点,及递归子节点数据变化的处理。

    public static final String TREE_CACHE="/tree-path";

    @Test
    public void testTreeCache() throws Exception {
        CuratorFramework curatorFramework = getCuratorFramework();
        createIfNeed(TREE_CACHE);
        TreeCache treeCache = new TreeCache(curatorFramework, TREE_CACHE);
        treeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
                log.info(" tree cache: {}",event);

                Map<String, ChildData> currentChildren = treeCache.getCurrentChildren(TREE_CACHE);
                log.info("currentChildren: {}",currentChildren);
            }
        });
        treeCache.start();
    }

应用一:服务器动态上下线

在这里插入图片描述

@Slf4j
public class DistributeServer {

    private String connectString = "192.168.56.101:2182,192.168.56.101:2183,192.168.56.101:2184";
    private int sessionTimeout = 2000;
    private static ZooKeeper zk;
    final static CountDownLatch countDownLatch = new CountDownLatch(1);
    public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
        DistributeServer server = new DistributeServer();
        // 1 获取zk连接
        server.getConnect();

		// 发生连接异常时 ,添加该段代码
        if (ZooKeeper.States.CONNECTING.equals(zk.getState())){
            System.out.println("连接中");
            countDownLatch.await();
        }
        // 2 注册服务器到zk集群
        server.regist("127.0.0.1");

        // 3 启动业务逻辑(睡觉)
        server.business();
    }

    private void business() throws InterruptedException {
        Thread.sleep(Long.MAX_VALUE);
    }

    private void regist(String hostname) throws KeeperException, InterruptedException {
    // 服务动态上下线,在zookeeper创建临时节点 CreateMode.EPHEMERAL_SEQUENTIAL
        String create = zk.create("/servers/"+hostname, hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

        System.out.println(hostname +" is online") ;
    }

    private void getConnect() throws IOException, InterruptedException {

        zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                // 可以启动监听者
                if (Watcher.Event.KeeperState.SyncConnected.equals(watchedEvent.getState())){
                    System.out.println("连接成功" + watchedEvent);
                    countDownLatch.countDown();
                }
            }
        });
    }
}

在连接zookeeper集群的过程中可能会出现这样的异常

Exception in thread “main” org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = ConnectionLoss for /servers/127.0.0.2
at org.apache.zookeeper.KeeperException.create(KeeperException.java:102)
at org.apache.zookeeper.KeeperException.create(KeeperException.java:54)
at org.apache.zookeeper.ZooKeeper.create(ZooKeeper.java:1538)
at com.atguigu.case1.DistributeServer.regist(DistributeServer.java:34)
at com.atguigu.case1.DistributeServer.main(DistributeServer.java:23)

客户端获取zookeeper集群节点信息

public class DistributeClient {

    private static String connectString = "192.168.56.101:2182,192.168.56.101:2183,192.168.56.101:2184";
    private int sessionTimeout = 2000000;
    private ZooKeeper zk;

    public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
        DistributeClient client = new DistributeClient();

        // 1 获取zk连接
        client.getConnect();

        // 2 监听/servers下面子节点的增加和删除
        client.getServerList();

        // 3 业务逻辑(睡觉)
        client.business();

    }

    private void business() throws InterruptedException {
        Thread.sleep(Long.MAX_VALUE);
    }

    private void getServerList() throws KeeperException, InterruptedException {
        List<String> children = zk.getChildren("/servers", true);

        ArrayList<String> servers = new ArrayList<>();

        for (String child : children) {

            byte[] data = zk.getData("/servers/" + child, false, null);

            servers.add(new String(data));
        }

        // 打印
        System.out.println(servers);
    }

    private void getConnect() throws IOException {
        zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {

                try {
                    getServerList();
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

获取集群应用配置信息

@Slf4j
public class ReconfigApp {

    private final static String connectString = "192.168.109.200:2181,192.168.109.200:2182,192.168.109.200:2183,192.168.109.200:2184";

    private static int SESSION_TIMEOUT = 5 * 1000;

    private static CountDownLatch countDownLatch = new CountDownLatch(1);

    private static ZooKeeper zookeeper = null;

    private static Watcher watcher = new Watcher() {
        @Override
        public void process(WatchedEvent event) {
            if (event.getType() == Event.EventType.None
                    && event.getState() == Event.KeeperState.SyncConnected) {
                countDownLatch.countDown();
                log.info(" 连接建立");
                // start to watch config
                try {
                    log.info(" 开始监听:{}", ZooDefs.CONFIG_NODE);
                    zookeeper.getConfig(true, null);
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else if (event.getPath() != null && event.getPath().equals(ZooDefs.CONFIG_NODE)) {
                try {
                    byte[] config = zookeeper.getConfig(this, null);
                    String clientConfigStr = ConfigUtils.getClientConfigStr(new String(config));
                    log.info(" 配置发生变更: {}", clientConfigStr);
                    zookeeper.updateServerList(clientConfigStr.split(" ")[1]);
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }
    };

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        zookeeper = new ZooKeeper(connectString, SESSION_TIMEOUT, watcher);
        countDownLatch.await();
        Scanner scanner = new Scanner(System.in);
        while (true) {
            byte[] data = zookeeper.getData("/zookeeper/config", true, null);
            scanner.next();
            log.info("DATA: {}", new String(data));
        }
    }
}

应用二 :ZooKeeper 分布式锁案例

什么叫做分布式锁呢?
比如说"进程 1"在使用该资源的时候,会先去获得锁,"进程 1"获得锁以后会对该资源保持独占,这样其他进程就无法访问该资源,"进程 1"用完该资源以后就将锁释放掉,让其他进程来获得锁,那么通过这个锁机制,我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作分布式锁。
在这里插入图片描述

public class DistributedLock {

    private String connectString = "192.168.56.101:2182,192.168.56.101:2183,192.168.56.101:2184";
    private final int sessionTimeout = 2000;
    private final ZooKeeper zk;

    private CountDownLatch connectLatch = new CountDownLatch(1);
    private CountDownLatch waitLatch = new CountDownLatch(1);

    private String waitPath;
    private String currentMode;

    public DistributedLock() throws IOException, InterruptedException, KeeperException {

        // 获取连接
        zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                // connectLatch  如果连接上zk  可以释放
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected){
                    connectLatch.countDown();
                }

                // waitLatch  需要释放
                if (watchedEvent.getType()== Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)){
                    waitLatch.countDown();
                }
            }
        });

        // 等待zk正常连接后,往下走程序
        connectLatch.await();

        // 判断根节点/locks是否存在
        Stat stat = zk.exists("/locks", false);

        if (stat == null) {
            // 创建一下根节点
            zk.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
    }

    // 对zk加锁
    public void zklock() {
        // 创建对应的临时带序号节点
        try {
            currentMode = zk.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

            // wait一小会, 让结果更清晰一些
            Thread.sleep(10);

            // 判断创建的节点是否是最小的序号节点,如果是获取到锁;如果不是,监听他序号前一个节点

            List<String> children = zk.getChildren("/locks", false);

            // 如果children 只有一个值,那就直接获取锁; 如果有多个节点,需要判断,谁最小
            if (children.size() == 1) {
                return;
            } else {
                Collections.sort(children);

                // 获取节点名称 seq-00000000
                String thisNode = currentMode.substring("/locks/".length());
                // 通过seq-00000000获取该节点在children集合的位置
                int index = children.indexOf(thisNode);

                // 判断
                if (index == -1) {
                    System.out.println("数据异常");
                } else if (index == 0) {
                    // 就一个节点,可以获取锁了
                    return;
                } else {
                    // 需要监听  他前一个节点变化
                    waitPath = "/locks/" + children.get(index - 1);
                    zk.getData(waitPath,true,new Stat());
                    // 等待监听
                    waitLatch.await();
                    return;
                }
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

    // 解锁
    public void unZkLock() {

        // 删除节点
        try {
            zk.delete(this.currentMode,-1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }

    }

}
public class DistributedLockTest {

    public static void main(String[] args) throws InterruptedException, IOException, KeeperException {

       final  DistributedLock lock1 = new DistributedLock();

        final  DistributedLock lock2 = new DistributedLock();

       new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   lock1.zklock();
                   System.out.println("线程1 启动,获取到锁");
                   Thread.sleep(5 * 1000);

                   lock1.unZkLock();
                   System.out.println("线程1 释放锁");
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    lock2.zklock();
                    System.out.println("线程2 启动,获取到锁");
                    Thread.sleep(5 * 1000);

                    lock2.unZkLock();
                    System.out.println("线程2 释放锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}

curator 框架实现分布式锁代码示例

public class CuratorLockTest {

    public static void main(String[] args) {

        // 创建分布式锁1
        InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework(), "/locks");

        // 创建分布式锁2
        InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework(), "/locks");

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock1.acquire();
                    System.out.println("线程1 获取到锁");

                    lock1.acquire();
                    System.out.println("线程1 再次获取到锁");

                    Thread.sleep(5 * 1000);

                    lock1.release();
                    System.out.println("线程1 释放锁");

                    lock1.release();
                    System.out.println("线程1  再次释放锁");

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock2.acquire();
                    System.out.println("线程2 获取到锁");

                    lock2.acquire();
                    System.out.println("线程2 再次获取到锁");

                    Thread.sleep(5 * 1000);

                    lock2.release();
                    System.out.println("线程2 释放锁");

                    lock2.release();
                    System.out.println("线程2  再次释放锁");

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private static CuratorFramework getCuratorFramework() {

        ExponentialBackoffRetry policy = new ExponentialBackoffRetry(30000, 3);

        CuratorFramework client = CuratorFrameworkFactory.builder().connectString("192.168.56.101:2182,192.168.56.101:2183,192.168.56.101:2184")
                .connectionTimeoutMs(2000)
                .sessionTimeoutMs(2000)
                .retryPolicy(policy).build();

        // 启动客户端
        client.start();
        
        System.out.println("zookeeper 启动成功");
        return client;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值