1. 程式人生 > 其它 >Zookeeper分散式服務

Zookeeper分散式服務

Zookeeper(CP)

以叢集的方式【leader和follower】為分散式應用提供協調服務、負責儲存和管理大家都關係的資料,接受觀察者註冊、訊息分發等服務

特點:
  1. 只要有半數以上的節點存活就能保證zookeeper的叢集正常工作

  2. 每一個Client儲存一份相同的資料副本

  3. 來自同一個client的跟新請求可以順序執行

  4. 資料更新原子性,且在一定時間範圍內,client能讀到最新的資料

  5. 軟負載均衡:可以讓訪問數最少的伺服器去處理最新伺服器請求

啟動命令

#啟動zk伺服器,查詢zk伺服器狀態
./zkServer.sh status
./zkServer.sh start path/to/zoo.config
./zkServer.sh stop
#連線伺服器
./zkCli.sh

客戶端命令列

create treeNode data #在某個節點下存放資料
create -s treeNode data #有序號的節點,記錄節點建立的先後順序,沒有從零開始
ls TreeNode # 獲取某個樹節點下的所有資料
delete TreeNode #刪除節點,如果下面還有子節點是無法刪除的
rmr Tree #刪除某個子樹

get treeNode #獲取某個節點下的資料
get treeNode watch #註冊對某個節點修改的監聽,只會有效一次
set treeNode data #改變值
get path #獲取某個樹形結構下的值
get -w path #獲取某個樹形結構下的值,並監聽一次變化
helo #幫助手冊
quit#退出
短暫資料:
create -e treeNode data #在某個節點下存放資料,當前客戶端與伺服器斷開則節點消失
節點引數:

czxid:建立該節點的事務ID、mzxid:最後更新的事務ID、cversion:節點的修改次數、dataversion:資料變化號

節點型別【樹形結構】

持久節點,順序【名字遞增】&非順序:persistent、persistent_sequential,除了在記憶體中,在磁碟中也會儲存一份

臨時節點,順序&非順序:ephemeral、ephemeral_sequential 短暫節點在與客戶端斷開連線後,建立的節點自己刪除了。其原理是通過ping去保活,如果斷開,則會發生session-timeOut,順序的臨時節點適合用來充當分散式鎖。相比redis叢集而言,zookeeper叢集具備更好的資料一致性。另外臨時節點不允許建立子節點

容器節點:當沒有子節點時,會被伺服器刪除【預設60s檢查一次】

TTL節點:過了TTL指定節點的時間就會被伺服器刪除,和redis一致

叢集搭建

在.cfg檔案中設定儲存資料的目錄,同時在目錄下touch一個myid,並設定一個唯一標識

配置檔案:預設是/conf/zoo.cfg

server.A1=B:C:D
server.A2=B:C:D
server.A3=B:C:D
#Ai:myid,唯一的數字標識,在資料儲存目錄中myid檔案裡定義
#B:伺服器的IP地址
#C:leader伺服器交換資訊的埠號
#D:傳遞選舉資訊的埠號

當半數啟動後集群開始正常運轉【一般三個或以上】

節點的讀寫

在zookeeper中,某個節點接受到客戶端寫請求,會將請求轉發給leader,由leader廣播寫請求,並統計成功數。當超過半數回覆成功時,才會向客戶端返回成功

CAP:

CAP原則又稱CAP定理,指的是在一個分散式系統中,一致性(Consistency)、可用性(Availability)、分割槽容忍性(Partition tolerance):將資料項複製到多個節點上,當某些節點發生故障,仍然可以訪問到資料的能力

整合spring

連線並設定監聽是否變化,產生變化後再次監聽:不依賴spring,只依賴一個zookeeper.jar包,如果是Spring框架,可以通過配置pean的方式,直接注入 zookeeper
public class ZooKeeperTest {
    private int sessionTimeout = 2000;
    private String connectString="222.201.144.247:2181";
    public ZooKeeper zooKeeper;
    @Test
    public void init() throws IOException, KeeperException, InterruptedException {

        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                try {
                    System.out.println("_________________start___________________");
                    List<String> list = zooKeeper.getChildren("/",true);
                    for (int i = 0; i < list.size(); i++) {
                        System.out.println(list.get(i));
                    }
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
//        String s=zooKeeper.create("/connect", "spring".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        zooKeeper.getChildren("/",true);//設定監聽,只會生效一次
        Thread.sleep(Long.MAX_VALUE);
    }
}
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.10</version>
    </dependency>
監聽伺服器上下線:

伺服器叢集向Zookeeper叢集上傳訪問量總量,且資料型別為ephemeral,伺服器掉線則資料消失

客戶端叢集向Zookeeper監聽資料getChildren(),伺服器宕機、上線時做出處理並重設監聽

//客戶機,主機部分向Zookeeper叢集裡的/下寫上帶序號、ephemeral的key 和 hostName即可,而由客戶機負責監聽
public class DistributeClient {

    public static void main(String[] args) throws KeeperException, InterruptedException, IOException {
        DistributeClient client = new DistributeClient();
        //連線到Zookeeper叢集
        client.getConnect();
        //設定監聽在控制檯列印節點
        client.getChildren();
        //業務部分
        client.business();
    }

    private int sessionTimeout = 2000;
    private String connectString="222.201.144.247:2181";//如果是Zookeeper叢集的話直接在用逗號隔開即可
    public ZooKeeper zooKeeper;

    private  void getConnect() throws IOException {
        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                try {
                    getChildren();
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private void getChildren() throws KeeperException, InterruptedException {
        List<String> children = zooKeeper.getChildren("/",true);

        ArrayList<String> host = new ArrayList<>();
        for (String child :children){
            byte[] data = zooKeeper.getData("/"+child,false,null);
            host.add(new String(data));
        }
        System.out.println(host);
    }

    private void business(){
    }

}

利用zookeeper實現分散式鎖

方法一:

獲取鎖:create -e /ephemeral/lock threadName 建立失敗則監聽 get -w /ephemeral/lock【此時處於阻塞狀態】,利用Watcher重新獲取鎖。弊端:所有的執行緒都需要反覆的監聽,嘗試獲取鎖,產生系統資源浪費

方法二:【只能實現公平鎖】

請求直接在 create -e -s /ephemeral/lock threadName 建立一個臨時順序節點,並判斷自己是否是最小的節點,如果是,則獲取鎖,若不是,則只監聽前一個節點【只比其小一的節點】並等待。最後通過delete釋放鎖