java大數據之zookeeper+springboot

1. zookeeper簡介

java大數據之zookeeper+springboot

1.1. ZooKeeper 的由來

Zookeeper最早起源於雅虎研究院的一個研究小組。在當時,研究人員發現,在雅虎內部很多大型系統基本都需要依賴一個類似的系統來進行分佈式協調,但是這些系統往往都存在分佈式單點問題。所以,雅虎的開發人員就試圖開發一個通用的無單點問題的分佈式協調框架,以便讓開發人員將精力集中在處理業務邏輯上。

關於"ZooKeeper"這個項目的名字,其實也有一段趣聞。在立項初期,考慮到之前內部很多項目都是使用動物的名字來命名的(例如著名的Pig項目),雅虎的工程師希望給這個項目也取一個動物的名字。時任研究院的首席科學家RaghuRamakrishnan(羅摩克里希納)開玩笑地說:"在這樣下去,我們這兒就變成動物園了!"此話一出,大家紛紛表示就叫動物園管理員吧一一一因為各個以動物命名的分佈式組件放在一起,雅虎的整個分佈式系統看上去就像一個大型的動物園了,而Zookeeper正好要用來進行分佈式環境的協調一一於是,Zookeeper的名字也就由此誕生了。

1.2. ZooKeeper 概覽

java大數據之zookeeper+springboot

Zookeeper 一個最常用的使用場景就是用於擔任服務生產者和服務消費者的註冊中心。 服務生產者將自己提供的服務註冊到Zookeeper中心,服務的消費者在進行服務調用的時候先到Zookeeper中查找服務,獲取到服務生產者的詳細信息之後,再去調用服務生產者的內容與數據。如下圖所示,在 Dubbo架構中 Zookeeper 就擔任了註冊中心這一角色。

java大數據之zookeeper+springboot

2. zookeeper安裝配置

1. 文件準備

將下載下來的Zookeeper 的配置文件進行解壓 在linux上輸入:

tar -xvf zookeeper-3.4.10.tar.gz

然後移動到/opt/zookeeper裡面,沒有就新建,然後將文件夾重命名為zookeeper3.4 輸入

mkdir /opt/zookeeper

mv zookeeper-3.4.10 /opt/zookeeper/zookeeper3.4

2. 環境配置(省略,前期已經做過了)

編輯 /etc/profile 文件 輸入:

export ZK_HOME=/opt/zookeeper/zookeeper3.4

export PATH=.:${JAVA_HOME}/bin:${SCALA_HOME}/bin:${SPARK_HOME}/bin:${ZK_HOME}/bin:$PATH

輸入:

source /etc/profile

使配置生效

3. 修改配置文件

3.1. 創建文件和目錄

在集群的服務器上都創建這些目錄

mkdir /opt/zookeeper/data

mkdir /opt/zookeeper/dataLog

並且在/opt/zookeeper/data目錄下創建myid文件 輸入:

touch myid

創建成功之後,更改myid文件。 我這邊為了方便,將master、slave1、slave2的myid文件內容改為1,2,3

3.2. 新建zoo.cfg

切換到/opt/zookeeper/zookeeper3.4/conf 目錄下 如果沒有 zoo.cfg 該文件,就複製zoo_sample.cfg文件並重命名為zoo.cfg。 修改這個新建的zoo.cfg文件

dataDir=/opt/zookeeper/data

dataLogDir=/opt/zookeeper/dataLog

server.1=master:2888:3888

server.2=slave1:2888:3888

server.3=slave2:2888:3888

配置說明

client port,顧名思義,就是客戶端連接zookeeper服務的端口。這是一個TCP port。

dataLogDir裡是放到的順序日誌(WAL)。而dataDir裡放的是內存數據結構的snapshot,便於快速恢復。為了達到性能最大化,一般建議把dataDir和dataLogDir分到不同的磁盤上,這樣就可以充分利用磁盤順序寫的特性。dataDir和dataLogDir需要自己創建,目錄可以自己制定,對應即可。

server.1中的這個1需要和master這個機器上的dataDir目錄中的myid文件中的數值對應。server.2中的這個2需要和slave1這個機器上的dataDir目錄中的myid文件中的數值對應。server.3中的這個3需要和slave2這個機器上的dataDir目錄中的myid文件中的數值對應。當然,數值你可以隨便用,只要對應即可。2888和3888的端口號也可以隨便用,因為在不同機器上,用成一樣也無所謂。

其他配置說明:1.tickTime:CS通信心跳數 Zookeeper 服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會發送一個心跳。tickTime以毫秒為單位。 tickTime=2000 2.initLimit:LF初始通信時限 集群中的follower服務器(F)與leader服務器(L)之間初始連接時能容忍的最多心跳數(tickTime的數量)。 initLimit=10 3.syncLimit:LF同步通信時限 集群中的follower服務器與leader服務器之間請求和應答之間能容忍的最多心跳數(tickTime的數量)。 syncLimit=5

依舊將zookeeper傳輸到其他的機器上,記得更改 /opt/zookeeper/data 下的myid,這個不能一致。 輸入:

scp -r /opt/zookeeper root@slave1:/opt

scp -r /opt/zookeeper root@slave2:/opt

修改myid

4. 啟動zookeeper

因為zookeeper是選舉制,它的主從關係並不是像hadoop那樣指定的,具體可以看官方的文檔說明。 成功配置zookeeper之後,

在每臺機器上啟動zookeeper。 切換到zookeeper目錄下

cd /opt/zookeeper/zookeeper3.4/bin

輸入:

zkServer.sh start

成功啟動之後 查看狀態輸入:

zkServer.sh status

可以查看各個機器上zookeeper的leader和follower ,只能一個是主,隨機選擇,入下圖:

java大數據之zookeeper+springboot

3. zookeeper結構和命令

3.1. zookeeper特性

1、Zookeeper:一個leader,多個follower組成的集群

2、全局數據一致:每個server保存一份相同的數據副本,client無論連接到哪個server,數據都是一致的

3、分佈式讀寫,更新請求轉發,由leader實施

4、更新請求順序進行,來自同一個client的更新請求按其發送順序依次執行

5、數據更新原子性,一次數據更新要麼成功,要麼失敗

6、實時性,在一定時間範圍內,client能讀到最新數據

3.2. zookeeper數據結構

1、層次化的目錄結構,命名符合常規文件系統規範(見下圖)

2、每個節點在zookeeper中叫做znode,並且其有一個唯一的路徑標識

3、節點Znode可以包含數據和子節點

3.3. 數據結構的圖

java大數據之zookeeper+springboot

3.4. 節點類型

1、Znode有兩種類型:

短暫(ephemeral)(斷開連接自己刪除)

持久(persistent)(斷開連接不刪除)

2、Znode有四種形式的目錄節點(默認是persistent )

PERSISTENT

PERSISTENT_SEQUENTIAL(持久序列/test0000000019 )

EPHEMERAL

EPHEMERAL_SEQUENTIAL

3、創建znode時設置順序標識,znode名稱後會附加一個值,順序號是一個單調遞增的計數器,由父節點維護

4、在分佈式系統中,順序號可以被用於為所有的事件進行全局排序,這樣客戶端可以通過順序號推斷事件的順序

ZooKeeper的數據模型是內存中的一個ZNode數,由斜槓(/)進行分割的路徑,就是一個ZNode,每個ZNode上除了保存自己的數據內容,還保存一系列屬性信息;

ZooKeeper中的數據節點分為兩種:持久節點和臨時節點。

所謂的持久節點是指一旦這個ZNode創建成功,除非主動進行ZNode的移除操作,節點會一直保存在ZooKeeper上;而臨時節點的生命週期是跟客戶端的會話相關聯的,一旦客戶端會話失效,這個會話上的所有臨時節點都會被自動移除

3.5. zookeeper命令行操作

運行 zkCli.sh –server 進入命令行工具

在linux cli輸入:./zkCli.sh -server hostIP/hostName

[zk: master(CONNECTED) 0]說明已經進入zookeeper命令行

java大數據之zookeeper+springboot

1、使用 ls 命令來查看當前 ZooKeeper 中所包含的內容:

ls /

java大數據之zookeeper+springboot

2、創建一個新的 znode ,使用 create /zk myData 。這個命令創建了一個新的 znode 節點" zk "以及與它關聯的字符串:

create /zk "myData"

java大數據之zookeeper+springboot

3、我們運行 get 命令來確認 znode 是否包含我們所創建的字符串:

get /zk

java大數據之zookeeper+springboot

· czxid. 節點創建時的zxid.

· mzxid. 節點最新一次更新發生時的zxid.

· ctime. 節點創建時的時間戳.

· mtime. 節點最新一次更新發生時的時間戳.

· pZxid 這個節點就和子節點有關啦!是與 該節點的子節點(或該節點)的最近一次 創建 / 刪除 的時間戳對應

·

java大數據之zookeeper+springboot

· dataVersion. 節點數據的更新次數.

· cversion. 其子節點的更新次數.

· aclVersion. 節點ACL(授權信息)的更新次數.

· ephemeralOwner. 如果該節點為ephemeral節點, ephemeralOwner值表示與該節點綁定的session id. 如果該節點不是ephemeral節點, ephemeralOwner值為0x0. 至於什麼是ephemeral節點, 請看後面的講述.

· dataLength. 節點數據的字節數.

· numChildren. 子節點個數.

#監聽這個節點的變化,當另外一個客戶端改變/zk時,它會打出下面的

#WATCHER::

#WatchedEvent state:SyncConnected type:NodeDataChanged path:/zk

get /zk watch

4、下面我們通過 set 命令來對 zk 所關聯的字符串進行設置:

set /zk "aaa"

5、下面我們將剛才創建的 znode 刪除:

delete /zk

java大數據之zookeeper+springboot

6、刪除節點(包含子節點):rmr

rmr /zk

java大數據之zookeeper+springboot

7,短暫節點ephemeral和持久節點persistent的區別:

創建短暫節點:

java大數據之zookeeper+springboot

然後使用close斷開連接

java大數據之zookeeper+springboot

發現在其他機器上,close之前可以正常獲取節點信息,但是close之後無法獲取節點信息。如下圖:

java大數據之zookeeper+springboot

3.6. zookeeper-api應用

3.6.1. 基本使用

org.apache.zookeeper.Zookeeper是客戶端入口主類,負責建立與server的會話

它提供了表 1 所示幾類主要方法 :

3.6.2. 增刪改查(springboot)

java大數據之zookeeper+springboot

 package com.aaa.qy87.zookeeper;

import org.apache.zookeeper.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;

/**
* @Author: 陳建
* @Date: 2019/2/18 0018 15:50
* @Version 1.0
* 測試zookeeperAPI
*/
public class MyZookeeper {
//zookeeper實例
ZooKeeper zk=null;
//sessionTimeout連接超時時間
int sessionTimeout=30000;
Watcher wc= new Watcher() {
@Override
public void process(WatchedEvent event) {

}
};

/**
* 初始化zookeeper實例
*/
@Before
public void initZookeeperConnect() throws IOException {
//String connectString, int sessionTimeout, Watcher watcher
zk= new ZooKeeper("192.168.153.101:2181",sessionTimeout,wc);
}

/**
* 創建節點
*/
@Test
public void createNode() throws KeeperException, InterruptedException {
//第一個參數path:the path for the node節點的路徑(名稱)
//第二個參數data:the initial data for the node(節點值)
//第三個參數acl:the acl for the node(節點的訪問控制。權限)

//第四個參數createMode:specifying whether the node to be created is ephemeral and/or sequential
// 節點的類型,短暫還是持久
zk.create("/newNode","hello".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
/**
* 查看節點
*/
@Test
public void getNodeInfo() throws KeeperException, InterruptedException {
//String path 節點路徑, boolean watch是否監控, Stat stat節點狀態
byte[] zkData = zk.getData("/newNode", false, null);
String result=new String(zkData);
System.out.println("返回結果:"+result);
}
/**
* 修改節點
*/
@Test
public void setNodeInfo() throws KeeperException, InterruptedException {
// String path 節點路徑, byte data[] 節點值, int version 版本號 默認-1
zk.setData("/newNode","xxx".getBytes(),-1);
//String path 節點路徑, boolean watch是否監控, Stat stat節點狀態
byte[] zkData = zk.getData("/newNode", false, null);
String result=new String(zkData);
System.out.println("返回結果:"+result);
}
/**
* 刪除節點
*/
@Test
public void deleteNodeInfo() throws KeeperException, InterruptedException {
// String path 節點路徑, int version 版本號 默認-1
zk.delete("/newNode",-1);
//String path 節點路徑, boolean watch是否監控, Stat stat節點狀態
byte[] zkData = zk.getData("/newNode", false, null);
String result=new String(zkData);
System.out.println("返回結果:"+result);
}

/**

* 關閉zookeeper連接
* @throws InterruptedException
*/
@After
public void closeZookeeper() throws InterruptedException {
zk.close();
}

}
java大數據之zookeeper+springboot

4. zookeeper原理

Zookeeper雖然在配置文件中並沒有指定master和slave

但是,zookeeper工作時,是有一個節點為leader,其他則為follower

Leader是通過內部的選舉機制臨時產生的

4.1. zookeeper全新集群的選舉機制

java大數據之zookeeper+springboot

以一個簡單的例子來說明整個選舉的過程.假設有五臺服務器組成的zookeeper集群,它們的id從1-5,同時它們都是最新啟動的,也就是沒有歷史數據,在存放數據量這一點上,都是一樣的.假設這些服務器依序啟動,來看看會發生什麼.1) 服務器1啟動,此時只有它一臺服務器啟動了,它發出去的報沒有任何響應,所以它的選舉狀態一直是LOOKING狀態2) 服務器2啟動,它與最開始啟動的服務器1進行通信,互相交換自己的選舉結果,由於兩者都沒有歷史數據,所以id值較大的服務器2勝出,但是由於沒有達到超過半數以上的服務器都同意選舉它(這個例子中的半數以上是3),所以服務器1,2還是繼續保持LOOKING狀態.3) 服務器3啟動,根據前面的理論分析,服務器3成為服務器1,2,3中的老大,而與上面不同的是,此時有三臺服務器選舉了它,所以它成為了這次選舉的leader.4) 服務器4啟動,根據前面的分析,理論上服務器4應該是服務器1,2,3,4中最大的,但是由於前面已經有半數以上的服務器選舉了服務器3,所以它只能接收當小弟的命了.5) 服務器5啟動,同4一樣,當小弟.

4.2. 非全新集群的選舉機制(數據恢復)

那麼,初始化的時候,是按照上述的說明進行選舉的,但是當zookeeper運行了一段時間之後,有機器down掉,重新選舉時,選舉過程就相對複雜了。

需要加入數據id、leader id和邏輯時鐘。

數據id:數據新的id就大,數據每次更新都會更新id。

Leader id:就是我們配置的myid中的值,每個機器一個。

邏輯時鐘:這個值從0開始遞增,每次選舉對應一個值,也就是說: 如果在同一次選舉中,那麼這個值應該是一致的 ; 邏輯時鐘值越大,說明這一次選舉leader的進程更新.

選舉的標準就變成:

1、邏輯時鐘小的選舉結果被忽略,重新投票

2、統一邏輯時鐘後,數據id大的勝出

3、數據id相同的情況下,leader id大的勝出

根據這個規則選出leader。


分享到:


相關文章: