协调服务zookeeper原理(c++)

zookeeper

应用场景

  1. 统一命名服务:就是和nginx 一样的功能,你输入这个域名,我根据负载均衡去找到相应的服务器IP地址

  2. 统一配置管理:配置文件同步,让所有需要这个配置文件的中间件kafka等等去监听zookeeper上的文件,发生变化就通知

  3. 统一集群管理:zookeeper上的文件去监控客户端的变化,客户端一旦发生了变化,发么我就写到文件里

  4. 服务器节点动态上下线:看图

  5. 软负载均衡

配置参数

#通信心跳,每隔多久发一次信号
tickTime=2000
#第一次建立连接需要的时间,就是initLimit*tickTime,超过这个时间就启动失败
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
## 通信同步时间,syncLmit*tickTime就是最大的通信时长,超过这个时间还没有通信就是这个节点宕机了
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/opt/zookeeper/server2/apache-zookeeper-3.5.7-bin/data
dataLogDir=/opt/zookeeper/server2/apache-zookeeper-3.5.7-bin/logData
# the port at which the clients will connect
clientPort=2182
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
​
server.1=127.0.0.1:2881:3881
server.2=127.0.0.1:2882:3882
server.3=127.0.0.1:2883:3883

刚开始配置的时候,启动一个服务器,结果报错了,因为集群半数的服务器没有启动,所有全部启动就好了

投票机制

  • SID:服务器ID。用来唯一标识一台ZooKeeper集群中的机器,每台机器不能重复,和myid一致。

  • ZXID:事务ID。ZXID是一个事务ID,用来标识一次服务器状态的变更。在某一时刻,集群中的每台机器的ZXID值不一定完全一致,这和ZooKeeper服务器对于客户端“更新请求”的处理逻辑有关。

  • Epoch:每个Leader任期的代号。没有Leader时同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加

两种情况:

  1. 第一次启动:只看myid

  2. 无法和leader保持连接:先看epoch,再看zxid,再看myid

    1. leader挂了

    2. 没有得到半数follow的统一

节点的信息

[zk: localhost:2181(CONNECTED) 1] ls -s /
[zookeeper]cZxid = 0x0                    # 创建节点的事务ID
ctime = Thu Jan 01 08:00:00 CST 1970      # 节点被创建的时间
mZxid = 0x0                               # 最后一次更新这个节点的事务ID
mtime = Thu Jan 01 08:00:00 CST 1970      # 最后一次修改这个节点的时间
pZxid = 0x0                               # 这个节点的最后更新的子节点的事务ID
cversion = -1                             # 子节点的变化次数
dataVersion = 0                           # 节点数据变化号
aclVersion = 0                            # 访问控制列表的变化号
ephemeralOwner = 0x0                      # 临时节点的seenion id 不是临时节点就是0
dataLength = 0                            # 数据大小
numChildren = 1                           # 多少子节点

节点类型:持久和短暂,有序号和无序号

持久和短暂:客户端和服务端断开连接之后,这个节点还在不在了

有序号和无序号:很好理解了,这个序号又父节点,单调递增,这个就是方便了排序

持久节点带序号的:正常创建node1

持久节点不带序号:客户端与Zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号 node1_001

临时不带序号的:创建完删除就行了

临时带序号的:删除之后这个节点XXXXXXXX

#持久节点
#-s 就是有序号的
#-e 就是临时节点
# create创建
# get 获取当前值
# set 修改当前值
[zk: localhost:2181(CONNECTED) 2] ls / 
[zookeeper]
[zk: localhost:2181(CONNECTED) 3] create /sanguo "diaochan"
Created /sanguo
[zk: localhost:2181(CONNECTED) 4] ls /
[sanguo, zookeeper]
[zk: localhost:2181(CONNECTED) 5] create /sanguo/shuguo
Created /sanguo/shuguo
[zk: localhost:2181(CONNECTED) 6] create /sanguo/wuguo
Created /sanguo/wuguo
[zk: localhost:2181(CONNECTED) 7] create /sanguo/weiguo
Created /sanguo/weiguo
[zk: localhost:2181(CONNECTED) 8] ls /
[sanguo, zookeeper]
[zk: localhost:2181(CONNECTED) 9] ls /sanguo
[shuguo, weiguo, wuguo]
[zk: localhost:2181(CONNECTED) 1] get /sanguo
diaochan

监听器:

1、首先要有一个main(线程

2、在main线程中创建Zookeeper客户端, 这时就会创建两个线程,一个负责网络连接通信(connet) ,一个负责监听(listener) 。

3、通过connect线程将注册的监听事件发送给Zookeeper。

4、在Zookeeper的注册监听器列表中将注册的监听事件添加到列表中。

5、Zookeeper监听到有 数据或路径变化,就会将这个消息发送给listener线程。

6、listener线程内部调用了process0方法。

常见的监听:

  1. 节点数据的变化 get -w 路径

  2. 节点增删的变化 ls -w 路径

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <zookeeper/zookeeper.h>
//#include <zookeeper/zookeeper_log.h>
void zktest_watcher_g(zhandle_t* zh, int type, int state,
        const char* path, void* watcherCtx)
{
    printf("Something happened.\n");
    printf("type: %d\n", type);
    printf("state: %d\n", state);
    printf("path: %s\n", path);
    printf("watcherCtx: %s\n", (char *)watcherCtx);
}
void zktest_dump_stat(const struct Stat *stat)
{
    char tctimes[40];
    char tmtimes[40];
    time_t tctime;
    time_t tmtime;
    if (!stat) {
        fprintf(stderr,"null\n");
        return;
    }
    tctime = stat->ctime/1000;
    tmtime = stat->mtime/1000;
       
    ctime_r(&tmtime, tmtimes);
    ctime_r(&tctime, tctimes);
       
    fprintf(stderr, "\tctime = %s\tczxid=%llx\n"
    "\tmtime=%s\tmzxid=%llx\n"
    "\tversion=%x\taversion=%x\n"
    "\tephemeralOwner = %llx\n",
     tctimes, stat->czxid,
     tmtimes, stat->mzxid,
    (unsigned int)stat->version, (unsigned int)stat->aversion,
    stat->ephemeralOwner);
}
//当 zoo_aexists 请求完成时会调用该函数
//rc参数为: ZOK 操作完成;ZNONODE 节点不存在;ZNOAUTH 客户端没有权限删除节点。
void zktest_stat_completion(int rc, const struct Stat *stat, const void *data)
{
    fprintf(stderr, "%s: rc = %d Stat:\n", (char*)data, rc);
    zktest_dump_stat(stat);
}
void zktest_void_completion(int rc, const void *data)
{
    fprintf(stderr, "[%s]: rc = %d\n", (char*)(data==0?"null":data), rc);
}
//当创建节点请求完成时会调用该函数
//rc 参数为: ZOK 操作完成;ZNONODE 父节点不存在;ZNODEEXISTS 节点已存在;ZNOAUTH 客户端没有权限创建节点。ZNOCHILDRENFOREPHEMERALS 临时节点不能创建子节点
//value 参数即新节点的路径名
//string_completion_t completion 中 const void *data 参数即为 zoo_acreate 中的 const void *data。
void zktest_string_completion(int rc, const char *name, const void *data)
{
    fprintf(stderr, "[%s]: rc = %d\n", (char*)(data==0?"null":data), rc);
    if (!rc) {
        fprintf(stderr, "\tname = %s\n", name);
    }
}
int main(int argc, const char *argv[])
{
    const char* host = "127.0.0.1:2181";
    //如果 30 秒内客户端没有连接上 Zookeeper 服务则表示连接超时。
    int timeout = 30000;
    //设置日志等级。
    zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);
//初始化zookeeper句柄(zhandle_t)
//第四个参数为客户端会话id,客户端尝试重连的先前会话的ID,如果不需要重连先前的会话,则设置为 0
//第五个参数为当前zk的上下文对象,最后一个参数是预留参数
    zhandle_t* zkhandle = zookeeper_init(host,
            zktest_watcher_g, timeout, 0, (void * )"hello zookeeper.", 0);
    if (zkhandle == NULL) {
        fprintf(stderr, "Error when connecting to zookeeper servers...\n");
        exit(EXIT_FAILURE);
    }
    // struct ACL ALL_ACL[] = {{ZOO_PERM_ALL, ZOO_ANYONE_ID_UNSAFE}};
    // struct ACL_vector ALL_PERMS = {1, ALL_ACL};
//创建一个znode节点,节点路径为"/xyz",保存一个长度为5,内容为hello的数据,任何人都可以访问
//这个一个异步函数,调用之后,直接往下执行,但是此时节点并没有真正的创建成功。节点创建成功之后会调用zktest_string_compeltion函数
//最后一个参数是该回调函数,要传入的参数
    int ret = zoo_acreate(zkhandle, "/xyz", "hello", 5,
           &ZOO_OPEN_ACL_UNSAFE, 0 /* ZOO_SEQUENCE */,
           zktest_string_completion, "acreate");
    if (ret) {
        fprintf(stderr, "Error %d for %s\n", ret, "acreate");
        exit(EXIT_FAILURE);
    }
    ret = 0;
    
//第三个参数是监视,如果非 0,则在服务器端设置监视,当节点发生变化时客户端会得到通知,即使当前指定的节点不存在也会设置监视,这样该节点被创建时,客户端也可以得到通知。
    ret = zoo_aexists(zkhandle, "/xyz", 1, zktest_stat_completion, "aexists");
    if (ret) {
        fprintf(stderr, "Error %d for %s\n", ret, "aexists");
        exit(EXIT_FAILURE);
    }
    ret = 0;
    // Wait for asynchronous zookeeper call done.
    getchar();
//第三个参数:期望的节点版本号,如果真实的版本号与期望的版本号不同则 zoo_delete() 调用失败,-1 表示不不检查版本号。
    ret = zoo_adelete(zkhandle, "/xyz", -1, zktest_void_completion, "adelete");
    if (ret) {
        fprintf(stderr, "Error %d for %s\n", ret, "adelete");
        exit(EXIT_FAILURE);
    }
    // Wait for asynchronous zookeeper call done.
    getchar();
    zookeeper_close(zkhandle);
    return 0;
}

用途

zookeeper 都有哪些使用场景? - 腾讯云开发者社区-腾讯云 (tencent.com)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值