Curator的簡單封裝使用

前言:

    閒來無事,想起把zookeeper的相關知識再補充一下。作爲中間件部門的成員,與zookeeper打交道的機會還是很多的。目前市面上的很多產品,尤其是分佈式相關的,基本都會用上zookeeper。

    本文不是一篇介紹zookeeper是什麼和怎麼使用的文章,而是介紹zookeeper的使用框架Curator的。本來筆者還想着把Curator的使用再好好熟悉下,因爲一段時間不用就覺得生疏了,突然發現之前竟然對Curator的使用封裝過,好吧,那就發出來吧,以供大家參考、指正。

 

    寫這篇博客的另一個動機就是爲了下一篇博客,下一篇會寫一下真正的大牛他們是怎麼封裝使用Curator,也算是拋磚引玉了。

 

準備工作:

    老規矩,首先介紹下筆者的使用環境和相關依賴。

    1.zookeeper服務,單機版,本地使用,版本爲3.4.11

    2.maven依賴如下:

		<!-- zookeeper -->
		<dependency>
		    <groupId>org.apache.zookeeper</groupId>
		    <artifactId>zookeeper</artifactId>
		    <version>3.4.11</version>
			<exclusions>
				<exclusion>
					<artifactId>log4j</artifactId>
					<groupId>log4j</groupId>
				</exclusion>
			</exclusions>
		</dependency>

		<!-- curator -->
		<dependency>
		    <groupId>org.apache.curator</groupId>
		    <artifactId>curator-framework</artifactId>
		    <version>4.0.0</version>
			<exclusions>
				<exclusion>
					<artifactId>zookeeper</artifactId>
					<groupId>org.apache.zookeeper</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
			<version>4.0.0</version>
		</dependency>

1.Curator封裝使用

    筆者就寫了一個單類,裏面封裝CRUD的操作方法,具體如下:

package com.example.demo.curator;

import org.apache.curator.RetryPolicy;
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.BackgroundCallback;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.Executor;

/**
 * Curator的簡單封裝
 * @author lucky
 */
public class CuratorManager {

	/** 默認值 */
	private static final String DEFAULT_CONNECT_STRING = "localhost:2181";
	private static final int DEFAULT_SESSION_TIME_OUT = 5000;
	private static final int DEFAULT_CONNECTION_TIME_OUT = 3000;
	private static final RetryPolicy DEFAULT_RETRY_POLICY
			= new ExponentialBackoffRetry(1000, 3);

	/** zookeeper服務器地址 */
	private String connectAddr;

	/** session超時時間 */
	private int sessionTimeOut;

	/** 連接超時時間 */
	private int connectionTimeOut;

	/** 重試策略 */
	private RetryPolicy policy = null;

	private CuratorFramework client = null;

	/** 構造方法 */
	public CuratorManager() {
		this(DEFAULT_CONNECT_STRING,DEFAULT_SESSION_TIME_OUT,DEFAULT_CONNECTION_TIME_OUT,DEFAULT_RETRY_POLICY);
	}

	public CuratorManager(String connectAddr) {
		this(connectAddr,DEFAULT_SESSION_TIME_OUT,DEFAULT_CONNECTION_TIME_OUT,DEFAULT_RETRY_POLICY);
	}

	public CuratorManager(String connectAddr, Integer sessionTimeOut, Integer connectionTimeOut, RetryPolicy policy) {
		this.connectAddr = connectAddr == null ? DEFAULT_CONNECT_STRING : connectAddr;
		this.sessionTimeOut = sessionTimeOut == null ? DEFAULT_SESSION_TIME_OUT : sessionTimeOut;
		this.connectionTimeOut = connectionTimeOut == null ? DEFAULT_CONNECTION_TIME_OUT : connectionTimeOut;
		this.policy = policy == null ? DEFAULT_RETRY_POLICY : policy;

		this.client = CuratorFrameworkFactory.newClient(connectAddr,sessionTimeOut,connectionTimeOut,policy);
		client.start();
	}

	/** create */

	/**
	 * 只創建路徑,data默認爲空
	 * @param path 路徑
	 * @throws Exception
	 */
	public void create(String path) throws Exception{
		this.createWithData(path,"");
	}

	/**
	 * 創建路徑和對應data值
	 * @param path 路徑
	 * @param data 值
	 * @throws Exception
	 */
	public void createWithData(String path, String data) throws Exception{
		this.createWithMode(path,data,CreateMode.PERSISTENT);
	}

	/**
	 * 創建路徑和對應data值,並指定創建模式
	 * @param path 路徑
	 * @param data 值
	 * @param mode 創建模式
	 * @throws Exception
	 */
	public void createWithMode(String path, String data, CreateMode mode) throws Exception{
		client.create().creatingParentsIfNeeded().withMode(mode).forPath(path, data.getBytes());
	}

	/**
	 * 創建路徑和對應data值,指定創建模式,並創建callback
	 * @param path 路徑
	 * @param data 值
	 * @param mode 創建模式
	 * @param callback 響應事件
	 * @param es 使用線程池來執行響應事件
	 * @throws Exception
	 */
	public void createWithBackGround(String path, String data, CreateMode mode, BackgroundCallback callback, Executor es) throws Exception {

		ACLBackgroundPathAndBytesable<String> pathAndBytesable = client.create().creatingParentsIfNeeded().withMode(mode);
		if (null != callback) {
			if (null != es) {
				pathAndBytesable.inBackground(callback, es);
			} else {
				pathAndBytesable.inBackground(callback);
			}
		}

		pathAndBytesable.forPath(path, data.getBytes());
	}

	/** delete */

	/**
	 * 刪除該路徑,包括子節點
	 * @param path 路徑
	 * @throws Exception
	 */
	public void delete(String path) throws Exception {
		this.deleteWithVersion(path,-1);
	}

	/**
	 * 刪除該路徑指定version,包括子節點
	 * @param path 路徑
	 * @param version 版本
	 * @throws Exception
	 */
	public void deleteWithVersion(String path, int version) throws Exception {
		this.deleteWithAllChildren(path, version, false);
	}

	/**
	 * 可自定義是否刪除該path下子節點
	 * @param path 路徑
	 * @param version 版本
	 * @param deleteChildren 是否刪除子節點
	 * @throws Exception
	 */
	public void deleteWithAllChildren(String path, int version, boolean deleteChildren) throws Exception {
		if (deleteChildren) {
			client.delete().guaranteed().deletingChildrenIfNeeded().withVersion(version).forPath(path);
		} else {
			client.delete().guaranteed().withVersion(version).forPath(path);
		}
	}

	/** get */

	/**
	 * 獲取路徑對應值
	 * @param path 路徑
	 * @return 值
	 * @throws Exception
	 */
	public String get(String path) throws Exception {
		byte[] bytes = client.getData().forPath(path);
		return new String(bytes);
	}

	/**
	 * 獲取路徑stat信息
	 * @param path 路徑
	 * @return stat信息
	 * @throws Exception
	 */
	public Stat getWithStat(String path) throws Exception {
		Stat stat = new Stat();
		client.getData().storingStatIn(stat).forPath(path);

		return stat;
	}


	/** update */

	/**
	 * 修改path對應data值
	 * @param path 路徑
	 * @param data 值
	 * @throws Exception
	 */
	public void update(String path, String data) throws Exception {
		this.updateWithVersion(path ,data, -1);
	}

	/**
	 * 修改path對應version的data值
	 * @param path 路徑
	 * @param data 值
	 * @param version 版本
	 * @throws Exception
	 */
	public void updateWithVersion(String path, String data, int version) throws Exception {
		client.setData().withVersion(version).forPath(path, data.getBytes());
	}

	/**
	 * 添加對應path的監聽,當該path的data值變動時,則觸發callback
	 * @param path 路徑
	 * @param callback 回調方法
	 * @param es 使用線程池來執行響應事件
	 * @throws Exception
	 */
	public void dataListener(String path, NodeCacheCallback callback, Executor es) throws Exception {
		NodeCache nodeCache = new NodeCache(client, path, false);
		nodeCache.start(true);

		NodeCacheListener listener = new NodeCacheListener() {
			@Override
			public void nodeChanged() throws Exception {
				callback.callback(nodeCache);
			}
		};

		if (null != es) {
			nodeCache.getListenable().addListener(listener, es);
		} else {
			nodeCache.getListenable().addListener(listener);
		}
	}

	/**
	 * 添加對應path的監聽,當該path下的子節點發生變動時,則觸發callback
	 * 注意:二級子節點時間無法監聽
	 * @param path 路徑
	 * @param callback 回調方法
	 * @param es 使用線程池來執行響應事件
	 * @throws Exception
	 */
	public void childrenListener(String path, NodeChildrenCacheCallback callback, Executor es) throws Exception {

		PathChildrenCache pathChildrenCache = new PathChildrenCache(client, path, true);
		pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);

		PathChildrenCacheListener pathChildrenCacheListener = new PathChildrenCacheListener(){
			@Override
			public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
				callback.callback(pathChildrenCache, client, event);
			}
		};

		if (null != es) {
			pathChildrenCache.getListenable().addListener(pathChildrenCacheListener, es);
		} else {
			pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
		}
	}

	/**
	 * 自定義data值變化監聽接口
	 */
	interface NodeCacheCallback{
		void callback(NodeCache cache);
	}

	/**
	 * 自定義子節點變動監聽接口
	 */
	interface NodeChildrenCacheCallback{
		void callback(PathChildrenCache cache, CuratorFramework client, PathChildrenCacheEvent event);
	}

	public static void main(String[] args) {

		CuratorManager curatorManager = new CuratorManager();

		String path = "/xw/test/create";
		String data = "just for test";

		try {

			// create
			curatorManager.createWithData(path ,data);
            // update
			curatorManager.update(path,"test just");
            // get state
			Stat withStat = curatorManager.getWithStat(path);
			System.out.println(withStat);
			
            // get data
			String s = curatorManager.get(path);
			System.out.println(s);

            // delete
			curatorManager.delete(path);
			curatorManager.deleteWithAllChildren("/xw",-1,true);

            // data change listener
			curatorManager.dataListener(path, new NodeCacheCallback() {
				@Override
				public void callback(NodeCache cache) {
					System.out.println("node data update, current data: " + cache.getCurrentData());
				}
			}, null);

			// children change listener 
			curatorManager.childrenListener(path, new NodeChildrenCacheCallback() {
				@Override
				public void callback(PathChildrenCache cache, CuratorFramework client, PathChildrenCacheEvent event) {
					switch (event.getType()) {
						case CHILD_ADDED:
							System.out.println("child_add, data:" + event.getData());

						case CHILD_UPDATED:
							System.out.println("child_update, data:" + event.getData());

						case CHILD_REMOVED:
							System.out.println("child_removed, data:" + event.getData());

						default:
							break;
					}
				}
			}, null);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

總結:

    簡單的封裝使用Curator,看了越來越多的源碼之後,就會發現,寫代碼的時候不自覺的會想往這方面去靠。

    會去想着寫更簡潔的、封裝性更好的代碼。

    同時設計模式相關的也會不自覺的冒出來。

    共勉之!

 

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