docker安裝Zookeeper,以及Zookeeper工具類編寫和分佈式鎖的測試

一、docker安裝zookeeper

1.拉取鏡像。默認是最新版本

docker pull zookeeper 

2.運行docker鏡像

docker run --privileged=true -d --name zookeeper --publish 2181:2181  -d zookeeper:latest

3.查看容器

docker ps 

在這裏插入圖片描述

4.(1)idea zookeeper 查看工具
在這裏插入圖片描述
(2)zkclient

二.Zookeeper工具類編寫(引用curator連接方式)

curator的詳解,請參考:https://www.jianshu.com/p/70151fc0ef5d
1.工具類

package com.utils;


import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable;
import org.apache.curator.framework.api.BackgroundPathAndBytesable;
import org.apache.curator.framework.api.BackgroundPathable;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;


/**
 * Date: 2018年11月6日
 *
 * @author lk
 */
public class ZkClient {

    private String zkAddr;
    private int timeOut;
    private String authSchema;
    private String authInfo;
    private CuratorFramework client;



    public ZkClient(String zkAddr, int timeOut, String namespace) throws Exception{

        this(zkAddr,timeOut,namespace,null);

    }

    /**
     * 獲取zk 連接客戶端
     * @param zkAddr zk地址 ip:port,ip:port,ip:port
     * @param timeOut 連接超時ms
     * @param namespace 所有的操作都是在 /namespace 下的節點操作
     * @param acl  Access Control List(訪問控制列表)。Znode被創建時帶有一個ACL列表<br>
     * acl 主要由三個維度:schema,id,permision 控制節點權限 <br>
     * eg:<br>
     * Id id = new Id("digest", DigestAuthenticationProvider.generateDigest("username:password"));<br>
     * ACL acl = new ACL(ZooDefs.Perms.ALL, id); <br>
     * <br>
     * 維度 schema: <br>
     * 1:digest 用戶名+密碼驗證 它對應的維度id=username:BASE64(SHA1(password))<br>
     * 2:host 客戶端主機名hostname驗證 <br>
     * 3:ip 它對應的維度id=客戶機的IP地址,設置的時候可以設置一個ip段,比如ip:192.168.1.0/16, 表示匹配前16個bit的IP段<br>
     * 4:auth 使用sessionID驗證 <br>
     * 5:world 無驗證,默認是無任何權限  它下面只有一個id, 叫anyone  <br>
     * 6:super: 在這種scheme情況下,對應的id擁有超級權限,可以做任何事情(cdrwa)  <br>
     * 7:sasl: sasl的對應的id,是一個通過了kerberos認證的用戶id  <br>
     * <br>
     * 維度:permision <br>
     * ZooDefs.Perms.READ 讀權限<br>
     * ZooDefs.Perms.WRITE 寫權限<br>
     * ZooDefs.Perms.CREATE 創建節點權限<br>
     * ZooDefs.Perms.DELETE 刪除節點權限<br>
     * ZooDefs.Perms.ADMIN 能設置權限<br>
     * ZooDefs.Perms.ALL 所有權限<br>
     * ALL = READ | WRITE | CREATE | DELETE | ADMIN<br>
     * @throws Exception
     */

    public ZkClient(String zkAddr, int timeOut, String namespace,ACL acl) throws Exception{
        this.zkAddr = zkAddr;
        if (timeOut > 0) {
            this.timeOut = timeOut;
        }
        if (null != acl) {
            this.authSchema = acl.getId().getScheme();
            this.authInfo = acl.getId().getId();
        }
        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory
                .builder().connectString(this.zkAddr).namespace(StringUtils.isEmpty(namespace)?"":namespace)
                .connectionTimeoutMs(this.timeOut)
                .retryPolicy(new RetryNTimes(5, 10));
        if ((!StringUtils.isEmpty(this.authSchema))
                && (!StringUtils.isEmpty(this.authInfo))) {
            builder.authorization(this.authSchema, this.authInfo.getBytes());
        }
        this.client = builder.build();
        this.client.start();
        this.client.blockUntilConnected(5, TimeUnit.SECONDS);

    }



    /**
     * 創建一個所有權限節點即schema:world;id:annyone;permision:ZooDefs.Perms.ALL
     * @param nodePath 創建的結點路徑
     * @param data 節點數據
     * @param createMode 節點模式
     * @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
     * @throws Exception
     */
    public void createNode(String nodePath, String data, CreateMode createMode,boolean recursion)
            throws Exception {

        createNode(nodePath, ZooDefs.Ids.OPEN_ACL_UNSAFE, data, createMode,recursion);
    }


    /**
     * 創建節點
     * @param nodePath 創建節點的路徑
     * @param acls 節點控制權限列表
     * @param data 節點存放的數據
     * @param createMode 創建節點的模式
     * @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
     * 節點模式CreateMode<br>
     * 1:CreateMode.EPHEMERAL 創建臨時節點;該節點在客戶端掉線的時候被刪除<br>
     * 2:CreateMode.EPHEMERAL_SEQUENTIAL  臨時自動編號節點,一旦創建這個節點的客戶端與服務器端口也就是session 超時,這種節點會被自動刪除,並且根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功創建的目錄節點(可做分佈式鎖)<br>
     * 3:CreateMode.PERSISTENT 持久化目錄節點,存儲的數據不會丟失。<br>
     * 4:CreateMode.PERSISTENT_SEQUENTIAL  順序自動編號的持久化目錄節點,存儲的數據不會丟失,並且根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功創建的目錄節點名<br>
     * @throws Exception
     */
    public void createNode(String nodePath, List<ACL> acls, String data,
                           CreateMode createMode,boolean recursion) throws Exception {
        byte[] bytes = null;
        if (!StringUtils.isEmpty(data)) {
            bytes = data.getBytes("UTF-8");
        }
        createNode(nodePath, acls, bytes, createMode,recursion);
    }

    /**
     * @param nodePath 創建節點的路徑
     * @param acls 節點控制權限列表
     * @param data 節點存放的數據
     * @param createMode 創建節點的模式
     * @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
     * 節點模式CreateMode<br>
     * 1:CreateMode.EPHEMERAL 創建臨時節點;該節點在客戶端掉線的時候被刪除<br>
     * 2:CreateMode.EPHEMERAL_SEQUENTIAL  臨時自動編號節點,一旦創建這個節點的客戶端與服務器端口也就是session 超時,這種節點會被自動刪除,並且根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功創建的目錄節點(可做分佈式鎖)<br>
     * 3:CreateMode.PERSISTENT 持久化目錄節點,存儲的數據不會丟失。<br>
     * 4:CreateMode.PERSISTENT_SEQUENTIAL  順序自動編號的持久化目錄節點,存儲的數據不會丟失,並且根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功創建的目錄節點名<br>
     * @throws Exception
     */
    public void createNode(String nodePath, List<ACL> acls, byte[] data,
                           CreateMode createMode,boolean recursion) throws Exception {
        if(recursion){
            ((BackgroundPathAndBytesable<?>) ((ACLBackgroundPathAndBytesable<?>) this.client
                    .create().creatingParentsIfNeeded().withMode(createMode))
                    .withACL(acls)).forPath(nodePath, data);
        }
        else{
            ((BackgroundPathAndBytesable<?>) ((ACLBackgroundPathAndBytesable<?>) this.client
                    .create().withMode(createMode))
                    .withACL(acls)).forPath(nodePath, data);
        }
    }

    /**
     * 創建一個所有權限的永久節點
     * @param nodePath
     * @param data
     * @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
     * @throws Exception
     */
    public void createPersitentNode(String nodePath, String data,boolean recursion) throws Exception {

        createNode(nodePath, data, CreateMode.PERSISTENT,recursion);
    }

    /**
     * 創建一個所有權限的零時節點
     * @param nodePath
     * @param data
     * @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
     * @throws Exception
     */
    public void createEphemeralNode(String nodePath, String data,boolean recursion) throws Exception {

        createNode(nodePath, data, CreateMode.EPHEMERAL,recursion);
    }

    /**
     * 創建一個帶權限的永久節點
     * @param nodePath
     * @param data
     * @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
     * @throws Exception
     */
    public void createPersitentNodeWithAcl(String nodePath, String data,List<ACL> acls,boolean recursion) throws Exception {

        createNode(nodePath, acls, data, CreateMode.PERSISTENT,recursion);
    }

    /**
     * 創建一個帶權限的零時節點
     * @param nodePath
     * @param data
     * @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
     * @throws Exception
     */
    public void createEphemeralNodeAcl(String nodePath, String data,List<ACL> acls,boolean recursion) throws Exception {

        createNode(nodePath, acls, data, CreateMode.EPHEMERAL,recursion);
    }




    /**
     * 創建序列節點且當父節點不存在時創建父節點
     * @param nodePath
     * @param acls 可參考:ZooDefs.Ids
     * @param createMode
     * @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
     * @throws Exception
     */
    public void createSeqNode(String nodePath,List<ACL> acls,CreateMode createMode,boolean recursion) throws Exception {
        if(recursion){
            ((BackgroundPathAndBytesable<?>) ((ACLBackgroundPathAndBytesable<?>) this.client
                    .create().creatingParentsIfNeeded()
                    .withMode(createMode))
                    .withACL(acls)).forPath(nodePath);
        }
        else{
            ((BackgroundPathAndBytesable<?>) ((ACLBackgroundPathAndBytesable<?>) this.client
                    .create()
                    .withMode(createMode))
                    .withACL(acls)).forPath(nodePath);
        }
    }

    /**
     * 存在返回 節點stat 信息;否則返回null
     * @param path
     * @return
     * @throws Exception
     */
    public Stat exists(String path) throws Exception {

        return  this.client.checkExists().forPath(path);
    }

    /**
     * 判斷節點是否存在,存在則註冊節點監視器
     * @param path
     * @param watcher
     * @return
     */
    public boolean exists(String path, Watcher watcher) throws Exception {

        if (null != watcher) {
            return null != ((BackgroundPathable<?>) this.client.checkExists().usingWatcher(watcher)).forPath(path);
        }
        return null != this.client.checkExists().forPath(path);
    }

    /**
     * 判斷是否處於連接狀態
     * @return
     */
    public boolean isConnected() {

        if ((null == this.client)
                || (!CuratorFrameworkState.STARTED.equals(this.client
                .getState()))) {
            return false;
        }
        return true;
    }

    public void retryConnection() {
        this.client.start();
    }

    /**
     * 獲取連接客戶端
     * @return
     */
    public CuratorFramework getInnerClient(){

        return this.client;

    }

    /**
     * 關閉連接
     */
    public void quit() {

        if ((null != this.client)
                && (CuratorFrameworkState.STARTED
                .equals(this.client.getState()))) {
            this.client.close();
        }
    }


    /**
     * 刪除節點
     * @param path
     * @param deleChildren
     * @throws Exception

     */
    public void deleteNode(String path,boolean deleChildren) throws Exception {

        if(deleChildren){
            this.client.delete().guaranteed().deletingChildrenIfNeeded()
                    .forPath(path);
        }
        else{
            this.client.delete().forPath(path);
        }
    }

    /**
     * 設置節點數據
     * @param nodePath
     * @param data
     * @throws Exception
     */
    public void setNodeData(String nodePath, String data) throws Exception {

        byte[] bytes = null;
        if (!StringUtils.isEmpty(data)) {
            bytes = data.getBytes("UTF-8");
        }
        setNodeData(nodePath, bytes);
    }

    /**
     * 設置節點數據
     * @param nodePath
     * @param data
     * @throws Exception
     */
    public void setNodeData(String nodePath, byte[] data) throws Exception {
        this.client.setData().forPath(nodePath, data);
    }

    public String getNodeData(String nodePath, boolean watch) throws Exception {
        byte[] data;
        if (watch) {
            data = (byte[]) ((BackgroundPathable<?>) this.client.getData()
                    .watched()).forPath(nodePath);
        } else {
            data = (byte[]) this.client.getData().forPath(nodePath);
        }
        if ((null == data) || (data.length <= 0)) {
            return null;
        }
        return new String(data, "UTF-8");
    }

    public String getNodeData(String nodePath) throws Exception {
        return getNodeData(nodePath, false);
    }

    public String getNodeData(String nodePath, Watcher watcher)
            throws Exception {
        byte[] data = getNodeBytes(nodePath, watcher);
        return new String(data, "UTF-8");
    }

    public byte[] getNodeBytes(String nodePath, Watcher watcher)
            throws Exception {
        byte[] bytes = null;
        if (null != watcher) {
            bytes = (byte[]) ((BackgroundPathable<?>) this.client.getData()
                    .usingWatcher(watcher)).forPath(nodePath);
        } else {
            bytes = (byte[]) this.client.getData().forPath(nodePath);
        }
        return bytes;
    }

    public byte[] getNodeBytes(String nodePath) throws Exception {
        return getNodeBytes(nodePath, null);
    }


    @SuppressWarnings("unchecked")
    public List<String> getChildren(String nodePath, Watcher watcher)
            throws Exception {
        return (List<String>) ((BackgroundPathable<?>) this.client
                .getChildren().usingWatcher(watcher)).forPath(nodePath);
    }

    public List<String> getChildren(String path) throws Exception {
        return (List<String>) this.client.getChildren().forPath(path);
    }

    @SuppressWarnings("unchecked")
    public List<String> getChildren(String path, boolean watcher)
            throws Exception {
        if (watcher) {
            return (List<String>) ((BackgroundPathable<?>) this.client
                    .getChildren().watched()).forPath(path);
        }
        return (List<String>) this.client.getChildren().forPath(path);
    }


    public ZkClient addAuth(String authSchema, String authInfo)
            throws Exception {
        synchronized (ZkClient.class) {
            this.client.getZookeeperClient().getZooKeeper()
                    .addAuthInfo(authSchema, authInfo.getBytes());
        }
        return this;
    }

    /**
     * 分佈式鎖
     * @param lockPath
     * @return
     */
    public InterProcessLock getInterProcessLock(String lockPath) {
        return new InterProcessMutex(this.client, lockPath);
    }

}
    /***
     * 監聽節點變化
     */
    public PathChildrenCache watchNode(String path,PathChildrenCacheListener listener) throws Exception {
        //創建監聽
        PathChildrenCache pathChildrenCache=new PathChildrenCache(this.client,path,true);
        pathChildrenCache.getListenable().addListener(listener);
        pathChildrenCache.start();
        return pathChildrenCache;
    }

2.pom文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>mavenzookeeper</groupId>
  <artifactId>mavenzookeeper</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>mavenzookeeper</name>
  <packaging>jar</packaging>

  <!-- 繼承父包 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.1.3.RELEASE</version>
    <relativePath></relativePath>
  </parent>

  <!-- spring-boot的web啓動的jar包 -->
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--jpa的jar包 ,操作數據庫的,類似hibernate-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!--mysql驅動-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!--thymeleaf模板jar,是很不錯的html數據傳遞取值,類似jsp的jstl-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
    <dependency>
      <groupId>org.apache.zookeeper</groupId>
      <artifactId>zookeeper</artifactId>
      <version>3.4.6</version>
      <type>pom</type>
    </dependency>
    <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-recipes</artifactId>
      <version>2.12.0</version>
    </dependency>
      <dependency>
          <groupId>org.apache.curator</groupId>
          <artifactId>curator-framework</artifactId>
          <version>2.12.0</version>
      </dependency>



  </dependencies>

  <!--maven的插件-->
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>

    <!-- 配置java版本 不配置的話默認父類配置的是1.6-->
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <configuration>
            <source>1.8</source>
            <target>1.8</target>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

3.監聽最後調用和分佈式鎖測試

package com.controller;

import com.utils.ZkClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/zkController")
public class getZK {
    @Autowired
    private ZkClientService zkClient;
    @RequestMapping("/getZK")
    @ResponseBody
    public String getZK(){
        try {
            List<String> children = zkClient.getChildren("/");
            System.out.println(children.stream().toString());
             //分佈式鎖測試
            for(int i=0;i<50;i++){
                new Thread(()->{
                    InterProcessLock lock = null;
                    try{
                        lock = zkClient.getInterProcessLock("/distributeLock");
                        System.out.println(Thread.currentThread().getName()+"申請鎖");
                        lock.acquire();
                        System.out.println(Thread.currentThread().getName()+"持有鎖");
                        Thread.sleep(500);
                    }
                    catch(Exception e){
                        e.printStackTrace();
                    }
                    finally{
                        if(null != lock){
                            try {
                                lock.release();
                                System.out.println(Thread.currentThread().getName()+"釋放有鎖");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }
            zkClient.watchNode("/",(client,event)->{//對節點進行監聽
                String data=new String(event.getData().getData());
                switch (event.getType()){
                    case CHILD_ADDED: {
                        System.out.println("Node added: " + data);
                        break;
                    }
                    case CHILD_UPDATED: {
                        System.out.println("Node changed: " + data);
                        break;
                    }
                    case CHILD_REMOVED: {
                        System.out.println("Node removed: " + data);
                        break;
                    }

                }
            });
            return "ok";
        }catch (Exception e){
            e.printStackTrace();
            return "error";
        }

    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章