您好,登錄后才能下訂單哦!
Zookeeper分布式服務(wù)框架是Apache Hadoop的一個(gè)子項(xiàng)目,主要為分布式系統(tǒng)提供協(xié)調(diào)服務(wù)以及一些數(shù)據(jù)管理問題,如命名服務(wù)、集群管理、分布式應(yīng)用配置等。zookeeper可以將簡(jiǎn)單易用的接口和高效穩(wěn)定的系統(tǒng)提供給用戶。
在大型網(wǎng)站中,zookeeper一直占據(jù)著重要地位,主要功能如下:
zookeeper是為別的分布式程序服務(wù)的
Zookeeper本身就是一個(gè)分布式程序(只要有半數(shù)以上節(jié)點(diǎn)存活,zk就能正常服務(wù))
Zookeeper所提供的服務(wù)涵蓋:主從協(xié)調(diào)、服務(wù)器節(jié)點(diǎn)動(dòng)態(tài)上下線、統(tǒng)一配置管理、分布式共享鎖、統(tǒng)一名稱服務(wù)
雖然說可以提供各種服務(wù),但是zookeeper在底層其實(shí)只提供了兩個(gè)功能:
為用戶程序提供數(shù)據(jù)節(jié)點(diǎn)監(jiān)聽服務(wù);
管理(存儲(chǔ),讀取)用戶程序提交的數(shù)據(jù);
一、zookeeper集群安裝
由于zookeeper的集群投票選主機(jī)制(下面會(huì)介紹),超過半數(shù)的節(jié)點(diǎn)投票才能完成選主。并且必須超過半數(shù)的節(jié)點(diǎn)存活才能提供服務(wù)。所以集群中節(jié)點(diǎn)數(shù)最好為奇數(shù)臺(tái),但不少于3臺(tái)。我們將我們的zookeeper集群安裝到三臺(tái)虛擬機(jī)上。
1.1 環(huán)境準(zhǔn)備
三臺(tái)虛擬機(jī)
192.168.66.101
192.168.66.102
192.168.66.103
JDK安裝包(jdk-7u71-linux-i586.tar.gz)
zookeeper安裝包(zookeeper-3.4.5.tar.gz)
1.2 創(chuàng)建zk用戶
登錄三臺(tái)虛擬機(jī),添加zookeeper的管理用戶,執(zhí)行如下命令添加一個(gè)新用戶,注意必須使用root用戶權(quán)限來添加新用戶,需要在三臺(tái)虛擬機(jī)上都要?jiǎng)?chuàng)建一個(gè)新用戶。
groupadd zkg #添加一個(gè)組
useradd zk -g zkg # 添加一個(gè)用戶,并制定該用戶屬于zkg組
passwd # 給用戶設(shè)置密碼,下面提示輸入密碼,以及確認(rèn)密碼
1.3 安裝
三臺(tái)虛擬機(jī)的用戶創(chuàng)建完成之后,在一臺(tái)虛擬機(jī)上來安裝zookeeper,切換到剛創(chuàng)建的用戶下執(zhí)行如下命令
su zk #切換到zk用戶
cd ~ # 進(jìn)入zk用戶的home目錄
使用ftp工具將jdk安裝包和zookeeper安裝包上傳至任一臺(tái)虛擬機(jī)中zk用戶home目錄下,并解壓至apps目錄下
mkdir apps
tar -zxvf jdk-7u71-linux-i586.tar.gz -C apps/
tar -zxvf zookeeper-3.4.5.tar.gz -C apps/
按照下面的步驟,配置環(huán)境變量,JDK安裝完畢,注意三臺(tái)機(jī)器都需要配置
su # 切換到root用戶
vi /etc/profile # 編輯系統(tǒng)配置文件,將下面三行內(nèi)容粘貼至文件末尾
########################################################
export JAVA_HOME=/home/zk/apps/jdk-7u71-linux-i586
export ZOOKEEPER_HOME=/home/zk/apps/zookeeper-3.4.5
export PATH=$PATH:$ZOOKEEPER_HOME/bin:$JAVA_HOME/bin
#########################################################
source /etc/profile # 使修改后配置文件生效
su zk # 切換為zk用戶
檢查jdk的安裝是否成功,只檢查已有加壓文件的機(jī)器
java -version # 查看jdk版本信息
修改zookeeper的配置文件
cd ~/apps/zookeeper-3.4.5/conf # 進(jìn)入到zookeeper的配置文件存放目錄
cp zoo_sample.cfg zoo.cfg # 將zoo_sample.cfg復(fù)制一份,并更名為zoo.cfg
vi zoo.cfg # 編輯zoo.cfg并將下面內(nèi)容添加到文件末尾
#########################################################
server.1=192.168.66.101:2888:3888 # (主機(jī)名, 心跳端口、數(shù)據(jù)端口)
server.2=192.168.66.102:2888:3888
server.3=192.168.66.103:2888:3888
#########################################################
#########################################################
dataDir=/home/zk/apps/zookeeper-3.4.5/data
dataLogDir=/home/zk/apps/zookeeper-3.4.5/log
#########################################################
:wq #保存并退出
創(chuàng)建data和log目錄,用于存放數(shù)據(jù)和日志信息
cd /home/zk/apps/zookeeper-3.4.5/
mkdir -m 755 data
mkdir -m 755 log
在data文件夾下新建myid文件,myid的文件內(nèi)容為1
cd data/
echo 1 > myid #新建myid文件,并輸入內(nèi)容為1
將配置好的文件目錄發(fā)至其他機(jī)器上
scp -r /home/zk/apps zk@192.168.66.102:/home/zk/ # 通過scp將apps目錄發(fā)至其他機(jī)器,需要輸入密碼
scp -r /home/zk/apps zk@192.168.66.103:/home/zk/
修改其他機(jī)器上的myid
到192.168.66.102上:修改myid為:2
到192.168.66.103上:修改myid為:3
啟動(dòng)每臺(tái)機(jī)器上的zookeeper
zkServer.sh start
查看集群狀態(tài)
jps # 查看進(jìn)程
zkServer.sh status #查看集群狀態(tài),主從信息
二、zookeeper的結(jié)構(gòu)和命令
2.1 zookeeper特性
Zookeeper:一個(gè)leader,多個(gè)follower組成的集群
全局?jǐn)?shù)據(jù)一致:每個(gè)server保存一份相同的數(shù)據(jù)副本,client無論連接到哪個(gè)server,數(shù)據(jù)都是一致的
分布式讀寫,更新請(qǐng)求轉(zhuǎn)發(fā),由leader實(shí)施
更新請(qǐng)求順序進(jìn)行,來自同一個(gè)client的更新請(qǐng)求按其發(fā)送順序依次執(zhí)行
數(shù)據(jù)更新原子性,一次數(shù)據(jù)更新要么成功,要么失敗
實(shí)時(shí)性,在一定時(shí)間范圍內(nèi),client能讀到最新數(shù)據(jù)
2.2 zookeeper數(shù)據(jù)結(jié)構(gòu)
層次化的目錄結(jié)構(gòu),命名符合常規(guī)文件系統(tǒng)規(guī)范(見下圖)
每個(gè)節(jié)點(diǎn)在zookeeper中叫做znode,并且其有一個(gè)唯一的路徑標(biāo)識(shí)
節(jié)點(diǎn)Znode可以包含數(shù)據(jù)和子節(jié)點(diǎn)(但是EPHEMERAL類型的節(jié)點(diǎn)不能有子節(jié)點(diǎn),下一頁詳細(xì)講解)
客戶端應(yīng)用可以在節(jié)點(diǎn)上設(shè)置監(jiān)視器(后續(xù)詳細(xì)講解)
2.3 節(jié)點(diǎn)類型
Znode有兩種類型
短暫(ephemeral)(斷開連接自己刪除)
持久(persistent)(斷開連接不刪除)
Znode有四種形式的目錄節(jié)點(diǎn)(默認(rèn)是persistent )
PERSISTENT
PERSISTENT_SEQUENTIAL(持久序列/test0000000019 )
EPHEMERAL
EPHEMERAL_SEQUENTIAL
創(chuàng)建znode時(shí)設(shè)置順序標(biāo)識(shí),znode名稱后會(huì)附加一個(gè)值,順序號(hào)是一個(gè)單調(diào)遞增的計(jì)數(shù)器,由父節(jié)點(diǎn)維護(hù)
在分布式系統(tǒng)中,順序號(hào)可以被用于為所有的事件進(jìn)行全局排序,這樣客戶端可以通過順序號(hào)推斷事件的順序
2.4 zookeeper命令行操作
運(yùn)行 zkCli.sh –server <ip>進(jìn)入命令行工具
zkCli.sh –server 192.168.66.101
ls / #查看當(dāng)前 ZooKeeper 中所包含的內(nèi)容
create /zk "myData" # 創(chuàng)建一個(gè)新的 znode ,使用 create /zk myData 。這個(gè)命令創(chuàng)建了一個(gè)新的 znode 節(jié)點(diǎn)“ zk ”以及與它關(guān)聯(lián)的字符串
get /zk # 我們運(yùn)行 get 命令來確認(rèn) znode 是否包含我們所創(chuàng)建的字符串
get /zk watch
#監(jiān)聽這個(gè)節(jié)點(diǎn)的變化,當(dāng)另外一個(gè)客戶端改變/zk時(shí),它會(huì)打出下面的
#WATCHER::
#WatchedEvent state:SyncConnected type:NodeDataChanged path:/zk
set /zk "zsl" #通過 set 命令來對(duì) zk 所關(guān)聯(lián)的字符串進(jìn)行設(shè)置
delete /zk # 刪除一個(gè)節(jié)點(diǎn)
rmr /zk # 刪除一個(gè)節(jié)點(diǎn)
2.5 zookeeper Java api的使用
2.5.1 基本使用
org.apache.zookeeper.Zookeeper是客戶端入口主類,負(fù)責(zé)建立與server的會(huì)話
它提供了表 1 所示幾類主要方法
功能
描述
create
在本地目錄樹中創(chuàng)建一個(gè)節(jié)點(diǎn)
delete
刪除一個(gè)節(jié)點(diǎn)
exists
測(cè)試本地是否存在目標(biāo)節(jié)點(diǎn)
get/set data
從目標(biāo)節(jié)點(diǎn)上讀取 / 寫數(shù)據(jù)
get/set ACL
獲取 / 設(shè)置目標(biāo)節(jié)點(diǎn)訪問控制列表信息
get children
檢索一個(gè)子節(jié)點(diǎn)上的列表
sync
等待要被傳送的數(shù)據(jù)
2.5.2 是用Java API實(shí)現(xiàn)簡(jiǎn)單的增刪改查
public class SimpleDemo {
// 會(huì)話超時(shí)時(shí)間,設(shè)置為與系統(tǒng)默認(rèn)時(shí)間一致
private static final int SESSION_TIMEOUT = 30000;
// 創(chuàng)建 ZooKeeper 實(shí)例
ZooKeeper zk;
// 創(chuàng)建 Watcher 實(shí)例
Watcher wh = new Watcher() {
public void process(org.apache.zookeeper.WatchedEvent event)
{
System.out.println(event.toString());
}
};
// 初始化 ZooKeeper 實(shí)例
private void createZKInstance() throws IOException
{
zk = new ZooKeeper("weekend01:2181", SimpleDemo.SESSION_TIMEOUT, this.wh);
}
private void ZKOperations() throws IOException, InterruptedException, KeeperException
{
System.out.println("/n1. 創(chuàng)建 ZooKeeper 節(jié)點(diǎn) (znode : zoo2, 數(shù)據(jù): myData2 ,權(quán)限: OPEN_ACL_UNSAFE ,節(jié)點(diǎn)類型: Persistent");
zk.create("/zoo2", "myData2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("/n2. 查看是否創(chuàng)建成功: ");
System.out.println(new String(zk.getData("/zoo2", false, null)));
System.out.println("/n3. 修改節(jié)點(diǎn)數(shù)據(jù) ");
zk.setData("/zoo2", "shenlan211314".getBytes(), -1);
System.out.println("/n4. 查看是否修改成功: ");
System.out.println(new String(zk.getData("/zoo2", false, null)));
System.out.println("/n5. 刪除節(jié)點(diǎn) ");
zk.delete("/zoo2", -1);
System.out.println("/n6. 查看節(jié)點(diǎn)是否被刪除: ");
System.out.println(" 節(jié)點(diǎn)狀態(tài): [" + zk.exists("/zoo2", false) + "]");
}
private void ZKClose() throws InterruptedException
{
zk.close();
}
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
SimpleDemo dm = new SimpleDemo();
dm.createZKInstance();
dm.ZKOperations();
dm.ZKClose();
}
}
2.5.3 Zookeeper的監(jiān)聽器工作機(jī)制
監(jiān)聽器是一個(gè)接口,我們的代碼中可以實(shí)現(xiàn)Wather這個(gè)接口,實(shí)現(xiàn)其中的process方法,方法中即我們自己的業(yè)務(wù)邏輯
監(jiān)聽器的注冊(cè)是在獲取數(shù)據(jù)的操作中實(shí)現(xiàn):
getData(path,watch?)監(jiān)聽的事件是:節(jié)點(diǎn)數(shù)據(jù)變化事件
getChildren(path,watch?)監(jiān)聽的事件是:節(jié)點(diǎn)下的子節(jié)點(diǎn)增減變化事件
三、zookeeper的應(yīng)用案例(分布式應(yīng)用HA||分布式鎖)
3.1 實(shí)現(xiàn)分布式應(yīng)用的(主節(jié)點(diǎn)HA)及客戶端動(dòng)態(tài)更新主節(jié)點(diǎn)狀態(tài)
某分布式系統(tǒng)中,主節(jié)點(diǎn)可以有多臺(tái),可以動(dòng)態(tài)上下線
任意一臺(tái)客戶端都能實(shí)時(shí)感知到主節(jié)點(diǎn)服務(wù)器的上下線
A、客戶端實(shí)現(xiàn)
public class AppClient {
private String groupNode = "sgroup";
private ZooKeeper zk;
private Stat stat = new Stat();
private volatile List<String> serverList;
/**
連接zookeeper
*/
public void connectZookeeper() throws Exception {
zk
= new ZooKeeper("localhost:4180,localhost:4181,localhost:4182", 5000, new Watcher() {
public void process(WatchedEvent event) {
// 如果發(fā)生了"/sgroup"節(jié)點(diǎn)下的子節(jié)點(diǎn)變化事件, 更新server列表, 并重新注冊(cè)監(jiān)聽
if (event.getType() == EventType.NodeChildrenChanged
&& ("/" + groupNode).equals(event.getPath())) {
try {
updateServerList();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
updateServerList();
}
/**
更新server列表
*/
private void updateServerList() throws Exception {
List<String> newServerList = new ArrayList<String>();
// 獲取并監(jiān)聽groupNode的子節(jié)點(diǎn)變化
// watch參數(shù)為true, 表示監(jiān)聽子節(jié)點(diǎn)變化事件.
// 每次都需要重新注冊(cè)監(jiān)聽, 因?yàn)橐淮巫?cè), 只能監(jiān)聽一次事件, 如果還想繼續(xù)保持監(jiān)聽, 必須重新注冊(cè)
List<String> subList = zk.getChildren("/" + groupNode, true);
for (String subNode : subList) {
// 獲取每個(gè)子節(jié)點(diǎn)下關(guān)聯(lián)的server地址
byte[] data = zk.getData("/" + groupNode + "/" + subNode, false, stat);
newServerList.add(new String(data, "utf-8"));
}
// 替換server列表
serverList = newServerList;
System.out.println("server list updated: " + serverList);
}
/**
client的工作邏輯寫在這個(gè)方法中
此處不做任何處理, 只讓client sleep
*/
public void handle() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
AppClient ac = new AppClient();
ac.connectZookeeper();
ac.handle();
}
}
B、服務(wù)器端實(shí)現(xiàn)
public class AppServer {
private String groupNode = "sgroup";
private String subNode = "sub";
/**
連接zookeeper
@param address server的地址
*/
public void connectZookeeper(String address) throws Exception {
ZooKeeper zk = new ZooKeeper(
"localhost:4180,localhost:4181,localhost:4182",
5000, new Watcher() {
public void process(WatchedEvent event) {
// 不做處理
}
});
// 在"/sgroup"下創(chuàng)建子節(jié)點(diǎn)
// 子節(jié)點(diǎn)的類型設(shè)置為EPHEMERAL_SEQUENTIAL, 表明這是一個(gè)臨時(shí)節(jié)點(diǎn), 且在子節(jié)點(diǎn)的名稱后面加上一串?dāng)?shù)字后綴
// 將server的地址數(shù)據(jù)關(guān)聯(lián)到新創(chuàng)建的子節(jié)點(diǎn)上
String createdPath = zk.create("/" + groupNode + "/" + subNode, address.getBytes("utf-8"),
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("create: " + createdPath);
}
/**
server的工作邏輯寫在這個(gè)方法中
此處不做任何處理, 只讓server sleep
*/
public void handle() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
// 在參數(shù)中指定server的地址
if (args.length == 0) {
System.err.println("The first argument must be server address");
System.exit(1);
}
AppServer as = new AppServer();
as.connectZookeeper(args[0]);
as.handle();
}
}
3.2 分布式共享鎖的簡(jiǎn)單實(shí)現(xiàn)
客戶端A
public class DistributedClient {
// 超時(shí)時(shí)間
private static final int SESSION_TIMEOUT = 5000;
// zookeeper server列表
private String hosts = "localhost:4180,localhost:4181,localhost:4182";
private String groupNode = "locks";
private String subNode = "sub";
private ZooKeeper zk;
// 當(dāng)前client創(chuàng)建的子節(jié)點(diǎn)
private String thisPath;
// 當(dāng)前client等待的子節(jié)點(diǎn)
private String waitPath;
private CountDownLatch latch = new CountDownLatch(1);
/**
連接zookeeper
*/
public void connectZookeeper() throws Exception {
zk = new ZooKeeper(hosts, SESSION_TIMEOUT, new Watcher() {
public void process(WatchedEvent event) {
try {
// 連接建立時(shí), 打開latch, 喚醒wait在該latch上的線程
if (event.getState() == KeeperState.SyncConnected) {
latch.countDown();
}
// 發(fā)生了waitPath的刪除事件
if (event.getType() == EventType.NodeDeleted && event.getPath().equals(waitPath)) {
doSomething();
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 等待連接建立
latch.await();
// 創(chuàng)建子節(jié)點(diǎn)
thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
// wait一小會(huì), 讓結(jié)果更清晰一些
Thread.sleep(10);
// 注意, 沒有必要監(jiān)聽"/locks"的子節(jié)點(diǎn)的變化情況
List<String> childrenNodes = zk.getChildren("/" + groupNode, false);
// 列表中只有一個(gè)子節(jié)點(diǎn), 那肯定就是thisPath, 說明client獲得鎖
if (childrenNodes.size() == 1) {
doSomething();
} else {
String thisNode = thisPath.substring(("/" + groupNode + "/").length());
// 排序
Collections.sort(childrenNodes);
int index = childrenNodes.indexOf(thisNode);
if (index == -1) {
// never happened
} else if (index == 0) {
// inddx == 0, 說明thisNode在列表中最小, 當(dāng)前client獲得鎖
doSomething();
} else {
// 獲得排名比thisPath前1位的節(jié)點(diǎn)
this.waitPath = "/" + groupNode + "/" + childrenNodes.get(index - 1);
// 在waitPath上注冊(cè)監(jiān)聽器, 當(dāng)waitPath被刪除時(shí), zookeeper會(huì)回調(diào)監(jiān)聽器的process方法
zk.getData(waitPath, true, new Stat());
}
}
}
private void doSomething() throws Exception {
try {
System.out.println("gain lock: " + thisPath);
Thread.sleep(2000);
// do something
} finally {
System.out.println("finished: " + thisPath);
// 將thisPath刪除, 監(jiān)聽thisPath的client將獲得通知
// 相當(dāng)于釋放鎖
zk.delete(this.thisPath, -1);
}
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
new Thread() {
public void run() {
try {
DistributedClient dl = new DistributedClient();
dl.connectZookeeper();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
Thread.sleep(Long.MAX_VALUE);
}
}
分布式多進(jìn)程模式實(shí)現(xiàn)
public class DistributedClientMy {
// 超時(shí)時(shí)間
private static final int SESSION_TIMEOUT = 5000;
// zookeeper server列表
private String hosts = "spark01:2181,spark02:2181,spark03:2181";
private String groupNode = "locks";
private String subNode = "sub";
private boolean haveLock = false;
private ZooKeeper zk;
// 當(dāng)前client創(chuàng)建的子節(jié)點(diǎn)
private volatile String thisPath;
/**
連接zookeeper
*/
public void connectZookeeper() throws Exception {
zk = new ZooKeeper("spark01:2181", SESSION_TIMEOUT, new Watcher() {
public void process(WatchedEvent event) {
try {
// 子節(jié)點(diǎn)發(fā)生變化
if (event.getType() == EventType.NodeChildrenChanged && event.getPath().equals("/" + groupNode)) {
// thisPath是否是列表中的最小節(jié)點(diǎn)
List<String> childrenNodes = zk.getChildren("/" + groupNode, true);
String thisNode = thisPath.substring(("/" + groupNode + "/").length());
// 排序
Collections.sort(childrenNodes);
if (childrenNodes.indexOf(thisNode) == 0) {
doSomething();
thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 創(chuàng)建子節(jié)點(diǎn)
thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
// wait一小會(huì), 讓結(jié)果更清晰一些
Thread.sleep(new Random().nextInt(1000));
// 監(jiān)聽子節(jié)點(diǎn)的變化
List<String> childrenNodes = zk.getChildren("/" + groupNode, true);
// 列表中只有一個(gè)子節(jié)點(diǎn), 那肯定就是thisPath, 說明client獲得鎖
if (childrenNodes.size() == 1) {
doSomething();
thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
}
}
/**
共享資源的訪問邏輯寫在這個(gè)方法中
*/
private void doSomething() throws Exception {
try {
System.out.println("gain lock: " + thisPath);
Thread.sleep(2000);
// do something
} finally {
System.out.println("finished: " + thisPath);
// 將thisPath刪除, 監(jiān)聽thisPath的client將獲得通知
// 相當(dāng)于釋放鎖
zk.delete(this.thisPath, -1);
}
}
public static void main(String[] args) throws Exception {
DistributedClientMy dl = new DistributedClientMy();
dl.connectZookeeper();
Thread.sleep(Long.MAX_VALUE);
}
}
四、zookeeper的選舉機(jī)制(全新集群paxos)
以一個(gè)簡(jiǎn)單的例子來說明整個(gè)選舉的過程。
假設(shè)有五臺(tái)服務(wù)器組成的zookeeper集群,它們的id從1-5,同時(shí)它們都是最新啟動(dòng)的,也就是沒有歷史數(shù)據(jù),在存放數(shù)據(jù)量這一點(diǎn)上,都是一樣的.假設(shè)這些服務(wù)器依序啟動(dòng),來看看會(huì)發(fā)生什么。
服務(wù)器1啟動(dòng),此時(shí)只有它一臺(tái)服務(wù)器啟動(dòng)了,它發(fā)出去的報(bào)沒有任何響應(yīng),所以它的選舉狀態(tài)一直是LOOKING狀態(tài)
服務(wù)器2啟動(dòng),它與最開始啟動(dòng)的服務(wù)器1進(jìn)行通信,互相交換自己的選舉結(jié)果,由于兩者都沒有歷史數(shù)據(jù),所以id值較大的服務(wù)器2勝出,但是由于沒有達(dá)到超過半數(shù)以上的服務(wù)器都同意選舉它(這個(gè)例子中的半數(shù)以上是3),所以服務(wù)器1,2還是繼續(xù)保持LOOKING狀態(tài).
服務(wù)器3啟動(dòng),根據(jù)前面的理論分析,服務(wù)器3成為服務(wù)器1,2,3中的老大,而與上面不同的是,此時(shí)有三臺(tái)服務(wù)器選舉了它,所以它成為了這次選舉的leader.
服務(wù)器4啟動(dòng),根據(jù)前面的分析,理論上服務(wù)器4應(yīng)該是服務(wù)器1,2,3,4中最大的,但是由于前面已經(jīng)有半數(shù)以上的服務(wù)器選舉了服務(wù)器3,所以它只能接收當(dāng)小弟的命了.
服務(wù)器5啟動(dòng),同4一樣,當(dāng)小弟
五、非全新集群的選舉機(jī)制(數(shù)據(jù)恢復(fù))
那么,初始化的時(shí)候,是按照上述的說明進(jìn)行選舉的,但是當(dāng)zookeeper運(yùn)行了一段時(shí)間之后,有機(jī)器down掉,重新選舉時(shí),選舉過程就相對(duì)復(fù)雜了。
需要加入數(shù)據(jù)id、leader id和邏輯時(shí)鐘。
數(shù)據(jù)id:數(shù)據(jù)新的id就大,數(shù)據(jù)每次更新都會(huì)更新id。
Leader id:就是我們配置的myid中的值,每個(gè)機(jī)器一個(gè)。
邏輯時(shí)鐘:這個(gè)值從0開始遞增,每次選舉對(duì)應(yīng)一個(gè)值,也就是說: 如果在同一次選舉中,那么這個(gè)值應(yīng)該是一致的 ; 邏輯時(shí)鐘值越大,說明這一次選舉leader的進(jìn)程更新.
選舉的標(biāo)準(zhǔn)就變成:
1、邏輯時(shí)鐘小的選舉結(jié)果被忽略,重新投票
2、統(tǒng)一邏輯時(shí)鐘后,數(shù)據(jù)id大的勝出
3、數(shù)據(jù)id相同的情況下,leader id大的勝出
根據(jù)這個(gè)規(guī)則選出leader。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。