ZooKeeper介紹
- Zookeeper 分布式服務框架是 Apache Hadoop 的一個子項目,它主要是用來解決分布式應用中經常遇到的一些數據管理問題,如:統一命名服務、狀態同步服務、集群管理、分布式應用配置項的管理等。本文將從使用者角度詳細介紹 Zookeeper 的安裝和配置文件中各個配置項的意義,以及分析 Zookeeper 的典型的應用場景(配置文件的管理、集群管理、同步鎖、Leader 選舉、隊列管理等)。
- 簡單說下我的理解:
- ZooKeeper有三種工作模式:
Standalone
:單點模式,有單點故障問題。偽分布式
:在一臺機器同時運行多個ZooKeeper實例,仍然有單點故障問題,當然,其中配置的端口號要錯開的,適合實驗環境模擬集群使用。完全分布式
:在多臺機器上部署ZooKeeper集群,適合線上環境使用。
- ZooKeeper到底是什么:
三種端口號
:這里先說明三個ZooKeeper配置需要的端口號,因為后面的解釋中會經常引用,就拉到前面講啦
- 端口X:客戶端連接ZooKeeper集群使用的監聽端口號
- 端口Y:leader和follower之間數據同步使用的端口號
- 端口Z:leader選舉專用的端口號
單點分析
:在每個ZooKeeper節點當中,ZooKeeper維護了一個類似linux的樹狀文件系統結構,可以把一些配置信息,數據等存放到ZooKeeper當中,也可以把ZooKeeper當中的一個目錄節點當做一個鎖的互斥文件來實現并發安全控制,你看到這就先把ZooKeeper理解為一個在操作系統上運行的一個虛擬文件系統,只不過他不是像HDFS那樣真的用來存放文件的,他是利用文件系統的節點作為底層實現來提供分布式環境很常用的功能,這在后面的實戰使用
會具體講解.集群分析
:ZooKeeper中的每個節點都是上面的單點分析
那樣工作的,但是集群多節點之間到底如何進行協商和通信呢?
- 首先,類似mysql讀寫分離那樣,ZooKeeper的每個節點都存放相同的數據,因此訪問ZooKeeper的時候會被分流道各個節點實現高并發,多節點也順便實現了高可用。
- ZooKeeper的節點之間也有主次關系,集群啟動完成之后,ZooKeeper會運行選舉程序(端口Z)從集群中選擇一個leader節點,而其他的節點就是follower節點,對于ZooKeeper的寫操作,會被轉發到leader節點,而follower節點和leader節點的數據同步(端口Y)也在后臺自動實現,讀操作則每個節點都能提供,負載均衡~
容災機制
:follower節點掛了比較不要緊,有leader協調,整個集群還不至于發生致命影響。但是leader節點要是掛了,就群龍無首了,但是其實這個狀態和ZooKeeper集群啟動前期還沒確定leader節點的時候是一樣的狀態,下面講解這個狀態如何進行leader的 選舉。
由于每個節點配置文件中都維護了整個ZooKeeper集群的所有節點列表,因此在沒確定leader節點或者leader節點掛掉的時候,每個節點向leader的通信必然是失敗的,follower節點就是這么發現leader節點掛了的,這個時候他就能啟動選舉程序進行leader競選,和其他的所有follower節點進行通信,根據某種算法方案確定leader的節點之后,被選中的節點就啟動leader的程序,化身leader重新領導整個集群。
- 客戶端訪問高可用:
讀操作
:每個節點都能響應寫操作
:不管向哪個節點請求都會轉發到leader節點執行,再把數據同步到各個follower節點。訪問follower節點宕機
:客戶端會保存一個地址列表,會自動使用另一個地址進行訪問,實在不行還有leader節點分配地址再次訪問呢,而且一旦客戶端和服務器的連接被斷開,客戶端就會自動去連接另一個節點的地址進行請求。訪問leader節點宕機
:也是使用這個地址列表,如果是讀操作,則leader選舉出來之前都能訪問,但是如果是寫操作,就要等leader選舉完成之后才能進行操作。- 我之前有疑問的地方就是以為客戶端只維護一個節點的地址,這樣的話導致了這個節點宕機之后客戶端就和ZooKeeper集群斷開了且無法重新連接了,但是后來知道了客戶端是保存了節點地址列表,那所有問題就很好理解啦。
- 好了,說了很多廢話你可能有點暈了,但是總說一點,別忘了,現在先把他當成一個樹形文件系統~
ZooKeeper環境部署和和簡單測試
- ZooKeeper下載:http://apache.fayea.com/zookeeper/
- 官網文檔:https://zookeeper.apache.org/doc/r3.4.9/
- 安裝準備:
- JDK
- hosts
- 防火墻
- SELinux
- ssh免密碼登錄
- 等等這些基本配置前面的Hadoop教程說過了,不再贅述,這些問題我相信都是很容易解決的。
- 安裝步驟總結:
(1) 創建緩存目錄;
(2) 配置zookeeper配置文件zoo.cfg,設置緩存目錄、監聽客戶端端口號、server列表配置等
(3) bin目錄下的zkServer.sh啟動即可,還能查看集群中的leader、follower之間的關系
- 單機模式:
- 創建緩存目錄;
- 解壓ZooKeeper安裝包,在conf目錄下的zoo_sample.cfg文件復制一個副本zoo.cfg,在里面進行ZooKeeper整體配置:
tickTime=2000
dataDir=/opt/zookeeper1
clientPort=2181
tickTime
:這個時間是作為 Zookeeper 服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會發送一個心跳。
dataDir
:顧名思義就是 Zookeeper 保存數據的目錄,默認情況下,Zookeeper 將寫數據的日志文件也保存在這個目錄里。
clientPort
:這個端口就是客戶端連接 Zookeeper 服務器的端口,Zookeeper 會監聽這個端口,接受客戶端的訪問請求。
- bin目錄下的zkServer.sh執行即可啟動:
bin/zkServer.sh start
- 可以查看ZooKeeper的執行狀態:
bin/zkServer.sh status
- 集群模式:
- 創建緩存目錄;
- 解壓ZooKeeper安裝包,在conf目錄下的zoo_sample.cfg文件復制一個副本zoo.cfg,在里面進行ZooKeeper整體配置:
tickTime=2000
dataDir=/opt/zookeeper1
clientPort=2181
initLimit=5
syncLimit=2
server.1=192.168.211.1:2888:3888
server.2=192.168.211.2:2888:3888
上面前三個的配置和單機模式一樣,不多贅述了。
initLimit
:這個配置項是用來配置 Zookeeper 接受客戶端(這里所說的客戶端不是用戶連接 Zookeeper 服務器的客戶端,而是 Zookeeper 服務器集群中連接到 Leader 的 Follower 服務器)初始化連接時最長能忍受多少個心跳時間間隔數。當已經超過 10 個心跳的時間(也就是 tickTime)長度后 Zookeeper 服務器還沒有收到客戶端的返回信息,那么表明這個客戶端連接失敗。總的時間長度就是 52000=10 秒
syncLimit
:這個配置項標識 Leader 與 Follower 之間發送消息,請求和應答時間長度,最長不能超過多少個 tickTime 的時間長度,總的時間長度就是 22000=4 秒
server.A=B:C:D
:其中 A 是一個數字,表示這個是第幾號服務器;B 是這個服務器的 ip 地址;C 表示的是這個服務器與集群中的 Leader 服務器交換信息的端口(上面的端口Y);D 表示的是萬一集群中的 Leader 服務器掛了,需要一個端口來重新進行選舉,選出一個新的 Leader,而這個端口就是用來執行選舉時服務器相互通信的端口(上面的端口Z)。如果是偽集群的配置方式,由于 B 都是一樣,所以不同的 Zookeeper 實例通信端口號不能一樣,所以要給它們分配不同的端口號。
- 在上面配置的dataDir目錄下創建myid文件,填寫這個節點上的id號,就是
server.A=B:C:D
配置的A那個號碼
[root@VM_68_145_centos zookeeper]# cat /opt/zookeeper1/myid
1
[root@VM_68_145_centos zookeeper]# cat /opt/zookeeper2/myid
2
[root@VM_68_145_centos zookeeper]# cat /opt/zookeeper3/myid
3
[root@VM_68_145_centos zookeeper]# cat /opt/zookeeper4/myid
4
>* 將配置好的整個安裝包復制到每個節點機器上,scp命令復制過去,我這里是采用偽分布式,直接使用cp命令即可。
>* 到每個節點上zookeeper/bin目錄下的zkServer.sh執行即可啟動:
```bash
bin/zkServer.sh start
其他腳本介紹:
zkServer.sh : ZooKeeper服務器的啟動、停止和重啟腳本;
zkCli.sh : ZooKeeper的簡易客戶端;
zkEnv.sh : 設置ZooKeeper的環境變量;
zkCleanup.sh : 清理ZooKeeper歷史數據,包括事務日志文件和快照數據文件。
- 可以查看ZooKeeper的執行狀態:
zookeeper/bin/zkServer.sh status
- 簡單說明這樣的配置ZooKeeper集群是怎么工作的:
- 首先,每個節點上zoo.cfg配置文件中都有整個集群的列表,所以每個節點之間的通信都是可行的。
- 然后是dataDir目錄下的myid標記了這個機器上的這個節點是zoo.cfg上的集群列表的哪個記錄,從這個就能知道當前的這個節點所處的位置,也能知道當前機器的節點是不是leader,以便于執行leader該執行的程序。
- 關于配置的三個端口號:
- 端口X:監聽客戶端連接的,沒什么可說的
- 端口Y:follower和leader進行數據同步通信用的,這個是長連接隨時同步數據,健康情況下正常運行,leader宕機就無法正常執行,此時觸發選舉程序選擇新的leader。
- 端口Z:選舉時各個follower節點之間兩兩可以相互通信的,以便于成功選擇出leader。
踩坑記錄
:我剛開始以為端口X和端口Y是同一個,導致了我就設置了同一個端口號,所以顯然是無法啟動成功的,后來知道這兩個端口號完成的是不同的功能,所以改正完成就能啟動成功了。
- 可以創建一個啟動腳本一次性啟動整個集群,比如我搭建的偽分布式的啟動腳本:
#! /bin/sh
/usr/java/zookeeper/zookeeper1/bin/zkServer.sh start
/usr/java/zookeeper/zookeeper2/bin/zkServer.sh start
/usr/java/zookeeper/zookeeper3/bin/zkServer.sh start
/usr/java/zookeeper/zookeeper4/bin/zkServer.sh start
/usr/java/zookeeper/zookeeper1/bin/zkServer.sh status
/usr/java/zookeeper/zookeeper2/bin/zkServer.sh status
/usr/java/zookeeper/zookeeper3/bin/zkServer.sh status
/usr/java/zookeeper/zookeeper4/bin/zkServer.sh status
echo 'you can use zookeeper client connect to 119.29.153.56 on follow ports:'
echo '2181'
echo '2182'
echo '2183'
echo '2184'
各個節點的啟動狀態:
[root@VM_68_145_centos zookeeper]# /usr/java/zookeeper/zookeeper1/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/java/zookeeper/zookeeper1/bin/../conf/zoo.cfg
Mode: follower
[root@VM_68_145_centos zookeeper]# /usr/java/zookeeper/zookeeper2/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/java/zookeeper/zookeeper2/bin/../conf/zoo.cfg
Mode: follower
[root@VM_68_145_centos zookeeper]# /usr/java/zookeeper/zookeeper3/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/java/zookeeper/zookeeper3/bin/../conf/zoo.cfg
Mode: leader
[root@VM_68_145_centos zookeeper]# /usr/java/zookeeper/zookeeper4/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/java/zookeeper/zookeeper4/bin/../conf/zoo.cfg
Mode: follower
啟動完成之后順便查看了每個節點充當的是leader還是follower,并把集群的所有客戶端連接端口號輸出出來,從上面的執行結果可知,zookeeper3的這個實例是leader,其他實例是follower。
- ZooKeeper簡單操作測試:
- 命令行方式操縱ZooKeeper:
進入本地的ZooKeeper:
/usr/java/zookeeper/zookeeper1/bin/zkCli.sh
進入遠程的ZooKeeper:
/usr/java/zookeeper/zookeeper1/bin/zkCli.sh -server {ip}:{port}
> 這樣就進入了ZooKeeper的命令行客戶端,就能訪問指定的ZooKeeper集群中的數據,支持以下的操作(在命令行下輸入錯誤命令就能看到提示了。。。):
>```bash
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port
- 操作示例:
創建節點/testInput,節點中的數值為inputData
create /testInput "inputData"
查詢剛才插入的節點
get /testInput
運行結果
inputData
cZxid = 0x300000006
ctime = Sun Dec 18 21:47:21 CST 2016
mZxid = 0x300000006
mtime = Sun Dec 18 21:47:21 CST 2016
pZxid = 0x300000006
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0
> 所以整個操縱的過程就和平時操作linux文件的感覺差不多,下面的Java程序操縱其實也只是對這些命令行的封裝,下面也給出一些示例。
9. Java客戶端操縱ZooKeeper:
>* 這里也只簡單示例一些基本操作:
>```java
public class ZooKeeperClient {
//同步互斥變量,用來阻塞等待ZooKeeper連接完成之后再進行ZooKeeper的操作命令
private static CountDownLatch connectedSemaphore = new CountDownLatch( 1 );
public static void main(String[] args) throws Exception {
// 創建一個與服務器的連接
ZooKeeper zk = new ZooKeeper("119.29.153.56:2181",3000, new Watcher() {//這個是服務器連接完成回調的監聽器
// 監控所有被觸發的事件
public void process(WatchedEvent event) {
System.out.println("已經觸發了" + event.getType() + "事件!");
if ( Event.KeeperState.SyncConnected == event.getState() ) {
//連接完成的同步事件,互斥變量取消,下面的阻塞停止,程序繼續執行
connectedSemaphore.countDown();
}
}
});
//如果和ZooKeeper服務器的TCP連接還沒完全建立,就阻塞等待
connectedSemaphore.await();
// 創建一個目錄節點
zk.create("/testRootPath", "testRootData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
// 創建一個子目錄節點
zk.create("/testRootPath/testChildPathOne", "testChildDataOne".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
System.out.println(new String(zk.getData("/testRootPath",false,null)));
// 取出子目錄節點列表
System.out.println(zk.getChildren("/testRootPath",true));
// 修改子目錄節點數據
zk.setData("/testRootPath/testChildPathOne","modifyChildDataOne".getBytes(),-1);
System.out.println("目錄節點狀態:["+zk.exists("/testRootPath",true)+"]");
// 創建另外一個子目錄節點
zk.create("/testRootPath/testChildPathTwo", "testChildDataTwo".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
System.out.println(new String(zk.getData("/testRootPath/testChildPathTwo",true,null)));
// 刪除子目錄節點
zk.delete("/testRootPath/testChildPathTwo",-1);
zk.delete("/testRootPath/testChildPathOne",-1);
// 刪除父目錄節點
zk.delete("/testRootPath",-1);
// 關閉連接
zk.close();
}
}
輸出結果:
已經觸發了None事件!
testRootData
[testChildPathOne]
目錄節點狀態:[17179869200,17179869200,1482114759242,1482114759242,0,1,0,0,12,1,17179869201
]
已經觸發了NodeChildrenChanged事件!
testChildDataTwo
已經觸發了NodeDeleted事件!
已經觸發了NodeDeleted事件!
解釋
:
第一個添加節點/testRootPath,觸發None事件,之后再獲取這個節點數據、其子節點列表、節點狀態
接下來創建第二個子節點/testRootPath/testChildPathTwo,觸發NodeChildrenChanged事件
然后刪除兩個子節點,觸發兩次NodeDeleted事件
- 常用接口介紹:
IBM教程
這邊已經有部分接口的說明,可以拿來當做初步認識,在比較下面,往下翻哦~。- 幾個可能的疑問:
ACL
:這個是ZooKeeper本身提供的簡單的權限控制模型,有一些簡單的權限控制策略,可以稍做了解。
OP
:面向對象的思想嘛,一個ZooKeeper的操作命令也抽象為一個操作對象,不過它只是個抽象類,具體的實現有Delete、Check等子類才是實際的具體操作對象。
CreateMode
:創建節點的模式枚舉,四個成員分別是:PERSISTENT(持久化)、PERSISTENT_SEQUENTIAL(持久化并序號自增)、EPHEMERAL(臨時,當前session有效)、EPHEMERAL_SEQUENTIAL(臨時,當前session有效,序號自增)
ZooDefs.Ids
:提供了上面ACL
的常用的權限策略常量列表。
Event.KeeperState
:ZooKeeper的事件類型狀態常量列表。- 有了上面的就基本對ZooKeeper的API有了初步的認識,剩下的問題看官方文檔就很好理解了:http://people.apache.org/~larsgeorge/zookeeper-1215258/build/docs/dev-api/
特別注意:ZooKeeper是支持
Watcher監聽
的,你可以用它監聽某個節點的值是否存在、是否改變、是否被刪除等之類的動作,當他觸發了相關的動作就會進行回調
的,有點是異步編程,也是特別棒的東西。到這里入門就告一段落了,接下來就是實際應用場景的分析啦~
實戰使用
-
統一命名服務(Name Service)
:在ZooKeeper的樹形結構下你可以創建一個統一的不重復的命名,比如create創建一個節點即可,再創建一個相同名稱的節點是不允許的。 -
配置管理(Configuration Management)
:意思就是分布式應用的配置可以交給ZooKeeper來管理,不然一旦修改配置,就得每臺機器上的配置都做相應的修改,如果交給ZooKeeper管理的話,只需要修改ZooKeeper上的節點值即可。 -
集群管理(Group Membership)
:
- Zookeeper 不僅能夠幫你維護當前的集群中機器的服務狀態,而且能夠幫你選出一個“總管”,讓這個總管來管理集群,這就是 Zookeeper 的另一個功能 Leader Election。
- 它們的實現方式都是在 Zookeeper上創建一個 EPHEMERAL 類型的目錄節點,然后每個 Server 在它們創建目錄節點的父目錄節點上調用 getChildren(String path, boolean watch) 方法并設置 watch 為 true,由于是 EPHEMERAL 目錄節點,當創建它的 Server 死去,這個目錄節點也隨之被刪除,所以 Children 將會變化,這時 getChildren上的 Watch 將會被調用,所以其它 Server 就知道已經有某臺 Server 死去了。新增 Server 也是同樣的原理。
- Zookeeper 如何實現 Leader Election,也就是選出一個 Master Server。和前面的一樣每臺 Server 創建一個 EPHEMERAL 目錄節點,不同的是它還是一個 SEQUENTIAL 目錄節點,所以它是個 EPHEMERAL_SEQUENTIAL 目錄節點。之所以它是 EPHEMERAL_SEQUENTIAL 目錄節點,是因為我們可以給每臺 Server 編號,我們可以選擇當前是最小編號的 Server 為 Master,假如這個最小編號的 Server 死去,由于是 EPHEMERAL 節點,死去的 Server 對應的節點也被刪除,所以當前的節點列表中又出現一個最小編號的節點,我們就選擇這個節點為當前 Master。這樣就實現了動態選擇 Master,避免了傳統意義上單 Master 容易出現單點故障的問題。
-
共享鎖(Locks)
:共享鎖在同一個進程中很容易實現,但是在跨進程或者在不同 Server 之間就不好實現了。Zookeeper 卻很容易實現這個功能,實現方式也是需要獲得鎖的 Server 創建一個 EPHEMERAL_SEQUENTIAL 目錄節點,然后調用 getChildren方法獲取當前的目錄節點列表中最小的目錄節點是不是就是自己創建的目錄節點,如果正是自己創建的,那么它就獲得了這個鎖,如果不是那么它就調用 exists(String path, boolean watch) 方法并監控 Zookeeper 上目錄節點列表的變化,一直到自己創建的節點是列表中最小編號的目錄節點,從而獲得鎖,釋放鎖很簡單,只要刪除前面它自己所創建的目錄節點就行了。 -
隊列管理
:
同步隊列
:
當一個隊列的成員都聚齊時,這個隊列才可用,否則一直等待所有成員到達,這種是同步隊列。
隊列按照 FIFO 方式進行入隊和出隊操作,例如實現生產者和消費者模型。
同步隊列用 Zookeeper 實現的實現思路如下:
創建一個父目錄 /synchronizing,每個成員都監控標志(Set Watch)位目錄 /synchronizing/start 是否存在,然后每個成員都加入這個隊列,加入隊列的方式就是創建 /synchronizing/member_i 的臨時目錄節點,然后每個成員獲取 / synchronizing 目錄的所有目錄節點,也就是 member_i。判斷 i 的值是否已經是成員的個數,如果小于成員個數等待 /synchronizing/start 的出現,如果已經相等就創建 /synchronizing/start。FIFO隊列
:
實現的思路也非常簡單,就是在特定的目錄下創建 SEQUENTIAL 類型的子目錄 /queue_i,這樣就能保證所有成員加入隊列時都是有編號的,出隊列時通過 getChildren( ) 方法可以返回當前所有的隊列中的元素,然后消費其中最小的一個,這樣就能保證 FIFO。
-
可見ZooKeeper本身的樹形目錄結構以及其提供的對目錄節點的
監控Watcher
,提供了實時數據同步以及可客戶端的及時通知。并且利用節點之間的變化觸發的事件類型
可以很方便地設計很多實際應用場景的算法需求。具體的很多說明和算法示例代碼在IBM教程中有詳細的講解。
常用分布式服務框架和ZooKeeper的依賴講解
- 之前的
Hadoop 2.x
高可用配置使用了ZooKeeper,但是我根本不知道這個外部添加的ZooKeeper工具是怎么被Hadoop調用的,當時只是知道按照教程一步步下來就能成功運行,不過現在好好把ZooKeeper研究完了,就該來好好回顧一下這個問題了。- 這個是之前的Hadoop 2.x的高可用安裝配置教程:http://www.coselding.cn/article/2016-05-31/hadoop2-high-available.html
- 具體的過程就不在這里重復說明了,只挑要點講解:
- Hadoop 2.x啟動前要求先把ZooKeeper配置好并啟動起來,這個時候ZooKeeper還是獨立運行的。
- Hadoop 2.x配置完成之后在啟動步驟中有一步:
hdfs zkfc -formatZK
> 這步干嘛的?這個時候Hadoop中的多個NameNode節點都已經配置好了,這個步驟就是把NameNode的信息注冊到ZooKeeper當中,包括哪個是Active,有哪些Standby,都在ZooKeeper當中進行注冊記錄,并且有一個`ZKFC`進程負責觀察NameNode的狀態,如果有NameNode宕機了,就馬上通知ZooKeeper進行相應的記錄修改,也就是說,ZooKeeper當中實時存放著NameNode的節點列表以及哪個是Active。(這部分的實時記錄和更新代碼存在ZKFC當中,是Hadoop本身就已經實現的代碼,不需要我們自己編寫,配置好就行)。
>3. Hadoop怎么知道ZooKeeper的存在?`hdfs-site.xml`中不是配置了`dfs.nameservices`這個屬性嗎,這個屬性就是告訴Hadoop ZooKeeper的地址,Hadoop通過這個地址連接到ZooKeeper注冊NameNode的命名信息,這個動作由`hdfs zkfc -formatZK`這個命令觸發初始化執行。
>4. 到這步就已經知道了,Hadoop中的NameNode的信息ZooKeeper都知道了,那我們是怎么訪問HDFS的呢?教程中很清楚說明了,我們訪問的是`hdfs-site.xml`中配置的`dfs.nameservices`來訪問HDFS的,這個地址剛才說了,就是ZooKeeper的地址,所以在訪問HDFS的時候就是先訪問了ZooKeeper得到當前的Active NameNode,然后再用得到的Active NameNode地址再去訪問HDFS。
>5. Hadoop的Active NameNode宕機呢?`ZKFC`時刻檢測著NameNode,當NameNode宕機的時候,通知ZooKeeper,ZooKeeper保存了所有的NameNode的地址列表,他去通知所有的Standby NameNode進行`搶鎖競選`,誰搶到不重要,結果是會有一個Standby NameNode搶到鎖并切換為Active NameNode,并通知ZooKeeper,這個時候ZooKeeper中的數據依然是實時最新的,很完美~
>6. 這不實現了高可用和主備自動切換嗎~ 簡單粗暴~
### 參考文檔
>* IBM教程:[https://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/](https://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/)
>* 官網文檔:[https://zookeeper.apache.org/doc/r3.4.9/](https://zookeeper.apache.org/doc/r3.4.9/)
>* 參考博客:[http://luchunli.blog.51cto.com/2368057/1681841](http://luchunli.blog.51cto.com/2368057/1681841)