Apache Curator客戶端的使用

原文鏈接:https://www.zifangsky.cn/1166.html

當前已有的三種API客戶端

  • zk原生API :不支持超時自動重連,不支持節點遞歸創建
  • zkclient:無文檔,異常處理弱爆了(簡單的拋出RuntimeException)
  • Apache Curator:簡化並提供各種場景的使用方法

Apache Curator

Apache Curator是一個比較完善的ZooKeeper客戶端框架,通過封裝的一套高級API 簡化了ZooKeeper的操作。通過查看官方文檔,可以發現Curator主要解決了三類問題

  • 封裝ZooKeeper client與ZooKeeper server之間的連接處理
  • 提供了一套Fluent風格的操作API
  • 提供ZooKeeper各種應用場景(recipe, 比如:分佈式鎖服務、集羣領導選舉、共享計數器、緩存機制、分佈式隊列等)的抽象封裝

Curator主要從以下幾個方面降低了zk使用的複雜性

  • 重試機制:提供可插拔的重試機制, 它將給捕獲所有可恢復的異常配置一個重試策略,並且內部也提供了幾種標準的重試策略(比如指數補償)
  • 連接狀態監控: Curator初始化之後會一直對zk連接進行監聽,一旦發現連接狀態發生變化將會作出相應的處理
  • zk客戶端實例管理:Curator會對zk客戶端到server集羣的連接進行管理,並在需要的時候重建zk實例,保證與zk集羣連接的可靠性
  • 各種使用場景支持:Curator實現了zk支持的大部分使用場景(甚至包括zk自身不支持的場景),這些實現都遵循了zk的最佳實踐,並考慮了各種極端情況

Curator幾個組成部分

  • Client: 是ZooKeeper客戶端的一個替代品, 提供了一些底層處理和相關的工具方法
  • Framework: 用來簡化ZooKeeper高級功能的使用, 並增加了一些新的功能, 比如管理到ZooKeeper集羣的連接, 重試處理
  • Recipes: 實現了通用ZooKeeper的recipe, 該組件建立在Framework的基礎之上
  • Utilities:各種ZooKeeper的工具類
  • Errors: 異常處理, 連接, 恢復等
  • Extensions: recipe擴展

使用

pom.xml中添加如下依賴:

		<!-- Apache Curator -->
		<dependency>
		    <groupId>org.apache.curator</groupId>
		    <artifactId>curator-framework</artifactId>
		    <version>4.0.0</version>
		</dependency>
		<dependency>
		    <groupId>org.apache.curator</groupId>
		    <artifactId>curator-recipes</artifactId>
		    <version>4.0.0</version>
		</dependency>
		<dependency>
		    <groupId>org.apache.curator</groupId>
		    <artifactId>curator-x-discovery</artifactId>
		    <version>4.0.0</version>
		</dependency>
		<dependency>
		    <groupId>org.apache.curator</groupId>
		    <artifactId>curator-test</artifactId>
		    <version>4.0.0</version>
		    <scope>test</scope>
		</dependency>

Java代碼測試使用

package cn.zifangsky.kafkademo.zookeeper;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;

import org.junit.Before;
import org.junit.Test;

/**
 * 測試Apache Curator框架的基本用法
 * API文檔:http://curator.apache.org/apidocs/index.html
 * @author zifangsky
 *
 */
public class TestApacheCurator {

	//會話超時時間
	private final int SESSION_TIMEOUT = 30 * 1000;
	
	//連接超時時間
	private final int CONNECTION_TIMEOUT = 3 * 1000;
	
	//ZooKeeper服務地址
	private static final String SERVER = "192.168.1.159:2100,192.168.1.159:2101,192.168.1.159:2102";
	
	//創建連接實例
	private CuratorFramework client = null;
	
	/**
	 * baseSleepTimeMs:初始的重試等待時間
	 * maxRetries:最多重試次數
	 */
	RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
	
	@Before
	public void init(){
		//創建 CuratorFrameworkImpl實例
		client = CuratorFrameworkFactory.newClient(SERVER, SESSION_TIMEOUT, CONNECTION_TIMEOUT, retryPolicy);
		
		//啓動
		client.start();
	}
	
	/**
	 * 測試創建節點
	 * @throws Exception 
	 */
	@Test
	public void testCreate() throws Exception{
		//創建永久節點
		client.create().forPath("/curator","/curator data".getBytes());
		
		//創建永久有序節點
		client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/curator_sequential","/curator_sequential data".getBytes());
		
		//創建臨時節點
		client.create().withMode(CreateMode.EPHEMERAL)
			.forPath("/curator/ephemeral","/curator/ephemeral data".getBytes());

		//創建臨時有序節點
		client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
			.forPath("/curator/ephemeral_path1","/curator/ephemeral_path1 data".getBytes());
		
		client.create().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
		.forPath("/curator/ephemeral_path2","/curator/ephemeral_path2 data".getBytes());
	}

	/**
	 * 測試檢查某個節點是否存在
	 * @throws Exception
	 */
	@Test
	public void testCheck() throws Exception{
		Stat stat1 = client.checkExists().forPath("/curator");
		Stat stat2 = client.checkExists().forPath("/curator2");
		
		System.out.println("'/curator'是否存在: " + (stat1 != null ? true : false));
		System.out.println("'/curator2'是否存在: " + (stat2 != null ? true : false));
	}

	/**
	 * 測試獲取和設置節點數據
	 * @throws Exception
	 */
	@Test
	public void testGetAndSet() throws Exception{
		//獲取某個節點的所有子節點
		System.out.println(client.getChildren().forPath("/"));
		
		//獲取某個節點數據
		System.out.println(new String(client.getData().forPath("/curator")));
		
		//設置某個節點數據
		client.setData().forPath("/curator","/curator modified data".getBytes());
	}
	/**
	 * 測試異步設置節點數據
	 * @throws Exception 
	 */
	@Test
	public void testSetDataAsync() throws Exception{
		//創建監聽器
		CuratorListener listener = new CuratorListener() {
			
			@Override
			public void eventReceived(CuratorFramework client, CuratorEvent event)
					throws Exception {
				System.out.println(event.getPath());
			}
		};
		
		//添加監聽器
		client.getCuratorListenable().addListener(listener);
		
		//異步設置某個節點數據
		client.setData().inBackground().forPath("/curator","/curator modified data with Async".getBytes());
		
		//爲了防止單元測試結束從而看不到異步執行結果,因此暫停10秒
		Thread.sleep(10000);
	}

	/**
	 * 測試另一種異步執行獲取通知的方式
	 * @throws Exception
	 */
	@Test
	public void testSetDataAsyncWithCallback() throws Exception{
		BackgroundCallback callback = new BackgroundCallback() {
			
			@Override
			public void processResult(CuratorFramework client, CuratorEvent event)
					throws Exception {
				System.out.println(event.getPath());
			}
		};
	
		//異步設置某個節點數據
		client.setData().inBackground(callback).forPath("/curator","/curator modified data with Callback".getBytes());		
	
		//爲了防止單元測試結束從而看不到異步執行結果,因此暫停10秒
		Thread.sleep(10000);	
	}

	/**
	 * 測試刪除節點
	 * @throws Exception 
	 */
	@Test
	public void testDelete() throws Exception{
		//創建測試節點
		client.create().orSetData().creatingParentContainersIfNeeded()
			.forPath("/curator/del_key1","/curator/del_key1 data".getBytes());

		client.create().orSetData().creatingParentContainersIfNeeded()
		.forPath("/curator/del_key2","/curator/del_key2 data".getBytes());
		
		client.create().forPath("/curator/del_key2/test_key","test_key data".getBytes());
		
		
		//刪除該節點
		client.delete().forPath("/curator/del_key1");
		
		//級聯刪除子節點
		client.delete().guaranteed().deletingChildrenIfNeeded().forPath("/curator/del_key2");
	}

	/**
	 * 測試事務管理:碰到異常,事務會回滾:
	 * 在上面的測試代碼中,很顯然因爲節點“/curator”存在子節點,所以在刪除的時候將會報錯,以上代碼並不能成功執行。這時觀察ZKui節點,可以發現事務已經回滾了:
	 * @throws Exception
	 */
	@Test
	public void testTransaction() throws Exception{
		//定義幾個基本操作
		CuratorOp createOp = client.transactionOp().create()
				.forPath("/curator/one_path","some data".getBytes());
		
		CuratorOp setDataOp = client.transactionOp().setData()
				.forPath("/curator","other data".getBytes());
		
		CuratorOp deleteOp = client.transactionOp().delete()
				.forPath("/curator");
		
		//事務執行結果
		List<CuratorTransactionResult> results = client.transaction()
				.forOperations(createOp,setDataOp,deleteOp);
		
		//遍歷輸出結果
		for(CuratorTransactionResult result : results){
			System.out.println("執行結果是: " + result.getForPath() + "--" + result.getType());
		}
	}

	/**
	 * 測試命名空間
	 * 爲了避免多個應用的節點名稱衝突的情況,CuratorFramework提供了命名空間的概念。具體做法是:CuratorFramework會爲它的API調用的節點路徑的前面自動添加上命名空間。命名空間本質上是從根節點開始的一個路徑,如:mydemo、mydemo/v1
	 * @throws Exception 
	 */
	@Test
	public void testNamespace() throws Exception{
		//創建帶命名空間的連接實例
		CuratorFramework client2 = CuratorFrameworkFactory.builder()
				.namespace("mydemo/v1")
				.connectString(SERVER)
				.sessionTimeoutMs(SESSION_TIMEOUT)
				.connectionTimeoutMs(CONNECTION_TIMEOUT)
				.retryPolicy(retryPolicy)
				.build();
		//啓動
		client2.start();
		
		client2.create().orSetData().creatingParentContainersIfNeeded()
		.forPath("/server1/method1","some data".getBytes());	
		
		client2.close();
	}
	
	@After
	public void close(){
		client.close();
	}

}

在Curator中,首先需要通過CuratorFrameworkFactory創建連接實例,接着才能進行各種基本操作。需要說明的是,關於連接重試策略,Curator默認提供了以下幾種:

  • ExponentialBackoffRetry:重試一定次數,每次重試時間依次遞增
  • RetryNTimes:重試N次
  • RetryOneTime:重試一次
  • RetryUntilElapsed:重試一定時間

針對上面的單元測試:測試刪除節點,我簡單說明一下:

  • orSetData()方法:如果節點存在則Curator將會使用給出的數據設置這個節點的值,相當於 setData() 方法
  • creatingParentContainersIfNeeded()方法:如果指定節點的父節點不存在,則Curator將會自動級聯創建父節點
  • guaranteed()方法:如果服務端可能刪除成功,但是client沒有接收到刪除成功的提示,Curator將會在後臺持續嘗試刪除該節點
  • deletingChildrenIfNeeded()方法:如果待刪除節點存在子節點,則Curator將會級聯刪除該節點的子節點

具體場景使用

多節點,多線程下發訂單,使用zookeeper分佈式鎖機制保證訂單正確接入oms系統
https://blog.csdn.net/xiongxianze/article/details/79176000

ZK UI客戶端圖形化查看工具

zkui是github上的一個開源項目,主要實現了針對ZooKeepe的可視化增刪改查功能。但是這個項目沒有提供Release版本,需要自行下載源碼編譯。在這裏,爲了方便大家使用,我已經fork了這個項目,在修改了幾處明顯的bug之後編譯了一個Pre-release版本,歡迎大家下載使用

下載地址:https://github.com/zifangsky/zkui/releases

(1)下載zkui-2.0.zip,並修改config.cfg配置文件:
主要修改以下內容:
在這裏插入圖片描述
(2)運行:
Windows系統點擊運行start.bat腳本,Linux系統則相應執行start.sh即可
(3)訪問WEB客戶端並使用:
訪問:http://127.0.0.1:6060/
同時使用 admin/admin 登錄,登錄之後的界面如下:
在這裏插入圖片描述

參考資料

Apache Curator客戶端的使用
https://blog.csdn.net/weixin_37778801/article/details/84704262
https://blog.csdn.net/vbirdbest/article/details/82716934
多節點,多線程下發訂單,使用zookeeper分佈式鎖機制保證訂單正確接入oms系統
https://blog.csdn.net/xiongxianze/article/details/79176000

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