Zookeeper客户端Curator的基本操作、分布式锁及领导者选举示例

Zookeeper的基本操作请参考之前博客:Zookeeper节点及客户端基本操作
本文只是简单的介绍一些常见的操作,更加详细的教程可以参考官网:http://curator.apache.org/getting-started.html

1.创建maven工程、引入curator依赖

curator使用sl4j作为日志框架,因此这里引入了logback

	<dependencies>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-framework</artifactId>
			<version>4.2.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
			<version>4.2.0</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
		</dependency>
	</dependencies>
2. 基本增删改查
import java.util.List;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Curator 客户端基本操作
 *
 */
public class CuratorClientDemo {
	private static final Logger LOG = LoggerFactory.getLogger(CuratorClientDemo.class);
	// Zookeeper 服务器地址
	private static final String ZK_SERVER = "127.0.0.1:2181";

	private CuratorFramework client;

	public CuratorClientDemo() {
		// 初始化客户端并启动
		this.client = CuratorFrameworkFactory.newClient(ZK_SERVER, new RetryNTimes(5, 6000));
		client.start();
		LOG.info("============curator client started=====");
	}

	/**
	 * 创建新的节点
	 * 
	 * @param path 节点路径
	 * @param data 节点数据
	 * @return 成功与否
	 */
	public boolean createNode(String path, String data) {
		try {
			this.client.create().creatingParentsIfNeeded().forPath(path, data.getBytes());
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			LOG.warn("==============创建节点 {} 失败=======", path);
			return false;
		}
	}

	/**
	 * 获取节点数据
	 * 
	 * @param path 节点路径
	 * @return 节点数据
	 */
	public String getData(String path) {
		try {
			byte[] data = this.client.getData().forPath(path);
			return new String(data);
		} catch (Exception e) {
			e.printStackTrace();
			LOG.warn("==============获取节点 {} 数据失败=======", path);
		}
		return null;
	}

	/**
	 * 获取子节点
	 * 
	 * @param path 父节点
	 * @return 子节点路径集合
	 */
	public List<String> getChildren(String path) {
		try {
			return this.client.getChildren().forPath(path);
		} catch (Exception e) {
			e.printStackTrace();
			LOG.warn("==============获取节点 {} 子节点失败=======", path);
		}
		return null;
	}

	/**
	 * 修改节点
	 * 
	 * @param path 节点路径
	 * @param data 节点数据
	 * @return 成功与否
	 */
	public boolean modifyNode(String path, String data) {
		try {
			this.client.setData().forPath(path, data.getBytes());
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			LOG.warn("==============修改节点 {} 失败=======", path);
			return false;
		}
	}

	/**
	 * 删除节点
	 * 
	 * @param path 节点路径
	 * @return 成功与否
	 */
	public boolean deleteNode(String path) {
		try {
			this.client.delete().forPath(path);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			LOG.warn("==============修改节点 {} 失败=======", path);
			return false;
		}
	}

	/**
	 * 关闭连接
	 */
	public void closeConnection() {
		this.client.close();
		LOG.info("============curator client closed=====");
	}

	public static void main(String[] args) throws Exception {
		CuratorClientDemo client = new CuratorClientDemo();
		String path = "/qqxhb";
		client.createNode(path, "test");
		String data = client.getData(path);
		System.out.println(data);
		List<String> children = client.getChildren("/");
		children.forEach(System.out::println);
		client.modifyNode(path, "zookeeper");
		client.deleteNode(path);
		client.closeConnection();
	}

}

3. 领导者选举功能

在分布式集群的环境中,经常需要一个领导者参与环境调度管理工作,curator客户端借助zookeeper可以很方便实现该功能。

import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 领导者选举客户端
 */
public class LeaderClient extends LeaderSelectorListenerAdapter implements Closeable {
	private static final Logger LOG = LoggerFactory.getLogger(LeaderSelectorListenerAdapter.class);

	private final String name;
	private final LeaderSelector leaderSelector;
	private final AtomicInteger leaderCount = new AtomicInteger();

	public LeaderClient(CuratorFramework client, String path, String name) {
		this.name = name;
		// 根据路径创建领导者选举实例,所有参与领导者选举的都必须使用同一个路径
		leaderSelector = new LeaderSelector(client, path, this);

		// 放弃领导者之后重新参与领导者选举
		leaderSelector.autoRequeue();
	}

	public void start() throws IOException {
		// 启动领导者选举,并在后台运行
		leaderSelector.start();
	}

	@Override
	public void close() throws IOException {
		leaderSelector.close();
	}

	@Override
	public void takeLeadership(CuratorFramework client) throws Exception {
		// 同一时刻,只有一个Listener会进入takeLeadership()方法,说明它是当前的Leader。
		// 注意:当Listener从takeLeadership()退出时就说明它放弃了“Leader身份”,下面是使用睡眠时间模拟持有领导者的时间

		final int waitSeconds = (int) (5 * Math.random()) + 1;

		LOG.info("当前领导者是:{},已经第 {} 次当选,占据领导者时间 {} 秒后放弃领导者身份。。。。。 ", this.name, leaderCount.getAndIncrement(),
				waitSeconds);
		try {
			Thread.sleep(TimeUnit.SECONDS.toMillis(waitSeconds));
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
		} finally {

			LOG.info("======= {} 放弃领导权。。。。。", this.name);
		}
	}
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.CloseableUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;

public class LeaderSelectorExample {
	private static final Logger LOG = LoggerFactory.getLogger(LeaderSelectorListenerAdapter.class);
	// 参与者数量
	private static final int PARTITIONS = 5;
	// 领导者选举节点路径
	private static final String PATH = "/qqxhb/leader";
	// Zookeeper 服务器地址
	private static final String ZK_SERVER = "127.0.0.1:2181";

	public static void main(String[] args) throws Exception {

		List<CuratorFramework> clients = Lists.newArrayList();
		List<LeaderClient> leaderClients = Lists.newArrayList();
		try {
			for (int i = 0; i < PARTITIONS; ++i) {
				CuratorFramework client = CuratorFrameworkFactory.newClient(ZK_SERVER,
						new ExponentialBackoffRetry(2000, 5));
				clients.add(client);
				LeaderClient leaderClient = new LeaderClient(client, PATH, "Client #" + i);
				leaderClients.add(leaderClient);

				client.start();
				leaderClient.start();
			}

			LOG.info("====按回车键结束领导者选举过程。。。。。。。。。");
			new BufferedReader(new InputStreamReader(System.in)).readLine();
		} finally {
			LOG.info("====领导者选举过程结束。。。。。。。。。");

			for (LeaderClient exampleClient : leaderClients) {
				CloseableUtils.closeQuietly(exampleClient);
			}
			for (CuratorFramework client : clients) {
				CloseableUtils.closeQuietly(client);
			}

		}
	}
}
4. 分布式锁功能

分布式集群环境经常也会遇到共享资源的问题,比如定时任务同一时间只希望其中一台服务器执行,则需要使用到分布式锁,同样在curator中有提供相应的简单实现。


import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * 模拟任务锁
 * 
 */
public class TaskLocks {
	private static final Logger LOG = LoggerFactory.getLogger(TaskLocks.class);
	private final InterProcessMutex lock;
	private final String taskName;
	private final String clientName;

	public TaskLocks(CuratorFramework client, String lockPath, String taskName, String clientName) {
		this.taskName = taskName;
		this.clientName = clientName;
		lock = new InterProcessMutex(client, lockPath);
	}

	public void doWork(long time, TimeUnit unit) throws Exception {
		if (!lock.acquire(time, unit)) {
			throw new IllegalStateException(this.clientName + " could not acquire the lock");
		}
		try {
			LOG.info("======= {} 获取到锁,开始执行任务{}。。。。", this.clientName, this.taskName);
			Thread.sleep((long) (3 * Math.random()));
		} finally {
			LOG.info("======= {}释放任务锁{}。。。。", this.clientName, this.taskName);
			lock.release();
		}
	}
}

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.CloseableUtils;

public class LockingExample {

	private static final int CLIENTS = 5;
	// 锁节点路径
	private static final String PATH = "/qqxhb/locks";
	// Zookeeper 服务器地址
	private static final String ZK_SERVER = "127.0.0.1:2181";

	public static void main(String[] args) throws Exception {

		ExecutorService service = Executors.newFixedThreadPool(CLIENTS);
		try {
			String task = "task_test";
			for (int i = 0; i < CLIENTS; ++i) {
				int cli = i;
				service.execute(() -> {
					CuratorFramework client = CuratorFrameworkFactory.newClient(ZK_SERVER,
							new ExponentialBackoffRetry(2000, 5));
					try {
						client.start();
						TaskLocks example = new TaskLocks(client, PATH, task, "Client #" + cli);
						example.doWork(10, TimeUnit.SECONDS);
					} catch (InterruptedException e) {
						Thread.currentThread().interrupt();
					} catch (Exception e) {
						e.printStackTrace();
					} finally {
						CloseableUtils.closeQuietly(client);
					}
				});
			}

			service.shutdown();
			service.awaitTermination(10, TimeUnit.MINUTES);
		} finally {
			service.shutdown();
		}
	}
}

本文涉及的源码地址(curator-demo模块):https://github.com/qqxhb/zookeeper

发布了131 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43792385/article/details/105125054