howtodown 发表于 2015-3-9 11:36:41

Zookeeper场景实践:命名服务

本帖最后由 howtodown 于 2015-3-9 11:38 编辑

问题导读

1.什么是命名服务?
2.命名服务如何发布?
3.服务消费者启动包含哪些操作?


static/image/hrline/4.gif



命名服务
是指通过指定的名字来获取资源或者服务的地址,提供者的信息。利用Zookeeper很容易创建一个全局的路径,而这个路径就可以作为一个名字,它可以指向集群中的集群,提供的服务的地址,远程对象等。简单来说使用Zookeeper做命名服务就是用路径作为名字,路径上的数据就是其名字指向的实体。阿里巴巴集团开源的分布式服务框架Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表。在Dubbo实现中:服务提供者在启动的时候,向ZK上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布。服务消费者启动的时候,订阅/dubbo/{serviceName}/providers目录下的提供者URL地址, 并向/dubbo/{serviceName} /consumers目录下写入自己的URL地址。注意,所有向ZK上注册的地址都是临时节点,这样就能够保证服务提供者和消费者能够自动感应资源的变化。另外,Dubbo还有针对服务粒度的监控,方法是订阅/dubbo/{serviceName}目录下所有提供者和消费者的信息。
场景实践上面的介绍已经满详细,实际实现起来也比较容易。下面讲讲模拟程序的主要特点。模拟程序有3个参数
[*]-m 程序运行的方式,指定是服务提供者provider还是服务消费者consumer,或者是服务监控者monitor
[*]-n 表示服务名称
[*]-s 表示Zookeeper的服务地址IP:PORT
运行命令如下:
服务提供者:
>nameservice -m provider -n query_bill -s172.17.0.36:2181
服务消费者:
>nameservice -m consumer -n query_bill -s172.17.0.36:2181
服务监控者:
>nameservice -m monitor -n query_bill -s172.17.0.36:2181
第一条命令是启动一个服务提供进程,它提供了一个名为query_bill的服务,程序首次运行时会创建
/NameService,/NameService/query_bill,/NameService/query_bill/provider,/NameService/query_bill/consumer/等几个路径。然后在服务提供进程在/NameService/query_bill/provider下创建临时序列节点.第二条命令是启动一个服务消费进程,它在/NameService/query_bill/consumer/下创建临时序列节点,并watch/NameService/query_bill/provider的子节点变化事件,及时更新provider列表。第三条命令是启动一个服务监控进程,它watch/NameService/query_bill/provider,/NameService/query_bill/consumer/两个路径的子节点变化,及时更新provider列表和comsumer列表。完整的代码如下:#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include"zookeeper.h"
#include"zookeeper_log.h"

enum MODE{PROVIDER_MODE,CONSUMER_MODE,MONITOR_MODE} g_mode;
char g_host= "172.17.0.36:2181";
char g_service={ 0 };
char g_path="/NameService";

//watch function when child list changed
void zktest_watcher_g(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx);
//show all process ip:pid
void show_list(zhandle_t *zkhandle,const char *path);
//if success,the g_mode will become MODE_MONITOR
void choose_mater(zhandle_t *zkhandle,const char *path);
//get localhost ip:pid
void getlocalhost(char *ip_pid,int len);

void print_usage();
void get_option(int argc,const char* argv[]);

/**********unitl*********************/
void print_usage()
{
    printf("Usage : [-h] [-m mode] [-n servicename] [-s ip:port] \n");
    printf("      -h Show help\n");
    printf("      -m set mode:provider,consumer,monitor\n");
    printf("      -n set servicename\n");
    printf("      -s server ip:port\n");
    printf("For example:\n");
    printf("    nameservice -m provider -n query_bill -s172.17.0.36:2181 \n");
    printf("    nameservice -m consumer -n query_bill -s172.17.0.36:2181 \n");
    printf("    nameservice -m monitor-n query_bill -s172.17.0.36:2181 \n");
}

void get_option(int argc,const char* argv[])
{
    extern char    *optarg;
    int            optch;
    int            dem = 1;
    const char    optstring[] = "hm:n:s:";


    while((optch = getopt(argc , (char * const *)argv , optstring)) != -1 )
    {
      switch( optch )
      {
      case 'h':
            print_usage();
            exit(-1);
      case '?':
            print_usage();
            printf("unknown parameter: %c\n", optopt);
            exit(-1);
      case ':':
            print_usage();
            printf("need parameter: %c\n", optopt);
            exit(-1);
      case 'm':
            if (strcasecmp(optarg,"provider") == 0){
                g_mode = PROVIDER_MODE;
            }else if (strcasecmp(optarg,"consumer") == 0){
                g_mode = CONSUMER_MODE;
            }else{
                g_mode = MONITOR_MODE;
            }
            break;
      case 'n':
            strncpy(g_service,optarg,sizeof(g_service));
            break;
      case 's':
            strncpy(g_host,optarg,sizeof(g_host));
            break;
      default:
            break;
      }
    }
}
void zktest_watcher_g(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx)
{
/*
    printf("watcher event\n");
    printf("type: %d\n", type);
    printf("state: %d\n", state);
    printf("path: %s\n", path);
    printf("watcherCtx: %s\n", (char *)watcherCtx);
*/

    if(type == ZOO_CHILD_EVENT &&
       state == ZOO_CONNECTED_STATE &&
       g_mode == CONSUMER_MODE){

      printf("providers list changed!\n");
      show_list(zh,path);
    }else if(type == ZOO_CHILD_EVENT &&
             state == ZOO_CONNECTED_STATE &&
             g_mode == MONITOR_MODE){

      printf("providers or consumers list changed!\n");

      char child_path;
      printf("providers:\n");
      sprintf(child_path,"%s/%s/provider",g_path,g_service);
      show_list(zh,child_path);

      printf("consumers:\n");
      sprintf(child_path,"%s/%s/consumer",g_path,g_service);
      show_list(zh,child_path);
    }
}
void getlocalhost(char *ip_pid,int len)
{
    char hostname = {0};
    struct hostent *hent ;

    gethostname(hostname,sizeof(hostname));
    hent = gethostbyname(hostname);

    char * localhost = inet_ntoa(*((struct in_addr*)(hent->h_addr_list)));

    snprintf(ip_pid,len,"%s:%d",localhost,getpid());
}

void show_list(zhandle_t *zkhandle,const char *path)
{

    struct String_vector procs;
    int i = 0;
    char localhost={0};

    getlocalhost(localhost,sizeof(localhost));

    int ret = zoo_get_children(zkhandle,path,1,&procs);

    if(ret != ZOK){
      fprintf(stderr,"failed to get the children of path %s!\n",path);
    }else{
      char child_path ={0};
      char ip_pid = {0};
      int ip_pid_len = sizeof(ip_pid);
      printf("--------------\n");
      printf("ip\tpid\n");
      for(i = 0; i < procs.count; ++i){
            sprintf(child_path,"%s/%s",path,procs.data);
            //printf("%s\n",child_path);
            ret = zoo_get(zkhandle,child_path,0,ip_pid,&ip_pid_len,NULL);
            if(ret != ZOK){
                fprintf(stderr,"failed to get the data of path %s!\n",child_path);
            }else if(strcmp(ip_pid,localhost)==0){
                printf("%s(Master)\n",ip_pid);
            }else{
                printf("%s\n",ip_pid);
            }
      }
    }

    for(i = 0; i < procs.count; ++i){
      free(procs.data);
      procs.data = NULL;
    }
}
int create(zhandle_t *zkhandle,const char *path,const char *ctx,int flag)
{
    char path_buffer;
    int bufferlen=sizeof(path_buffer);

    int ret = zoo_exists(zkhandle,path,0,NULL);
    if(ret != ZOK){
      ret = zoo_create(zkhandle,path,ctx,strlen(ctx),
                        &ZOO_OPEN_ACL_UNSAFE,flag,
                        path_buffer,bufferlen);
      if(ret != ZOK){
            fprintf(stderr,"failed to create the path %s!\n",path);
      }else{
            printf("create path %s successfully!\n",path);
      }
    }

    return ZOK;
}

int main(int argc, const char *argv[])
{
    int timeout = 30000;
    char path_buffer;
    int bufferlen=sizeof(path_buffer);
    int ret = 0;
    zoo_set_debug_level(ZOO_LOG_LEVEL_ERROR); //设置日志级别,避免出现一些其他信息

    get_option(argc,argv);

    zhandle_t* zkhandle = zookeeper_init(g_host,zktest_watcher_g, timeout, 0, (char *)"NameService Test", 0);

    if (zkhandle ==NULL)
    {
      fprintf(stderr, "Error when connecting to zookeeper servers...\n");
      exit(EXIT_FAILURE);
    }

    create(zkhandle,g_path,"NameService Test",0);

    sprintf(path_buffer,"%s/%s",g_path,g_service);
    create(zkhandle,path_buffer,"NameService Test",0);

    sprintf(path_buffer,"%s/%s/provider",g_path,g_service);
    create(zkhandle,path_buffer,"NameService Test",0);

    sprintf(path_buffer,"%s/%s/consumer",g_path,g_service);
    create(zkhandle,path_buffer,"NameService Test",0);

    if(g_mode == PROVIDER_MODE){

      char localhost={0};
      getlocalhost(localhost,sizeof(localhost));

      char child_path;
      sprintf(child_path,"%s/%s/provider/",g_path,g_service);
      ret = zoo_create(zkhandle,child_path,localhost,strlen(localhost),
                        &ZOO_OPEN_ACL_UNSAFE,ZOO_SEQUENCE|ZOO_EPHEMERAL,
                        path_buffer,bufferlen);
      if(ret != ZOK){
            fprintf(stderr,"failed to create the child_path %s,buffer:%s!\n",child_path,path_buffer);
      }else{
            printf("create child path %s successfully!\n",path_buffer);
      }

    }else if (g_mode == CONSUMER_MODE){

      char localhost={0};
      getlocalhost(localhost,sizeof(localhost));

      char child_path;
      sprintf(child_path,"%s/%s/consumer/",g_path,g_service);
      ret = zoo_create(zkhandle,child_path,localhost,strlen(localhost),
                        &ZOO_OPEN_ACL_UNSAFE,ZOO_SEQUENCE|ZOO_EPHEMERAL,
                        path_buffer,bufferlen);
      if(ret != ZOK){
            fprintf(stderr,"failed to create the child_path %s,buffer:%s!\n",child_path,path_buffer);
      }else{
            printf("create child path %s successfully!\n",path_buffer);
      }

      sprintf(child_path,"%s/%s/provider",g_path,g_service);
      show_list(zkhandle,child_path);

    }else if(g_mode == MONITOR_MODE){
      char child_path;
      printf("providers:\n");
      sprintf(child_path,"%s/%s/provider",g_path,g_service);
      show_list(zkhandle,child_path);

      printf("consumers:\n");
      sprintf(child_path,"%s/%s/consumer",g_path,g_service);
      show_list(zkhandle,child_path);
    }

    getchar();

    zookeeper_close(zkhandle);

    return 0;
}
相关文章:zookeeper原理
zookeeper中Watcher和Notifications
zookeeper适用场景:如何竞选Master及代码实现
zookeeper适用场景:配置文件同步
zookeeper适用场景:分布式锁实现
zookeeper适用场景:zookeeper解决了哪些问题




ainubis 发表于 2015-3-29 06:43:33


学习了(*^__^*) 嘻嘻……

spftoto 发表于 2018-7-17 17:01:37

“”阿里巴巴集团开源的分布式服务框架Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表。“”这个可否说的更详细点 究竟是怎么利用统一命名的呀?服务提供者在zookeeper中注册一个节点,然后将自己的URL作为那个节点的数据写进去。然后消费者是怎么访问这个服务的呢?怎么通过zookeeper的这个统一命名空间访问服务的呢?大佬,可否说的更详细点,谢谢了。
页: [1]
查看完整版本: Zookeeper场景实践:命名服务