Zookeeper場景實踐:(8) 分佈式隊列

1.基本介紹

按照ZooKeeper典型應用場景一覽裏的說法,分佈式隊列有兩種,一種是常規的先進先出隊列,另一種是要等到隊列成員聚齊之後的才統一按序執行。

第二種隊列可以先建立一個/queue,賦值爲n,表達隊列的大小。然後每個隊列成員加入時,就判斷是否達到隊列要求的大小,如果是可以進行下一步動作,否則繼續等待隊列成員的加入。比較典型的情況是,當一個大的任務可能需要很多的子任務完成才能開始進行。

比如彙總賬單的時候,就必須先將用戶的消費數據,積分數據等都統計完成後才能開始。彙總賬單的程序建立一個隊列/Queue,賦值爲2,然後分別統計消費數據和積分數據的程序當完成任務時就往/Queue下創建一個臨時節點。而彙總賬單程序監測到/Queue的子節點個數爲2時,就可以開始執行任務了。

實際上,我們也可以先建立一個數目爲2的子節點。當一個子任務完成的時候,就刪除一個子節點,當所有子節點都被刪除的時候,主任務就可以開始執行了。這個過程可以形象的理解爲拆除屏障。因此這種隊列還有一個專門的詞語描述,叫做屏障(barrier)。

2.場景分析

講了那麼多的關於屏障的認識,但是並不打算就去實現它,並且Zookeeper的官方文檔也有相關的知識。這次的主要目標是常規的FIFO隊列。我將實現隊列的兩個主要操作:push和pop。

1). int push(zhandle_t *zkhandle,const char *path,char *element)

  • zkhandlezookeeper_init初始化後的句柄
  • path爲隊列的路徑
  • element爲要壓入隊列的內容

2). int pop(zhandle_t *zkhandle,const char *path,char *element_buffer,int *buffer_len)

  • zkhandlezookeeper_init初始化後的句柄
  • path爲隊列的路徑
  • element_buffer爲要彈出的緩衝區
  • buffer_len爲指向緩衝區的大小的指針

簡單來說,假設隊列的路徑爲/Queue,push就是就是創建一個臨時有序的/Queue/queue-節點。pop就是取出/Queue/下序列號最小的節點。
我們知道在C++中stl裏有一個queue的類,實現了push,pop等操作,然而它是非線程安全的,即多個線程同時push/pop的時候可能會出現錯誤。而由於ZooKeeper保證了創建節點和刪除節點的一致性,因此可以說利用Zookeeper實現的隊列是進程安全的。

3. 場景實踐

來看push和pop的具體實現。push的實現很簡單,就是在{path}下創建一個有序的{path}/queue-子節點.

int push(zhandle_t *zkhandle,const char *path,char *element)
{
    char child_path[512] = {0};
    char path_buffer[512] = {0};
    int bufferlen = sizeof(path_buffer);

    sprintf(child_path,"%s/queue-",path);
    int ret = zoo_create(zkhandle,child_path,element,strlen(element),  
                     &ZOO_OPEN_ACL_UNSAFE,ZOO_SEQUENCE,  
                     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 ret;
}


pop的功能則是取出{path}下序號最小的子節點,如果沒有子節點,則返回-1.

int pop(zhandle_t *zkhandle,const char *path,char *element,int *len)
{
    int i = 0;
    struct String_vector children;
    int ret = zoo_get_children(zkhandle,path,0,&children);


    if(ret != ZOK){
        fprintf(stderr,"failed to create the path %s!\n",path);
    }else if (children.count == 0){
        strcpy(element,"");
        *len = 0;
        ret = -1;
    }else{
        char *min = children.data[0];
        for(i = 0; i < children.count; ++i){
            printf("%s:%s\n",min,children.data[i]);
            if(strcmp(min,children.data[i]) > 0){
                min = children.data[i];
            }
        }
        if(min != NULL){
            char child_path[512]={0};
            sprintf(child_path,"%s/%s",path,min);
            ret = zoo_get(zkhandle,child_path,0,element,len,NULL);

            if(ret != ZOK){
                fprintf(stderr,"failed to get data of the path %s!\n",child_path);
            }else{
                ret = zoo_delete(zkhandle,child_path, -1);

                if(ret != ZOK){
                    fprintf(stderr,"failed to delete the path %s!\n",child_path);
                }
            }
        }
    }

    for(i = 0; i < children.count; ++i){
        free(children.data[i]);
        children.data[i] = NULL;
    }


    return ret;
}


最後,再來看看模擬隊列操作的程序。和其他程序類似,它的選項有

  • -p:指定隊列的路徑
  • -m:指定操作是push還是pop
  • -v:只在push時有用,用與指定要push的元素的值
  • -s:指定Zookeeper的服務器的ip:port.

如:

向隊列/Queue中壓人一個元素,元素的值爲"Hello":

>myqueue -s 172.17.0.36:2181 -p /Queue -m push -v Hello

將隊列/Queue彈出一個元素

>myqueue -s 172.17.0.36:2181 -p /Queue -m pop


最後附上完整的源代碼:

#include<stdio.h>  
#include<string.h>  
#include<unistd.h>
#include"zookeeper.h"  
#include"zookeeper_log.h"  

char g_host[512]= "172.17.0.36:2181";  
char g_path[512]= "/Queue";
char g_value[512]="msg";
enum MODE{PUSH_MODE,POP_MODE} g_mode;

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

/**********unitl*********************/  
void print_usage()
{
    printf("Usage : [myqueue] [-h] [-m mode] [-p path ] [-v value][-s ip:port] \n");
    printf("        -h Show help\n");
    printf("        -p Queue path\n");
    printf("        -m mode:push or pop\n");
    printf("        -v the value you want to push\n");
    printf("        -s zookeeper server ip:port\n");
    printf("For example:\n");
    printf("    push the message \"Hello\" into the queue Queue:\n");
    printf("        >myqueue -s172.17.0.36:2181 -p /Queue -m push -v Hello\n");
    printf("    pop one message from the queue Queue:\n");
    printf("        >myqueue -s172.17.0.36:2181 -p /Queue -m pop\n");
}

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


    g_mode = PUSH_MODE;
    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,"push")==0){
                g_mode = PUSH_MODE;
            }else{
                g_mode = POP_MODE;
            }
            break;
        case 's':
            strncpy(g_host,optarg,sizeof(g_host));
            break;
        case 'p':
            strncpy(g_path,optarg,sizeof(g_path));
            break;
        case 'v':
            strncpy(g_value,optarg,sizeof(g_value));
            break;
        default:
            break;
        }
    }
} 

int push(zhandle_t *zkhandle,const char *path,char *element)
{
    char child_path[512] = {0};
    char path_buffer[512] = {0};
    int bufferlen = sizeof(path_buffer);

    sprintf(child_path,"%s/queue-",path);
    int ret = zoo_create(zkhandle,child_path,element,strlen(element),  
                     &ZOO_OPEN_ACL_UNSAFE,ZOO_SEQUENCE,  
                     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 ret;
}

int pop(zhandle_t *zkhandle,const char *path,char *element,int *len)
{
    int i = 0;
    struct String_vector children;
    int ret = zoo_get_children(zkhandle,path,0,&children);


    if(ret != ZOK){
        fprintf(stderr,"failed to create the path %s!\n",path);
    }else if (children.count == 0){
        strcpy(element,"");
        *len = 0;
        ret = -1;
    }else{
        char *min = children.data[0];
        for(i = 0; i < children.count; ++i){
            printf("%s:%s\n",min,children.data[i]);
            if(strcmp(min,children.data[i]) > 0){
                min = children.data[i];
            }
        }
        if(min != NULL){
            char child_path[512]={0};
            sprintf(child_path,"%s/%s",path,min);
            ret = zoo_get(zkhandle,child_path,0,element,len,NULL);

            if(ret != ZOK){
                fprintf(stderr,"failed to get data of the path %s!\n",child_path);
            }else{
                ret = zoo_delete(zkhandle,child_path, -1);

                if(ret != ZOK){
                    fprintf(stderr,"failed to delete the path %s!\n",child_path);
                }
            }
        }
    }

    for(i = 0; i < children.count; ++i){
        free(children.data[i]);
        children.data[i] = NULL;
    }


    return ret;
}

int front(zhandle_t *zkhandle,char *path,char *element,int *len)
{
    int i = 0;
    struct String_vector children;
    int ret = zoo_get_children(zkhandle,path,0,&children);

    if(ret != ZOK){
        fprintf(stderr,"failed to create the path %s!\n",path);
    }else if(children.count == 0){
        strcpy(element,"");
        *len = 0;
        ret = -1;
    }else{
        char *min = NULL;
        for(i = 0; i < children.count; ++i){
            if(strcmp(min,children.data[i]) > 0){
                min = children.data[i];
            }
        }
        if(min != NULL){
            char child_path[512]={0};
            sprintf(child_path,"%s/%s",path,min);
            ret = zoo_get(zkhandle,child_path,0,element,len,NULL);

            if(ret != ZOK){
                fprintf(stderr,"failed to get data of the path %s!\n",child_path);
            }
        }
    }

    for(i = 0; i < children.count; ++i){
        free(children.data[i]);
        children.data[i] = NULL;
    }

    return ret;

}


int main(int argc, const char *argv[])  
{  
    int timeout = 30000;  
    char path_buffer[512];  
    int bufferlen=sizeof(path_buffer);  

    zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); //設置日誌級別,避免出現一些其他信息  

    get_option(argc,argv);

    zhandle_t* zkhandle = zookeeper_init(g_host,NULL, timeout, 0, (char *)"lock Test", 0);  

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

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

    if(g_mode == PUSH_MODE){
        push(zkhandle,g_path,g_value); 
        printf("push:%s\n",g_value);
    }else{
        int len = sizeof(g_value);
        ret = pop(zkhandle,g_path,g_value,&len) ;

        if(ret == ZOK){
            printf("pop:%s\n",g_value);
        }else if( ret == -1){
            printf("queue is empty\n");
        }
    }



    zookeeper_close(zkhandle); 

    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章