1. 程式人生 > >【Zookeeper】Leader選舉機制示例

【Zookeeper】Leader選舉機制示例

本文介紹下zookeeper中leader選舉機制的基本用法和關鍵知識點。

一、 選項設定
提到Leader選舉,先需要重點介紹下建立znode時的Flag選項。

  • ZOO_EPHEMERAL

ZOO_EPHEMERAL,用來標記當建立這個znode的節點和Zookeeper失去連線後,這個znode將不再存在在Zookeeper裡,Zookeeper使用Watcher察覺事件資訊。當客戶端接收到事件資訊,比如連線超時、節點資料改變、子節點改變,可以呼叫相應的行為來處理資料。

  • ZOO_SEQUENCE

ZOO_SEQUENCE 用來標識節點命名具有遞增的字尾序號(一般是節點名稱後填充 10 位字元的序號,如 /xyz0000000000, /xyz0000000001, /xyz0000000002, ...),如下便所示,直接在/app_watch/下建立節點。

[zk: localhost:2181(CONNECTED) 42] ls /app_watch
[0000000017, 0000000018]

同樣地,ZOO_EPHEMERAL, ZOO_SEQUENCE 可以組合使用,下面的示例中就用到了序列號的特性。

二、監視機制

以下面的示例中,可以看到在client的回撥中,每監視到一次事件通知,需要再次呼叫觸發監視,這裡就需要具體說明下zookeeper和 watch機制。

Zookeeper 中最有特色且最不容易理解的是監視(Watches)。Zookeeper 所有的讀操作——getData(), getChildren(), 和 exists() 都 可以設定監視(watch),監視事件可以理解為一次性的觸發器, 官方定義如下: a watch event is one-time trigger, sent to the client that set the watch, which occurs when the data for which the watch was set changes。對此需要作出如下理解:

  • (一次性觸發)One-time trigger

當設定監視的資料發生改變時,該監視事件會被髮送到客戶端,例如,如果客戶端呼叫了 getData("/znode1", true) 並且稍後 /znode1 節點上的資料發生了改變或者被刪除了,客戶端將會獲取到 /znode1 發生變化的監視事件,而如果 /znode1 再一次發生了變化,除非客戶端再次對 /znode1 設定監視,否則客戶端不會收到事件通知。

  • (傳送至客戶端)Sent to the client

Zookeeper 客戶端和服務端是通過 socket 進行通訊的,由於網路存在故障,所以監視事件很有可能不會成功地到達客戶端,監視事件是非同步傳送至監視者的,Zookeeper 本身提供了保序性(ordering guarantee):即客戶端只有首先看到了監視事件後,才會感知到它所設定監視的 znode 發生了變化(a client will never see a change for which it has set a watch until it first sees the watch event). 網路延遲或者其他因素可能導致不同的客戶端在不同的時刻感知某一監視事件,但是不同的客戶端所看到的一切具有一致的順序。

  • (被設定 watch 的資料)The data for which the watch was set

 這意味著 znode 節點本身具有不同的改變方式。你也可以想象 Zookeeper 維護了兩條監視連結串列:資料監視和子節點監視(data watches and child watches) getData() and exists() 設定資料監視,getChildren() 設定子節點監視。 或者,你也可以想象 Zookeeper 設定的不同監視返回不同的資料,getData() 和 exists() 返回 znode 節點的相關資訊,而 getChildren() 返回子節點列表。因此, setData() 會觸發設定在某一節點上所設定的資料監視(假定資料設定成功),而一次成功的 create() 操作則會出發當前節點上所設定的資料監視以及父節點的子節點監視。一次成功的 delete() 操作將會觸發當前節點的資料監視和子節點監視事件,同時也會觸發該節點父節點的child watch。

Zookeeper 中的監視是輕量級的,因此容易設定、維護和分發。當客戶端與 Zookeeper 伺服器端失去聯絡時,客戶端並不會收到監視事件的通知,只有當客戶端重新連線後,若在必要的情況下,以前註冊的監視會重新被註冊並觸發,對於開發人員來說 這通常是透明的。只有一種情況會導致監視事件的丟失,即:通過 exists() 設定了某個 znode 節點的監視,但是如果某個客戶端在此 znode 節點被建立和刪除的時間間隔內與 zookeeper 伺服器失去了聯絡,該客戶端即使稍後重新連線 zookeeper伺服器後也得不到事件通知。

三、示例

在這個示例中,伺服器啟動會在/app_watch下注冊自己的資訊,並使用自增序列號的機制,客戶端同樣監聽/app_watch下的節點資訊變化,並打印出來,如果需要選舉leader的話,直接把child裡的序列號最小的選為leader就好了。

客戶端程式碼如下:

#include <zookeeper.h>
#include <zookeeper_log.h>
#include <iostream>
#include <string.h>

using namespace std;

const char* host = "127.0.0.1:2181";
const int timeout = 2000;
zhandle_t* zkhandle = NULL;
bool is_connect = false;

    char path[] = "/app_watch";
    int ret = 0;
	char buffer[1024];
	int buff_len;
	Stat stat;

void clients_watcher_g(zhandle_t * zh, int type, int state, const char* path, void* watcherCtx) {
    printf("global watcher - type:%d,state:%d\n", type, state);
    if ( type==ZOO_SESSION_EVENT ) {
        if ( state==ZOO_CONNECTED_STATE ) {
            printf("connected to zookeeper service successfully!\n");
            printf("timeout:%d\n", zoo_recv_timeout(zh));
            is_connect = true;
        }
        else if ( state==ZOO_EXPIRED_SESSION_STATE ) {
            printf("zookeeper session expired!\n");
        }
    }
    else {
        printf("other type:%d\n", type);
    }
}

void child_watch_cb(zhandle_t* zh, int type, int state, const char* path, void* watcher) {
    printf("call child watch cb\n");
}

void watch_cb(zhandle_t* zh, int type, int state, const char* path, void* watcher) {
    printf("call watch cb\n");

    // watch path
	buff_len = sizeof(buffer);
	bzero(buffer, buff_len);
    ret = zoo_wget(zkhandle, path, watch_cb, NULL, buffer, &buff_len, &stat);
	printf("ret=%d stat=%d len=%d buffer=%s\n", ret, stat.ctime, buff_len, buffer);
}

int main(int argc, char* argv[]) 
{
    //Init and connect
    zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);
    if (zkhandle) 
        zookeeper_close(zkhandle);

    zkhandle = zookeeper_init(host, clients_watcher_g, timeout, 0, NULL, 0);
    if (NULL == zkhandle) {
        printf("zookeeper init error\n");
        return 0;
    }

    // watch path
	buff_len = sizeof(buffer);
	bzero(buffer, buff_len);
    ret = zoo_wget(zkhandle, path, watch_cb, NULL, buffer, &buff_len, &stat);
	printf("ret=%d stat=%d len=%d buffer=%s\n", ret, stat.ctime, buff_len, buffer);

    while(1) {
    struct String_vector str_vec;
    ret = zoo_wget_children(zkhandle, path, child_watch_cb, NULL, &str_vec);
    if (ZOK != ret) {
        printf("zookeeper wget error\n");
        return 0;
    }
    for (int i = 0; i < str_vec.count; i++) {
        printf("data[%d] data:[%s]\n", i, str_vec.data[i]);
    }
    printf("sleep...\n");
    sleep(5);
    }

    zookeeper_close(zkhandle);
}

伺服器程式碼如下:
#include <zookeeper.h>
#include <zookeeper_log.h>
#include <iostream>
#include <string.h>

using namespace std;

const char* host = "127.0.0.1:2181";
const int timeout = 2000;
zhandle_t* zkhandle = NULL;
bool is_connect = false;

void clients_watcher_g(zhandle_t * zh, int type, int state, const char* path, void* watcherCtx) {
	printf("global watcher - type:%d,state:%d\n", type, state);
	if ( type==ZOO_SESSION_EVENT ) {
		if ( state==ZOO_CONNECTED_STATE ) {
			printf("connected to zookeeper service successfully!\n");
			printf("timeout:%d\n", zoo_recv_timeout(zh));
			is_connect = true;
		}
		else if ( state==ZOO_EXPIRED_SESSION_STATE ) {
			printf("zookeeper session expired!\n");
		}
	}
	else {
		printf("other type:%d\n", type);
	}
}

int main(int argc, char* argv[]) 
{
	//Init and connect
	zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);
	if (zkhandle) 
		zookeeper_close(zkhandle);

	zkhandle = zookeeper_init(host, clients_watcher_g, timeout, 0, NULL, 0);
	if (NULL == zkhandle) {
		printf("zookeeper init error\n");
		return 0;
	}

    // init
    char path[] = "/app_watch";
	Stat stat;
	int ret = 0;

    // create
    ret = zoo_create(zkhandle, path, NULL, -1, &ZOO_OPEN_ACL_UNSAFE, ZOO_EPHEMERAL|ZOO_SEQUENCE, NULL, 0); 
    printf("create [%s] ret=%d\n", path, ret);

	char buffer[1024];
	int buff_len;
    char child_path[1024];
    //snprintf(child_path, 1023, "%s", path, getpid());
    snprintf(child_path, 1023, "%s/", path, getpid());

    // create
	buff_len = sizeof(buffer);
    snprintf(buffer, buff_len, "10.101.1.101:%d", getpid());
    buff_len = strlen(buffer);
    printf("buff:%s len:%d\n", buffer, buff_len);
    ret = zoo_create(zkhandle, child_path, buffer, buff_len, &ZOO_OPEN_ACL_UNSAFE, ZOO_EPHEMERAL|ZOO_SEQUENCE, NULL, 0); 
    printf("create [%s] ret=%d\n", child_path, ret);

    while(1) sleep(1200);

	zookeeper_close(zkhandle);
}

執行一下客戶端,並啟動一個伺服器例項,檢視如下:
[zk: localhost:2181(CONNECTED) 11] ls /app_watch            
[0000000026]
[zk: localhost:2181(CONNECTED) 12] get /app_watch/0000000026
10.101.1.101:20594

看到 app_watch下已經註冊了一個節點,id是26,檢視client的日誌如下:

[[email protected] /home/derrywang/zookeeper/zk_demo2]# ./watch_client 
global watcher - type:-1,state:3
ret=0 stat=-1597853671 len=18 buffer=10.101.1.101:29413
connected to zookeeper service successfully!
timeout:4000
data[0] data:[0000000026]
sleep...

再啟動一個伺服器例項,檢視如下:

[zk: localhost:2181(CONNECTED) 13] ls /app_watch            
[0000000026, 0000000027]
[zk: localhost:2181(CONNECTED) 14] get /app_watch/0000000027
10.101.1.101:20871

看到app_watch下已經註冊了兩個節點了,id是27, 檢視client的日誌如下:

call child watch cb
data[0] data:[0000000026]
data[1] data:[0000000027]
sleep...

已經觸發了回撥,並獲了一個一個新的節點,這裡把第一個例項停掉,過一段時間client日誌如下:

call child watch cb
data[0] data:[0000000027]
sleep...
leader已死,只有一個節點了,這裡可以觸發切換了。

上面的例子說明了zookeeper leader選舉的核心機制了,瞭解了這個機制,再做路由配置等都是應用層的實現了。

相關推薦

ZookeeperLeader選舉機制示例

本文介紹下zookeeper中leader選舉機制的基本用法和關鍵知識點。 一、 選項設定 提到Leader選舉,先需要重點介紹下建立znode時的Flag選項。 ZOO_EPHEMERAL ZOO_EPHEMERAL,用來標記當建立這個znode的節點和Zookeepe

zookeeper事件 watch 機制 原理

zk作為一款成熟的分散式協調框架,訂閱-釋出功能是很重要的一個。所謂訂閱釋出功能,其實說白了就是觀察者模式。觀察者會訂閱一些感興趣的主題,然後這些主題一旦變化了,就會自動通知到這些觀察者。 zk的訂閱釋出也就是watch機制,是一個輕量級的設計。因為它採用了一種推拉結合的模

分散式ZookeeperLeader選舉-選舉過程介紹

【分散式】Zookeeper的Leader選舉-選舉過程介紹 選舉開始,伺服器會各自為自己投票,在投票完成後,會將投票資訊傳送給叢集中的所有伺服器(觀察者伺服器不參與選舉)。 選票由兩部分組成:伺服器唯一標識myid和事務編號zxid,即(myid,xzid)。 zxid越大說明資料越新,在選擇演算法中

ZooKeeper Notes 23Leader選舉-來自郵件列表

logical clocks是用來唯一標識一輪Leader選舉的。 - 次Leader掛了之後,叢集中的其他機器都會對logical clocks值做自增操作。 - 新一輪的Leader選舉開始或新一輪的投票開始了的時候,會對logical clocks值做自增操作。 在

zookeeper4master選舉

考慮7*24小時向外提供服務的系統,不能有單點故障,於是我們使用叢集,採用的是Master+Slave。叢集中有一臺主機和多臺備機,由主機向外提 供服務,備機監聽主機狀態,一旦主機宕機,備機必需迅速接管主機繼續向外提供服務。在這個過程中,從備機選出一臺機作為主機的過程,就是Master選 舉。  

說說zookeeper_工作機制和實現原理

本文簡單說說zookeeper的工作機制。 總體來說,客戶端先和zookeeper伺服器建立起一個TCP長連線(session),之後根據ACL許可權的設定,可在zookeeper伺服器上對目

zookeeper leader選舉機制

最近看了下zookeeper的原始碼,先整理下leader選舉機制 先看幾個關鍵資料結構和函式 服務可能處於的狀態,從名字應該很好理解 public enum ServerState {   LOOKING, FOLLOWING, LEADING, OBSERVING; } 選票引數,還有Notifi

leader 選舉機制

pic ati lower quorum leader asp class nal 共享 from: http://www.jasongj.com/2015/01/02/Kafka%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90/ 一種非常常用的選舉

bzoj4966總統選舉 隨機化+線段樹

編號 並且 bsp 出現 ++ for 條件 amp 描述 題目描述 黑惡勢力的反攻計劃被小C成功摧毀,黑惡勢力只好投降。秋之國的人民解放了,舉國歡慶。此時,原秋之國總統因沒能守護好國土,申請辭職,並請秋之國人民的大救星小C欽定下一任。作為一名民主人士,小C決定舉行全民大

javajava反射機制,動態獲取對象的屬性和對應的參數值,並屬性按照字典序排序,Field.setAccessible()方法的說明可用於微信支付 簽名生成

modifier 直接 this 字段值 1-1 讓我 toupper ima play 方法1:通過get()方法獲取屬性值 package com.sxd.test.controller; public class FirstCa{ private

shopexapp開發機制

添加 dem ges http false .html return index array shopex的app開發機制詳解 shopex的app開發機制,讓我們可以實現以下特性: 1、建立自己的數據庫表。 2、創建自己的控制器。 3、在前後臺增加欄目

zookeeper簡單安裝

zookeeper1.安裝java#!/bin/bash # rpm -ivh jdk-8u144-linux-x64.rpm # java -version java version "1.8.0_144"2.安裝zookeeper下載地址 : http://zookeeper.apac

zookeeper集群安裝

zookeeper cluster zoo.cfg 1.解壓(按照簡單安裝操作)2.修改配置文件vim zoo.cfg ----------- # The number of milliseconds of each tick tickTime=2000 # The number of ticks

mysqlmysql觸發器使用示例

mysql5 大小 use 資源池 nod for HR pac nta mysql觸發器 時間點:before/after 觸發事件: update/delete/insert 時間點+觸發事件:構成一個完整的觸發器的觸發時機; 一個觸發時機最多只能由1個Trigger

8.8.ZooKeeper 原理和選舉機制

TE 宋體 per 機制 CA tro 通過 family 沒有 1.ZooKeeper原理   Zookeeper雖然在配置文件中並沒有指定master和slave但是,zookeeper工作時,是有一個節點為leader,其他則為follower,Leader是通 過內

zookeeperApache curator的使用及zk分布式鎖實現

sets finally tac -- ont zkcli 單節點 基本操作 新建 上篇,本篇主要講Apache開源的curator的使用,有了curator,利用Java對zookeeper的操作變得極度便捷. 其實在學之前我也有個疑慮,我為啥要學curator,撇開漲薪

zookeeper叢集的選舉機制

 Zookeeper預設的演算法是FastLeaderElection, 採用投票數大於半數則勝出的邏輯。     選舉依據:         伺服器ID: &n

zookeeperzookeeper的基本命令及通過Java操作zk

接上講,這節主要講一下zookeeper的常用命令和如何使用java操作zk. 首先連線zookeeper 客戶端: #進入zookeeper安裝目錄下bin目錄,啟動zk客戶端 cd /usr/local/zookeeper/bin ./zkCli.sh 然後不知道zk的常用命令?沒

zookeeperApache curator的使用及zk分散式鎖實現

接上篇,本篇主要講Apache開源的curator的使用,有了curator,利用Java對zookeeper的操作變得極度便捷. 其實在學之前我也有個疑慮,我為啥要學curator,撇開漲薪這些外在的東西,就單技術層面來講,學curator能幫我做些什麼?這就不得不從zookeeper說起,上

zookeeperzookeeper介紹及安裝和叢集配置

1.什麼是zookeeper ?     zookeeper 英文直譯是動物管理員,試想下,動物園裡有很多動物,如果沒有動物管理員去做管理的話,各種動物混在一起很可能出現打架問題,疾病,髒,等等一系列問題,這個時候就需要有個主人去把這些動物統一管理起來,zookeeper其實