亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

基于一致性hash算法 C++語言的實現詳解

2020-01-26 16:16:46
字體:
來源:轉載
供稿:網友

    一致性hash算法實現有兩個關鍵問題需要解決,一個是用于結點存儲和查找的數據結構的選擇,另一個是結點hash算法的選擇。

    首先來談一下一致性hash算法中用于存儲結點的數據結構。通過了解一致性hash的原理,我們知道結點可以想象為是存儲在一個環形的數據結構上(如下圖),結點A、B、C、D按hash值在環形分布上是有序的,也就是說結點可以按hash值存儲在一個有序的隊列里。如下圖所示,當一個hash值為-2^20的請求點P查找路由結點時,一致性hash算法會按hash值的順時針方向路由到第一個結點上(B),也就是相當于要在存儲結點的有序結構中,按查詢的key值找到大于key值中的最小的那個結點。因此,我們應該選擇一種數據結構,它應該高效地支持結點頻繁地增刪,也必須具有理想的查詢效率。那么,紅黑樹可以滿足這些要求。紅黑樹是一顆近似平衡的一顆二叉查找樹,因為操作比如插入、刪除和查找某個值的最壞情況時間都要求與樹的高度成比例,這個在高度上的理論上限允許紅黑樹在最壞情況下都是高效的,而不同于普通的二叉查找樹。 因此,我們選擇使用紅黑樹作為結點的存儲結構,除了需要實現紅黑樹基本的插入、刪除、查找的基本功能,我們還應該增加另一個查詢lookup函數,用于查找大于key中最小的結點。

image

   接下來,我們來說hash算法的選擇。一致性hash算法最初提出來,就是為了解決負載均衡的問題。每個實體結點會包含很多虛擬結點,虛擬結點是平衡負載的關鍵。我們希望虛擬結點可以均衡的散列在整個“環”上,這樣不僅可以負載到不同hash值的路由請求,還可以當某個結點down掉,原來路由到down掉結點的請求也可以較均衡的路由到其他結點而不會對某個結點造成大量的負載請求。這里,我們選擇使用MD5算法。通過MD5算法,可以將一個標示串(用于標示虛擬結點)轉化得到一個16字節的字符數組,再對該數組進行處理,得到一個整形的hash值。由于MD5具有高度的離散性,所以生成的hash值也會具有很大的離散性,會均衡的散列到“環”上。

   筆者用C++語言對一致性hash算法進行了實現,下面我將會描述下一些關鍵細節。

1、首先定義實體結點類、虛擬結點類。一個實體結點對應多個虛擬結點。

  實體結點 CNode_s:

復制代碼 代碼如下:

/*實體結點*/
class CNode_s
{
public:
    /*構造函數*/
    CNode_s();
    CNode_s(char * pIden , int pVNodeCount , void * pData);

    /*獲取結點標示*/
    const char * getIden();

    /*獲取實體結點的虛擬結點數量*/
    int getVNodeCount();

    /*設置實體結點數據值*/
    void setData(void * data);

    /*獲取實體結點數據值*/
    void * getData();
private:
    void setCNode_s(char * pIden, int pVNodeCount , void * pData);
    char iden[100];/*結點標示串*/
    int vNodeCount; /*虛擬結點數目*/
    void * data;/*數據結點*/
};

虛擬結點 CVirtualNode_s:虛擬結點有一指針指向實體結點
復制代碼 代碼如下:

/*虛擬結點*/
class CVirtualNode_s
{
public:
    /*構造函數*/
    CVirtualNode_s();
    CVirtualNode_s(CNode_s * pNode);

    /*設置虛擬結點所指向的實體結點*/
    void setNode_s(CNode_s * pNode);

    /*獲取虛擬結點所指向的實體結點*/
    CNode_s * getNode_s();

    /*設置虛擬結點hash值*/
    void setHash(long pHash);

    /*獲取虛擬結點hash值*/
    long getHash();
private:
    long hash; /*hash值*/
    CNode_s * node; /*虛擬結點所指向的實體結點*/
};


2、hash算法具有可選擇性,定義一個hash算法接口,方便以后進行其他算法的擴展。

      這里創建MD5hash類,并繼承該接口,通過MD5算法求hash值。

類圖:

image  

CHashFun接口:

復制代碼 代碼如下:

/*定義Hash函數類接口,用于計算結點的hash值*/

class CHashFun
{
public:
    virtual long getHashVal(const char *) = 0;
};

CMD5HashFun 類繼承CHashFun接口,實現獲取hash值的getHashVal函數:
復制代碼 代碼如下:

/*用MD5算法計算結點的hash值,繼承CHashFun父類*/
class CMD5HashFun : public CHashFun
{
public:
    virtual long getHashVal (const char * );
};

long CMD5HashFun::getHashVal(const char * instr)
{
    int i;
    long hash = 0;
    unsigned char digest[16];

    /*調用MD5相關函數,生成instr的MD5碼,存入digest*/
    md5_state_t md5state;
    md5_init(&md5state);
    md5_append(&md5state, (const unsigned char *)instr, strlen(instr));
    md5_finish(&md5state, digest);

    /* 每四個字節構成一個32位整數,
    將四個32位整數相加得到instr的hash值(可能溢出) */
    for(i = 0; i < 4; i++)
    {
        hash += ((long)(digest[i*4 + 3]&0xFF) << 24)
            | ((long)(digest[i*4 + 2]&0xFF) << 16)
            | ((long)(digest[i*4 + 1]&0xFF) <<  8)
            | ((long)(digest[i*4 + 0]&0xFF));
    }
    return hash;
}

3、擴展紅黑樹結構中的查找函數,用于查找紅黑樹中大于key值中最小的結點。
復制代碼 代碼如下:

util_rbtree_node_t* util_rbtree_lookup(util_rbtree_t *rbtree, long key)
{
    if((rbtree != NULL) && !util_rbtree_isempty(rbtree))
    {
        util_rbtree_node_t *node = NULL;
        util_rbtree_node_t *temp = rbtree->root;
        util_rbtree_node_t *null = _NULL(rbtree);
        while(temp != null)
        {
            if(key <= temp->key)
            {
                node = temp; /* update node */
                temp = temp->left;
            }
            else if(key > temp->key)
            {
                temp = temp->right;
            }
        }
        /* if node==NULL return the minimum node */
        return ((node != NULL) ? node : util_rbtree_min(rbtree));
    }
    return NULL;
}

4、創建一致性hash類。使其具有插入、刪除、查找實體結點的功能。

具體算法和操作過程已經在代碼注釋中說明。

復制代碼 代碼如下:

class CConHash
{
public:
    /*構造函數*/
    CConHash(CHashFun * pFunc);

    /*設置hash函數*/
    void setFunc(CHashFun * pFunc);

    /*增加實體結點 , 0代表成功 , -1代表失敗*/
    int addNode_s(CNode_s * pNode);

    /*刪除實體結點 , 0代表成功 , -1代表失敗*/
    int delNode_s(CNode_s * pNode);

    /*查找實體結點*/
    CNode_s * lookupNode_s(const char * object);

    /*獲取一致性hash結構的所有虛擬結點數量*/
    int getVNodes();
private:
    /*Hash函數*/
    CHashFun * func;
    /*虛擬結點總個數*/
    int vNodes;
    /*存儲虛擬結點的紅黑樹*/
    util_rbtree_t * vnode_tree;
};
/*輔助函數,虛擬結點轉化為紅黑樹結點*/
util_rbtree_node_t * vNode2RBNode(CVirtualNode_s * vnode);

 
CConHash::CConHash(CHashFun * pFunc)
{
    /*設置hash函數*/
    assert(pFunc!=NULL);
    this->func = pFunc;
    this->vNodes = 0;
    /*初始化紅黑樹*/
    vnode_tree = new util_rbtree_s();
    util_rbtree_init(vnode_tree);
}

int CConHash::addNode_s(CNode_s * pNode)
{
    if(pNode==NULL) return -1;
    int vCount = pNode->getVNodeCount();
    if(vCount<=0) return -1;
    CVirtualNode_s * virtualNode ;
    util_rbtree_node_t * rbNode;
    char str [100];
    char num[10];
    strcpy(str,pNode->getIden());
    long hash = 0;
    /*生成虛擬結點并插入到紅黑樹中*/
    for(int i=0;i<vCount;i++)
    {
        virtualNode = new CVirtualNode_s(pNode);
        /*采用str+“i”的方法產生不同的iden串,用于后面的hash值計算*/
        itoa(i,num,10);
        strcat(str,num);
        hash = func->getHashVal(str);
        virtualNode->setHash(hash);
        if(!util_rbtree_search(vnode_tree,hash))
        {
            /*生成紅黑樹結點*/
            rbNode = vNode2RBNode(virtualNode); 
            if(rbNode!=NULL)
            {
                /*將該結點插入到紅黑樹中*/
                util_rbtree_insert(vnode_tree,rbNode);
                this->vNodes++;
            }
        }
    }
    return 0;
}

int CConHash::delNode_s(CNode_s * pNode)
{
    if(pNode==NULL) return -1;
    util_rbtree_node_t * rbNode;
    char str [100];
    char num [10];
    strcpy(str,pNode->getIden()); 
    int vCount = pNode->getVNodeCount();
    long hash = 0;
    CVirtualNode_s * node = NULL;
    /*將該實體結點產生的所有虛擬結點進行刪除*/
    for(int i=0;i<vCount;i++)
    {
        itoa(i,num,10);
        strcat(str,num);/*采用該方法產生不同的iden串*/
        hash = func->getHashVal(str);
        rbNode = util_rbtree_search(vnode_tree,hash);
        if(rbNode!=NULL)
        {
            node = (CVirtualNode_s *) rbNode->data;
            if(node->getNode_s()==pNode && node->getHash()==hash)
            {
                this->vNodes--;
                /*將該結點從紅黑樹中刪除*/
                util_rbtree_delete(vnode_tree,rbNode);
                delete rbNode;
                delete node;
            }
        }
    }
    return 0;
}

CNode_s * CConHash::lookupNode_s(const char * object)
{
    if(object==NULL||this->vNodes==0) return NULL;
    util_rbtree_node_t * rbNode;
    int key = this->func->getHashVal(object);
    /*在紅黑樹中查找key值比key大的最小的結點*/
    rbNode = util_rbtree_lookup(vnode_tree,key);
    if(rbNode!=NULL)
    {
        return ((CVirtualNode_s *) rbNode->data)->getNode_s();
    }
    return NULL;
}

int CConHash::getVNodes()
{
    return this->vNodes;
}

 
util_rbtree_node_t * vNode2RBNode(CVirtualNode_s * vnode)
{
    if(vnode==NULL) return NULL;
    util_rbtree_node_t *rbNode = new util_rbtree_node_t(); 
    rbNode->key = vnode->getHash();
    rbNode->data = vnode;
    return rbNode;
}

5、創建一個客戶端類,對一致性hash算法進行測試。

      寫了一個getIP的函數,模擬隨機產生的IP字符串。

復制代碼 代碼如下:

#include<iostream>
#include"CNode_s.h"
#include"CVirtualNode_s.h"
#include"CHashFun.h"
#include"CMD5HashFun.h"
#include"CConHash.h"
#include<string.h>
#include<time.h>

using namespace std;

void getIP(char * IP)
{
    int a=0, b=0 , c=0 , d=0;
    a = rand()%256;
    b = rand()%256;
    c = rand()%256;
    d = rand()%256;
    char aa[4],bb[4],cc[4],dd[4];
    itoa(a, aa, 10);
    itoa(b, bb, 10);
    itoa(c, cc, 10);
    itoa(d, dd, 10);
    strcpy(IP,aa);
    strcat(IP,".");
    strcat(IP,bb);
    strcat(IP,".");
    strcat(IP,cc);
    strcat(IP,".");
    strcat(IP,dd);
}

int main()
{
    srand(time(0));
    freopen("out.txt","r",stdin);
    /*定義hash函數*/
    CHashFun * func = new CMD5HashFun();
    /*創建一致性hash對象*/
    CConHash * conhash = new CConHash(func);

    /*定義CNode*/
    CNode_s * node1 = new CNode_s("machineA",50,"10.3.0.201");
    CNode_s * node2 = new CNode_s("machineB",80,"10.3.0.202");
    CNode_s * node3 = new CNode_s("machineC",20,"10.3.0.203");
    CNode_s * node4 = new CNode_s("machineD",100,"10.3.0.204");

    conhash->addNode_s(node1);
    conhash->addNode_s(node2);
    conhash->addNode_s(node3);
    conhash->addNode_s(node4);

    /*動態更改結點數據值*/
//  node1->setData("99999999");

    int ans1 ,ans2 ,ans3 ,ans4;
    ans1=ans2=ans3=ans4=0;

    char object[100];
    CNode_s * node ;
    /*動態刪除結點*/
    //conhash->delNode_s(node2);
    for(int i =0;i<30;i++)
    {
    //  getIP(object);
    //  cout<<object<<endl;
        cin>>object;
        node = conhash->lookupNode_s(object);
        if(node!=NULL)
        {
            cout<<object<<"----->/t"<<node->getIden()<<" /t "<<(char *)node->getData()<<endl;
            if(strcmp(node->getIden(),"machineA")==0) ans1++;
            if(strcmp(node->getIden(),"machineB")==0) ans2++;
            if(strcmp(node->getIden(),"machineC")==0) ans3++;
            if(strcmp(node->getIden(),"machineD")==0) ans4++;
        }
    }

    cout<<"Total test cases : "<<ans1+ans2+ans3+ans4<<endl;
    cout<<"Map to MachineA : "<<ans1<<endl;
    cout<<"Map to MachineB : "<<ans2<<endl;
    cout<<"Map to MachineC : "<<ans3<<endl;
    cout<<"Map to MachineD : "<<ans4<<endl;
    fclose(stdin);
    return 0;
}

6、刪除結點對hash路由的影響測試

image

測試結果截圖:

imageimage

分析:上面兩幅圖,左邊為原始四個實體結點的路由情況,后面為刪除結點2(Node2)之后的路由情況。不難發現,MachineB down之后,原先的路由請求,較均衡地負載到了其他機器結點,而且對原先路由到其他結點的請求沒有影響。比如139.149.184.125這個請求仍會路由到MachineD,并不會因為結點的減少而造成影響。但是,如果是增加實體結點,可能會造成增加前后路由情況不一致的現象,因為路由區間的更加狹小,但是不會有特別大的影響。 另一方面,可以發現實體結點的虛擬結點個數比例分配情況很大程度影響了結點的負載路由情況,比例大致與虛擬結點個數相一致。

總結:

   本文首先通過介紹實現一致性hash算法的關鍵算法和數據結構的選擇分析,選擇了紅黑樹作為虛擬結點的存儲結構,以及MD5算法作為Hash函數用于計算結點的hash值。并使用C++語言,對一致性hash算法進行了實現,實現了一致性hash實體結點的增加、刪除、查找等基本功能,并進行了測試分析。由于筆者水平有限,存在很多有待改進的地方,因此本文僅供大家參考、討論學習。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
最新69国产成人精品视频免费| 亚洲xxxx18| 91爱视频在线| 中文字幕亚洲一区在线观看| 亚洲精品一区二区三区婷婷月| 精品毛片三在线观看| 亚洲欧美国产日韩天堂区| 秋霞午夜一区二区| 久久久www成人免费精品张筱雨| 久久99国产精品久久久久久久久| 国产福利精品av综合导导航| 日韩精品在线观看一区| 91亚洲精品一区| 国产精品成人免费电影| 欧美激情一区二区久久久| 亚洲xxxx视频| 国产综合久久久久| 精品人伦一区二区三区蜜桃网站| 日韩av片电影专区| 国产精品嫩草影院久久久| 亚洲欧美国产精品| 欧美亚洲国产精品| 在线精品91av| 91亚洲精品视频| 欧美激情视频一区二区| 亚洲激情视频在线| 国产精品成人一区二区三区吃奶| 久久亚洲精品国产亚洲老地址| 国产亚洲精品美女久久久| 最近日韩中文字幕中文| 午夜精品视频在线| 91精品国产综合久久香蕉| 日韩电影免费观看中文字幕| 亚洲日韩欧美视频一区| 亚洲一区二区三区四区在线播放| 国产成人精品免费视频| 91麻豆国产精品| 欧美性xxxx在线播放| 亚洲第一综合天堂另类专| 精品色蜜蜜精品视频在线观看| 国产福利精品视频| 永久免费精品影视网站| 5566日本婷婷色中文字幕97| 欧美亚洲另类激情另类| 欧美激情视频一区二区| 精品福利在线视频| 亚洲中国色老太| 这里只有精品在线播放| 亚洲女人被黑人巨大进入al| 国产精品视频网站| 亚洲欧洲在线播放| 成人性生交大片免费看视频直播| 久久精品亚洲一区| 亚洲成人黄色在线观看| 久久久久久久久久久人体| 国产精品最新在线观看| 91久久国产综合久久91精品网站| 国产在线一区二区三区| 国产精品午夜一区二区欲梦| 国产小视频国产精品| 欧美巨大黑人极品精男| 国产精品人成电影在线观看| 日韩中文字幕网址| 日韩av电影在线网| 久久综合免费视频| 国产欧美一区二区白浆黑人| 日韩电影中文字幕一区| 亚洲综合中文字幕68页| 97av视频在线| 久久天天躁狠狠躁夜夜躁| 福利二区91精品bt7086| 国产精品免费一区二区三区都可以| 亚洲娇小xxxx欧美娇小| 在线电影欧美日韩一区二区私密| 国产在线不卡精品| 国产成人久久久精品一区| 91性高湖久久久久久久久_久久99| 日韩欧美在线第一页| 日韩在线观看免费高清| 久久久久久久久久国产| 91成人在线播放| 亚洲精品小视频在线观看| 欧美成人免费全部| 国产视频久久久久久久| 中国日韩欧美久久久久久久久| 91日本视频在线| 久久精品中文字幕| 一道本无吗dⅴd在线播放一区| 日韩中文字幕av| 欧美成人激情视频免费观看| 中文字幕亚洲专区| 国产精品日韩一区| 久久天天躁狠狠躁夜夜av| 色偷偷91综合久久噜噜| 庆余年2免费日韩剧观看大牛| 欧美特黄级在线| 精品无码久久久久久国产| 一本大道亚洲视频| 日韩久久午夜影院| 欧美午夜精品久久久久久浪潮| 在线观看久久av| 成人免费福利在线| 国产中文字幕日韩| 欧美激情aaaa| 欧美日韩高清区| 日韩精品在线第一页| 成人免费激情视频| 欧美日韩一区二区免费视频| 亚洲小视频在线观看| 一区二区欧美日韩视频| 国产精品青青在线观看爽香蕉| 成人有码在线播放| 欧美综合在线观看| 91丨九色丨国产在线| 欧美性猛交xxxx免费看| 欧美日韩一区二区三区在线免费观看| 国产亚洲欧洲高清一区| 日韩av片免费在线观看| 国内伊人久久久久久网站视频| 成人免费在线视频网址| 欧美成年人视频网站欧美| 日本一区二区在线免费播放| 91在线观看欧美日韩| 久久精品电影一区二区| 亚洲综合视频1区| 国产精品美女久久| 亚洲一区二区三区视频| 欧美日韩亚洲一区二区三区| 精品亚洲精品福利线在观看| 亚洲国产精品va在线观看黑人| 91免费欧美精品| 国产亚洲欧美日韩精品| 92福利视频午夜1000合集在线观看| 国产精品爱久久久久久久| 97国产真实伦对白精彩视频8| 国产午夜精品免费一区二区三区| 麻豆一区二区在线观看| 久久久久久com| 国产免费一区视频观看免费| 日韩中文字幕视频在线| 在线午夜精品自拍| 久久国产精品久久精品| 26uuu日韩精品一区二区| 欧美成人午夜免费视在线看片| 国产91精品久久久久久| 国产91精品久久久久久| 26uuu国产精品视频| 亚洲高清久久久久久| 精品美女永久免费视频| 亚洲欧洲在线看| 亚洲精品白浆高清久久久久久| 国产欧美婷婷中文| 国产亚洲美女久久| 性金发美女69hd大尺寸| 国产视频丨精品|在线观看| 一本色道久久88综合日韩精品| 国产精品老女人视频| 爽爽爽爽爽爽爽成人免费观看| 亚洲精品xxxx| 国产亚洲精品久久久久久777| 都市激情亚洲色图| 亚洲开心激情网| 国产精品亚洲激情| 日韩大片免费观看视频播放|