柚子快報激活碼778899分享:【ZooKeeper學(xué)習(xí)筆記】
柚子快報激活碼778899分享:【ZooKeeper學(xué)習(xí)筆記】
1. ZooKeeper基本概念
Zookeeper官網(wǎng):https://zookeeper.apache.org/index.html
Zookeeper是Apache Hadoop項目中的一個子項目,是一個樹形目錄服務(wù)Zookeeper翻譯過來就是動物園管理員,用來管理Hadoop(大象)、Hive(蜜蜂)、Pig(小豬)的管理員,簡稱zkZookeeper的本質(zhì)是一個分布式的、開源的、提供分布式應(yīng)用程序協(xié)調(diào)服務(wù)的組件Zookeeper提供的主要功能有:
配置管理分布式鎖集群管理
2. ZooKeeper常用命令
2.1 ZooKeeper數(shù)據(jù)模型
在正式介紹Zookeeper的常用命令之前,我們先來了解一下Zookeeper的相關(guān)數(shù)據(jù)模型:
Zookeeper的是一個樹形目錄服務(wù),其數(shù)據(jù)模型與unix文件系統(tǒng)目錄樹類似,是一個層次化的結(jié)構(gòu)這里面的每一個節(jié)點都被稱為ZNode,每個節(jié)點上都會保存自己的數(shù)據(jù)以及元數(shù)據(jù)信息節(jié)點也可以擁有子節(jié)點,同時允許少量數(shù)據(jù)(1MB)存儲在該節(jié)點之下節(jié)點類型大致可以分為如下四類:
PERSISTENT:持久化節(jié)點EPHEMERAL:臨時節(jié)點 -ePERSISTENT_SEQUENTIAL:持久化順序節(jié)點 -sEPHEMERAL_SEQUENTIAL:臨時順序節(jié)點 -e -s
2.2 ZooKeeper常用命令
Zookeeper是一個常見的客戶端-服務(wù)器模型,我們可以使用命令行或者JavaAPI的方式充當(dāng)客戶端進(jìn)行訪問,其架構(gòu)如下圖所示:
服務(wù)端命令:
./zkServer.sh start啟動zookeeper服務(wù)
./zkServer.sh status查看zookeeper服務(wù)運(yùn)行狀態(tài)
./zkServer.sh restart重啟zookeeper服務(wù)
./zkServer.sh stop關(guān)閉zookeeper服務(wù)
客戶端命令:
./zkCli.sh -server ip:port連接指定的zookeeper服務(wù)(如連接本地可忽略選項直接使用./zkCli.sh)
quit退出客戶端交互界面
help查看命令幫助
ls 目錄查看指定目錄下的znode節(jié)點
ls -s 目錄查看節(jié)點詳細(xì)信息
create znode [value]創(chuàng)建znode節(jié)點(可以攜帶data)
create znode -e [value]創(chuàng)建臨時節(jié)點(會話結(jié)束后消失)create znode -s [value]創(chuàng)建順序節(jié)點
get znode查看節(jié)點攜帶數(shù)據(jù)
set znode value設(shè)置節(jié)點數(shù)據(jù)
delete znode刪除指定的znode節(jié)點(必須為空)
deleteall znode刪除指定的znode節(jié)點及其子節(jié)點
2.3 ZooKeeper的JavaAPI操作
2.3.1 Curator介紹
Curator:是一個Zookeeper的Java客戶端庫
常見的Zookeeper Java客戶端有如下幾種:
原生JavaAPIZkClientCurator Curator的目標(biāo)就是簡化Zookeeper客戶端的使用Curator項目最初有Netflix公司研發(fā),后來捐給了Apache基金會,成為頂級項目
Curator官網(wǎng):http://curator.apache.org/
2.3.2 Curator API操作
2.3.2.1 建立連接
我們可以使用CuratorFrameworkFactory靜態(tài)工廠類進(jìn)行創(chuàng)建,可以通過如下兩種方式配置:
使用newClient()方法使用build()方法
下面我們就給出對應(yīng)兩種代碼的實現(xiàn)方式: newClient:
/**
* ZooKeeper測試類
*/
public class ZooKeeperTest {
private CuratorFramework client = null;
@Before
public void initByNewClient() {
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181",
3000,
3000,
new ExponentialBackoffRetry(3000, 1));
this.client = client;
this.client.start();
}
}
build:
/**
* ZooKeeper測試類
*/
public class ZooKeeperTest {
private CuratorFramework client = null;
@Before
public void init() {
CuratorFramework client = CuratorFrameworkFactory
.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(3000)
.connectionTimeoutMs(3000)
.retryPolicy(new ExponentialBackoffRetry(3000, 1))
.namespace("")
.build();
client.start();
this.client = client;
}
}
其中各個配置項含義如下:
connectString:連接字符串,配置服務(wù)器地址,格式為ip:portsessionTimeoutMs:會話超時時間connectionTimeoutMs:連接超時時間retryPolicy:重試策略namespace:設(shè)置根目錄位置
2.3.2.2 創(chuàng)建節(jié)點
創(chuàng)建節(jié)點有如下常見的四種方式: Case1:創(chuàng)建節(jié)點(不攜帶數(shù)據(jù))
@Test
public void testCreate1() throws Exception {
String path = client.create().forPath("/app1");
System.out.println(path);
}
Case2:創(chuàng)建節(jié)點(攜帶數(shù)據(jù))
@Test
public void testCreate2() throws Exception {
String path = client.create().forPath("/app2", "curator java api".getBytes());
System.out.println(path);
}
Case3:創(chuàng)建多級節(jié)點
@Test
public void testCreate4() throws Exception {
client.create().creatingParentsIfNeeded().forPath("/test/test1/test2");
}
Case4:創(chuàng)建節(jié)點并指定類型
@Test
public void testCreate3() throws Exception {
client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");
client.create().withMode(CreateMode.PERSISTENT).forPath("/app4");
client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/app5");
client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/app6");
}
2.3.2.3 刪除節(jié)點
刪除節(jié)點有如下常見的兩種方式: Case1:刪除節(jié)點(不含子節(jié)點)
@Test
public void testDelete() throws Exception {
client.delete().forPath("/app1");
}
Case2:刪除節(jié)點(遞歸刪除子節(jié)點)
@Test
public void testDeleteAll() throws Exception {
client.delete().deletingChildrenIfNeeded().forPath("/test");
}
2.3.2.4 查詢節(jié)點
查詢節(jié)點有如下常見的三種方式: Case1:查詢子節(jié)點信息
@Test
public void testGetChildren() throws Exception {
List
System.out.println(childrenList);
}
Case2:查詢節(jié)點數(shù)據(jù)
@Test
public void testGetData() throws Exception {
byte[] bytes = client.getData().forPath("/app2");
System.out.println("data: " + new String(bytes));
}
Case3:查詢節(jié)點詳細(xì)信息
@Test
public void testGetData3() throws Exception {
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("/app2");
System.out.println(stat);
}
2.3.2.5 修改節(jié)點
修改節(jié)點有如下常見的兩種方式: Case1:修改節(jié)點數(shù)據(jù)
@Test
public void testSetData() throws Exception {
Stat stat = client.setData().forPath("/app1", "some data".getBytes());
System.out.println(stat);
}
Case2:修改節(jié)點數(shù)據(jù)(帶有版本號)
@Test
public void testSetData2() throws Exception {
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("/app2");
int version = stat.getVersion();
System.out.println(version);
client.setData().withVersion(version).forPath("/app2", "set with version".getBytes());
}
3. ZooKeeper的事件監(jiān)聽機(jī)制
Watcher事件監(jiān)聽機(jī)制:
ZooKeeper允許用戶在指定節(jié)點上注冊一些Watcher,當(dāng)一些特定事件發(fā)生時,ZooKeeper就會將事件通知給對其感興趣的客戶端,這是ZooKeeper提供分布式協(xié)調(diào)服務(wù)的重要特性ZooKeeper引入了Watcher機(jī)制來實現(xiàn)發(fā)布 / 訂閱功能,能夠讓多個訂閱者同時監(jiān)聽某一個對象,當(dāng)一個對象狀態(tài)發(fā)生變化時就會通知所有訂閱者ZooKeeper提供原生Watcher的方式,但是比較麻煩,因此Curator使用Cache數(shù)據(jù)結(jié)構(gòu)進(jìn)行了優(yōu)化實現(xiàn)監(jiān)聽機(jī)制Curator提供了如下三種Cache:
NodeCache:只監(jiān)聽某一個指定的節(jié)點變化PathChildrenCache:監(jiān)控一個節(jié)點的所有子節(jié)點TreeCache:監(jiān)控整個樹上的節(jié)點,類似于前兩者的組合
3.1 Node Cache
代碼實現(xiàn):
@Test
public void testCuratorCache() throws Exception {
NodeCache cache = new NodeCache(client, "/app1");
cache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println("監(jiān)聽到節(jié)點變化...");
}
});
cache.start();
while (true) {
}
}
3.2 PathChildren Cache
代碼實現(xiàn):
@Test
public void testPathChildrenCache() throws Exception {
PathChildrenCache cache = new PathChildrenCache(client, "/app1", true);
cache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
System.out.println("監(jiān)聽到子節(jié)點變化...");
PathChildrenCacheEvent.Type type = pathChildrenCacheEvent.getType();
if (type.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)) {
System.out.println("監(jiān)聽到子節(jié)點數(shù)據(jù)變化...");
System.out.println("更新后數(shù)據(jù): " + pathChildrenCacheEvent.getData().getData());
}
}
});
cache.start();
while (true) {
}
}
3.3 Tree Cache
代碼實現(xiàn):
@Test
public void testTreeCache() throws Exception {
TreeCache cache = new TreeCache(client, "/app1");
cache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
System.out.println("監(jiān)聽到節(jié)點發(fā)生變化...");
System.out.println(treeCacheEvent);
}
});
cache.start();
while (true) {
}
}
4. ZooKeeper分布式鎖
4.1 ZooKeeper分布式鎖原理
核心思想:當(dāng)用戶獲取到鎖時就創(chuàng)建節(jié)點,使用完鎖就刪除節(jié)點
每當(dāng)一個用戶想要獲取鎖時就在/lock節(jié)點下創(chuàng)建一個 **臨時順序 **節(jié)點然后獲取/lock節(jié)點下的全部子節(jié)點,如果發(fā)現(xiàn)當(dāng)前節(jié)點編號是最小的,則該節(jié)點對應(yīng)的客戶端獲取到鎖,使用完鎖后,刪除該節(jié)點如果發(fā)現(xiàn)節(jié)點編號不是最小的,則對前一個比自己小的編號節(jié)點,并注冊事件監(jiān)聽器,監(jiān)聽刪除事件如果后續(xù)發(fā)現(xiàn)比自己小的節(jié)點被刪除,則客戶端會接收到來自ZooKeeper的通知,然后再次判斷所對應(yīng)節(jié)點編號是否是最小的,重復(fù)上述步驟
注意:這里創(chuàng)建臨時節(jié)點是因為防止獲取到鎖的客戶端宕機(jī)了,進(jìn)而導(dǎo)致鎖永遠(yuǎn)不會被刪的情況;這是創(chuàng)建順序節(jié)點是方便編號的排序
Cutator提供了下面五種分布式鎖的方式:
InterProcessMutex(分布式可重入排他鎖)InterProcessSemaphoreMutex(分布式不可重入排他鎖)InterProcessReadWriteLock(分布式讀寫鎖)InterProcessMutliLock(將多個鎖作為單個實體管理的容器)InterProcessSemaphoreV2(共享信號量)
4.2 分布式鎖實戰(zhàn)(模擬12306搶票)
代碼如下:
package org.example;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import java.util.concurrent.TimeUnit;
public class ZooKeeperLockTest {
private static int tickets = 10; // 票數(shù)
public static void main(String[] args) {
// 建立連接
CuratorFramework client = CuratorFrameworkFactory
.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(3000)
.connectionTimeoutMs(3000)
.retryPolicy(new ExponentialBackoffRetry(3000, 1))
.namespace("")
.build();
client.start();
// 獲取分布式鎖
InterProcessMutex lock = new InterProcessMutex(client, "/lock");
Thread t1 = new Thread(() -> {
while (true) {
try {
boolean hasLock = lock.acquire(3, TimeUnit.SECONDS);
if (hasLock && tickets > 0) {
// 不斷搶票
System.out.println("線程" + Thread.currentThread().getName() + "搶到了當(dāng)前第" + tickets + "張票");
tickets--;
if (tickets <= 0) {
break;
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
lock.release();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}, "攜程");
Thread t2 = new Thread(() -> {
while (true) {
try {
boolean hasLock = lock.acquire(3, TimeUnit.SECONDS);
if (hasLock && tickets > 0) {
// 不斷搶票
System.out.println("線程" + Thread.currentThread().getName() + "搶到了當(dāng)前第" + tickets + "張票");
tickets--;
if (tickets <= 0) {
break;
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
lock.release();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}, "飛豬");
t1.start();
t2.start();
}
}
5. ZooKeeper集群管理
Leader選舉過程:
ServerId:服務(wù)器ID
比如有三臺服務(wù)器,編號分別是1,2,3。則編號越大在選擇算法中的權(quán)重就越大
Zxid:數(shù)據(jù)ID
服務(wù)器中存放的數(shù)據(jù)ID越大,值越大說明更新的越頻繁,則在選擇算法中的權(quán)重就越大
在Leader選舉的過程中如果某臺ZooKeeper超過了半數(shù)選票,則直接當(dāng)選為Leader
柚子快報激活碼778899分享:【ZooKeeper學(xué)習(xí)筆記】
文章鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。