在分布式调度系统中可以使用zookeeper实现统一命名服务,以获得类似于UUID的全局唯一名称。
借助ZNODE顺序节点的特性,本文通过创建临时顺序节点来获得统一命名。使用zookeeper创建顺序节点时,成功创建的每个节点都会返回一个编号,使用该编号以及给定的名称即可生成具有特定含义的统一名称。实现逻辑如下:
1.所有客户端(本文以多线程模拟)都会创建同一个名称的顺序节点
2.节点创建完成后,会返回一个节点名(get_name_completion()中的value返回值)
3.客户端拿到返回值后,可以加上具有特定含义的字符串,即可拼成一个全局唯一的名称。
本文使用zookeeper的C语言客户端,下文代码依赖的头文件以及动态库的生成方式如下:
进入zookeeper 下的src/c/目录下
#./configure
# make & make install
默认头文件安装与 /usr/local/zookeeper/ 动态库位于/usr/local/lib
相关接口介绍如下,具体详见头文件:
//初始化一个zookeeper连接句柄
zhandle_t *zookeeper_init(const char *host, watcher_fn fn,
int recv_timeout, const clientid_t *clientid, void *context, int flags);
其中 watcher_fn fn是一个全局监视回调函数
//一个异步创建znode的接口,创建完成会调用completion回调函数
int zoo_acreate(zhandle_t *zh, const char *path, const char *value,
int valuelen, const struct ACL_vector *acl, int flags,
string_completion_t completion, const void *data);
//close the zookeeper handle and free up any resources
int zookeeper_close(zhandle_t *zh);
统一命名服务代码实现:
//nameService.cpp
#include
#include
#include
#include
#include
#include
using namespace std;
const int Clients = 3 ;
int connectd = 0 ;
//创建临时节点,并在完成之后,获得相应的名称
void *Named(void *);
//zookeeper会话的监视函数
void watcher(zhandle_t *zkh,int type, int state, const char *path,void *context);
//创建临时节点成功后的回调函数
void get_name_completion(int rc,const char *value, const void *data);
int main()
{
pthread_t threadID[Clients];
int err;
for(int i = 0 ; i < Clients ;++i)
{
err = pthread_create(&threadID[i], NULL, Named, NULL);
if(err != 0 )
cout<<"create new thread failed ..." <for(int i = 0 ; i < Clients ;++i)
{
err = pthread_join(threadID[i],NULL);
if(err)
cout<<"thread join failed ..."<return 0;
}
void *Named(void *arg)
{
unsigned long idx = pthread_self();
string host = "localhost:2181";
zhandle_t *zh;
zh = zookeeper_init(host.c_str(),watcher,15000,NULL,NULL,0);
if(NULL == zh)
{
cout<<"Init zookeeper handle failed, host:"<return NULL;
}
ostringstream oss;
oss<cout<<"threadID:"<string znode = "/nameservice";
//异步创建一个ZNODE,节点类型为 临时顺序节点(ZOO_EPHEMERAL | ZOO_SEQUENCE)
//由于不涉及ACL控制方式,acl设为ZOO_OPEN_ACL_UNSAFE
//同时通过strdup(oss.str().c_str())将节点内容传入get_name_completion回调函数以供调试
zoo_acreate(zh,znode.c_str(), oss.str().c_str(), oss.str().length(), &ZOO_OPEN_ACL_UNSAFE,
ZOO_EPHEMERAL | ZOO_SEQUENCE, get_name_completion, strdup(oss.str().c_str()));
sleep(5);//等待get_name_competion回调完成
zookeeper_close(zh);
return NULL;
}
void watcher(zhandle_t *zkh,int type, int state, const char *path,void *context)
{
if( ZOO_SESSION_EVENT == type)
{
if(state == ZOO_CONNECTED_STATE)
{
connectd = 1;
cout<<"Received a connectd event."<else if( state == ZOO_CONNECTING_STATE){
if(connectd == 1)
{
cout<<"Disconnected."<0 ;
} else if(state == ZOO_EXPIRED_SESSION_STATE){
connectd == 0;
zookeeper_close(zkh);
}
}
}
void get_name_completion(int rc,const char *value, const void *data)
{
if(rc == ZOK)
{
//可以用具有特定含义的字符串和value值拼接成名称,此处只用返回值作为名称
cout<<"threadID:"<<(const char*)data<<",get the name:"<free((void*)data); //strdup会调用malloc,必须手动释放这部分内存
}