Zookeeper中ZkClient及分布式锁的使用

1.zookeeper安装

直接查看:原文(windows操作系统下 springboot 集成 kafka)windows系统

2.zookeeper

启动zkCli.cmd操作

2.1   使用 ls 命令来查看当前 ZooKeeper 中所包含的内容

2.2 创建一个新的 znode ,使用 create /username myUserName

注意:创建子节点必须保证父节点存在

步骤:

1、 create /url 1

2、reate /url/com.eebbk.vtraining 2

 3、create /url/com.eebbk.vtraining/bannernew https://actcdn.eebbk.com/test/act-msfd/portal/c_banner-test_f5c1f6b.html(如果没有12两步直接执行会提示Node does not exist: /url/com.eebbk.vtraining/bannernew)

2.3  get /username 查看数据

2.4 delete /username

3.java简单使用zk(转)

3.1 引用依赖

 <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.5.5</version>
        </dependency>

3.2 新建ZooKeeperProSync 

package com.test.mybatisplus.zk;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.CountDownLatch;

/**
 * @Description zookeeper使用
 * @project mybatisplus
 * @author:hf
 * @date:
 * @company:
 */
public class ZooKeeperProSync implements Watcher {


    private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
    private static ZooKeeper zk = null;
    private static Stat stat = new Stat();

    public static void main(String[] args) throws Exception {
        //zookeeper配置数据存放路径
        String path = "/username";
        //连接zookeeper并且注册一个默认的监听器
        zk = new ZooKeeper("127.0.0.1:2181", 5000, //
                new ZooKeeperProSync());
        //等待zk连接成功的通知
        connectedSemaphore.await();
        //获取path目录节点的配置数据,并注册默认的监听器
        System.out.println(new String(zk.getData(path, true, stat)));

        Thread.sleep(Integer.MAX_VALUE);
    }
   @Override
    public void process(WatchedEvent event) {
        if (Event.KeeperState.SyncConnected == event.getState()) {  //zk连接成功通知事件
            if (Event.EventType.None == event.getType() && null == event.getPath()) {
                connectedSemaphore.countDown();
            } else if (event.getType() == Event.EventType.NodeDataChanged) {  //zk目录节点数据变化通知事件
                try {
                    System.out.println("配置已修改,新值为:" + new String(zk.getData(event.getPath(), true, stat)));
                } catch (Exception e) {
                }
            }
        }
    }

}

3.3 启动之后,输出username的值

3.4 在启动的zkCli.cmd 中使用命令修改 username的数据

4.ZkClient 使用

4.1 引入依赖

  <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.5.5</version>
        </dependency>
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.10</version>
        </dependency>

4.2 创建ZkClientUtils工具类

package com.test.mybatisplus.util;


import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNodeExistsException;
import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.zookeeper.CreateMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CountDownLatch;

@Component
public class ZkClientUtils
{

    private static final Log LOG = LogFactory.getLog(ZkClientUtils.class);
    // 如果是集群直接“,”隔开 CONN_STRING = "127.0.0.1:2181,127.0.0.1:2181,127.0.0.1:2181"
	//private static String CONN_STRING ="127.0.0.1:2181";

	private static String CONN_STRING ;

	// @Component注入之后 没用statics修饰直接@Value可取到值
	@Value("${zookeeper.node}")
	private  String CONN_STRING_TEST ;


	// @Component注入之后 用statics修饰 start
	private static ZkClient zkClient = null;

	@Value("${zookeeper.node}")
	public  void setConnString(String connString) {
		CONN_STRING = connString;
	}
    //end

    private static final String LOCK_NODE = "/distributed_lock";
    private static final String CHILDREN_NODE = "/task_";


	private ZkClientUtils(){

	}

	public synchronized static ZkClient getInstance(){
		if(zkClient==null){
			zkClient = new ZkClient(CONN_STRING, 30000, 30000,
					new BytesPushThroughSerializer());
		}
		return zkClient;
	}
	
	public static String readData(String path)
	{			

		byte[] readBytes = getInstance().readData(path);
		
		return new String(readBytes);
	}

	public static String create(String path,String data)
	{

		String result = getInstance().create(path,data.getBytes(), CreateMode.EPHEMERAL);
		//一但创建node即监听情况
		subscribeDataChanges(path);
		return result;
	}

	public static boolean delete(String path)
	{

		boolean result = getInstance().delete(path);

		return result;
	}

	public static void write(String path,String data)
	{
		 getInstance().writeData(path,data.getBytes());

	}

	/**
	 * 监控当前节点情况 path
	 * @param path
	 */
	public static void subscribeDataChanges(String path)
	{
		getInstance().subscribeDataChanges(path, new IZkDataListener() {

			@Override
			public void handleDataDeleted(String dataPath) throws Exception {
				System.out.println("subscribeDataChanges    handleDataDeleted");
				System.out.println("节点被删除");
			}

			@Override
			public void handleDataChange(String dataPath, Object data) throws Exception {
				System.out.println("subscribeDataChanges    handleDataChange");
				System.out.println("变更的节点为:" + dataPath + ", 变更内容为:" + data.toString());
			}
		});

	}

    //    首先zookeeper中我们可以创建一个/distributed_lock持久化节点
    //    然后再在/distributed_lock节点下创建自己的临时顺序节点,比如:/distributed_lock/task_00000000008
    //    获取所有的/distributed_lock下的所有子节点,并排序
    //    判读自己创建的节点是否最小值(第一位)
    //    如果是,则获取得到锁,执行自己的业务逻辑,最后删除这个临时节点。
    //    如果不是最小值,则需要监听自己创建节点前一位节点的数据变化,并阻塞。
    //    当前一位节点被删除时,我们需要通过递归来判断自己创建的节点是否在是最小的,如果是则执行5);如果不是则执行6)(就是递归循环的判断)
    /**
     * 分布式锁使用
     * @return
     */
    public static String getLock() {
        try {
            // 判断是否存在父节点 --创建永久节点才能创建子节点
            if(!getInstance().exists(LOCK_NODE)){
                getInstance().create(LOCK_NODE, "分布式锁节点".getBytes(), CreateMode.PERSISTENT);
            }
            // 1。在Zookeeper指定节点下创建临时顺序节点 --当失去连接或者会话过期就会自动删除
            String lockName = getInstance().createEphemeralSequential(LOCK_NODE + CHILDREN_NODE, "children".getBytes());
            // 尝试获取锁
            acquireLock(lockName);
            return lockName;
        } catch(Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 获取锁
     * @throws InterruptedException
     */
//    执行日志:
//    获取到锁:/distributed_lock/task_0000000063
//    ZkClientUtils - ...... 未获取到锁,阻塞等待 。。。。。。
//    ZkClientUtils - ...... 未获取到锁,阻塞等待 。。。。。。
//    ZkClientUtils - ...... 未获取到锁,阻塞等待 。。。。。。
//    ZkClientUtils - ...... 未获取到锁,阻塞等待 。。。。。。
//    ZkClientUtils - 。。。。。。前一个节点被删除(删除/distributed_lock/task_0000000063节点)  。。。。。。
//    ZkClientUtils - 获取到锁:/distributed_lock/task_0000000064
//    ZkClientUtils - ...... 未获取到锁,阻塞等待 。。。。。。
//    ZkClientUtils - 。。。。。。前一个节点被删除  。。。。。。
//    ZkClientUtils - 获取到锁:/distributed_lock/task_0000000065
    public static Boolean acquireLock(String lockName) throws InterruptedException {
        // 2.获取lock节点下的所有子节点
        List<String> childrenList = getInstance().getChildren(LOCK_NODE);
        // 3.对子节点进行排序,获取最小值
        Collections.sort(childrenList, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return Integer.parseInt(o1.split("_")[1]) - Integer.parseInt(o2.split("_")[1]);
            }
        });
        // 4.判断当前创建的节点是否在第一位
        int lockPostion = childrenList.indexOf(lockName.split("/")[lockName.split("/").length - 1]);
        if(lockPostion < 0) {
            // 不存在该节点
            throw new ZkNodeExistsException("不存在的节点:" + lockName);
        } else if (lockPostion == 0) {
            // 获取到锁
            LOG.info("获取到锁:" + lockName);
            return true;
        } else if (lockPostion > 0) {
            // 未获取到锁,阻塞
            LOG.info("...... 未获取到锁,阻塞等待 。。。。。。");
            // 5.如果未获取得到锁,监听当前创建的节点前一位的节点
            final CountDownLatch latch = new CountDownLatch(1);
            IZkDataListener listener = new IZkDataListener() {

                @Override
                public void handleDataDeleted(String dataPath) throws Exception {
                    // 6.前一个节点被删除,当不保证轮到自己
                    LOG.info("。。。。。。前一个节点被删除  。。。。。。");
                    acquireLock(lockName);
                    latch.countDown();
                }

                @Override
                public void handleDataChange(String dataPath, Object data) throws Exception {
                    // 节点内容被修改
                }
            };
            try {
                getInstance().subscribeDataChanges(LOCK_NODE + "/" + childrenList.get(lockPostion - 1), listener);
                latch.await();
            } finally {
                getInstance().unsubscribeDataChanges(LOCK_NODE + "/" + childrenList.get(lockPostion - 1), listener);
            }
        }
        return false;
    }

}

4.3 方法调用

 @ApiOperation(value = "getZkData",notes = "getZkData")
    @RequestMapping(value ="/getZkData" , method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
    public String getZkData(HttpServletRequest request,
                            HttpServletResponse response,
                            @ApiParam(value = "path") @RequestParam(required = true) String path
                           ){
      String result=  ZkClientUtils.readData(path);
      return  result;
    }

    @ApiOperation(value = "createZk",notes = "createZk")
    @RequestMapping(value ="/createZk" , method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
    public String createZk(HttpServletRequest request,
                            HttpServletResponse response,
                            @ApiParam(value = "path") @RequestParam(required = true) String path,
                            @ApiParam(value = "data") @RequestParam(required = true) String data
    ){
        String result=  ZkClientUtils.create(path,data);
        return  result;
    }

3 zoopeeper的应用

3.1 共享配置文件

3.2 zoopeeper跨进程,跨主机,跨网络的共享资源使用同步锁

    ZkClientUtils.getLock()

@ApiOperation(value = "getLock",notes = "getLock")
    @RequestMapping(value ="/getLock" , method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
    public String getLock(HttpServletRequest request,
                          HttpServletResponse response
    ){
       String  name = ZkClientUtils.getLock();
       String  result = "获取到锁:"+name;
       //业务逻辑
       //获取到 直接删除当前子节点
        //        boolean flag= ZkClientUtils.delete(name);
        //        if(flag){
        //            result+="删除"+name+"成功!";
        //        }else{
        //            result+="删除"+name+"失败!!!!";
        //        }
       return  result;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值