用于一致性散列的C库源代码简介。

什么是libconhash

libconhash是一个一致的哈希库,可以在Windows和Linux平台上编译,具有以下功能:

  1. libconhash高性能且易于使用,它使用红黑树来管理所有节点以实现高性能。
  2. 默认情况下,它使用MD5算法,但它也支持用户定义的哈希函数。
  3. 易于根据节点的处理能力进行扩展。

一致的散列

为什么需要一致的散列

现在我们将考虑进行负载平衡的常用方法。选择用于缓存对象o的机器号将是:

hash(o) mod n

这里,n是缓存机器的总数。虽然这在您添加或删除缓存机器之前仍然有效:

  1. 添加缓存机器时,对象o将缓存到机器中:
hash(o) mod (n+1)
  1. 删除缓存计算机时,对象o将缓存到计算机中:
hash(o) mod (n-1)

因此,您可以看到几乎所有对象都将散列到一个新位置。这将是一场灾难,因为原始内容服务器被来自缓存机器的请求所淹没。这就是为什么你需要一致的散列。

一致的散列可以保证在删除缓存机器时,只重新缓存其中的对象; 添加新的缓存机器时,只会刷新相当少的对象。

现在我们将逐步进行一致的散列。

哈希空间

通常,哈希函数会将映射到32位密钥,​​0~2^<sup>32</sup>-1​​。现在想象一下将范围映射到一个圆圈,然后键将被包裹,0将跟随2 ^ 32 -1,如图1所示。

图1

将对象映射到哈希空间

现在考虑四个对象:​​object1~object4​​。我们使用哈希函数来获取它们的键值并将它们映射到圆圈中,如图2所示。

一致性哈希_一致性哈希库

图2

hash(object1) = key1;
.....
hash(object4) = key4;

将缓存映射到哈希空间

一致散列的基本思想是使用相同的散列函数将缓存和对象映射到相同的散列空间。

现在考虑我们有三个缓存,A,B和C,然后映射结果如图3所示。

hash(cache A) = key A;
....
hash(cache C) = key C;

一致性哈希_一致性哈希库_02

图3

将对象映射到缓存中

现在所有的缓存和对象都被散列到同一个空间中,因此我们可以确定如何将对象映射到缓存中。以物体​​obj​​​为例,只需从​​obj​​环形处开始,顺时针转动环,直至找到服务器。如果该服务器已关闭,则转到下一个服务器,依此类推。见上面的图3。

根据该方法,​​object1​​​将缓存到缓存A中; ​​object2​​​并将​​object3​​​缓存到缓存C中,并将​​object4​​缓存到缓存B.

添加或删除缓存

现在考虑两种情况,缓存已关闭并被删除; 并添加了一个新的缓存。

如果删除缓存B,则只有在B中缓存的对象将被重新散列并移动到C; 在示例中,请参​​object4​​见图4。

一致性哈希_服务器_03

图4

如果添加了新的高速缓存D,并且在环之间​​object2​​​和​​object3​​​环中对D进行了散列,则只有D和B之间的对象将被重新散列; 在示例中,请参​​object2​​见图5。

一致性哈希_一致性哈希_04

图5

虚拟节点

如果没有部署足够的缓存,则可能在缓存之间具有非常不均匀的对象分布。解决方案是引入“虚拟节点”的概念。

虚拟节点是圆圈中缓存点的复制品,每个真实缓存对应于圆圈中的几个虚拟节点; 实际上,每当我们添加缓存时,我们都会在圆圈中创建一些虚拟节点; 当删除缓存时,我们从圆圈中删除其所有虚拟节点。

考虑上面的例子。系统中有两个缓存A和C,现在我们引入虚拟节点,副本为2,然后三个将是4个虚拟节点。缓存A1和缓存A2代表缓存A; 高速缓存C1和高速缓存C2表示高速缓存C,如图6所示。

一致性哈希_一致性哈希_05

图6

然后,从对象到虚拟节点的映射将是:

objec1->cache A2; objec2->cache A1; objec3->cache C1; objec4->cache C2

一致性哈希_缓存_06

当您获得虚拟节点时,您将获得缓存,如上图所示。

因此,object1和object2被缓存到缓存A中,而object3和object4被缓存到缓存中。结果现在更平衡。

所以现在你知道什么是一致的散列。

使用代码

libconhash的接口

/* initialize conhash library
* @pfhash : hash function, NULL to use default MD5 method
* return a conhash_s instance
*/
CONHASH_API struct conhash_s* conhash_init(conhash_cb_hashfunc pfhash);

/* finalize lib */
CONHASH_API void conhash_fini(struct conhash_s *conhash);

/* set node */
CONHASH_API void conhash_set_node(struct node_s *node,
const char *iden, u_int replica);

/*
* add a new node
* @node: the node to add
*/
CONHASH_API int conhash_add_node(struct conhash_s *conhash,
struct node_s *node);

/* remove a node */
CONHASH_API int conhash_del_node(struct conhash_s *conhash,
struct node_s *node);
...

/*
* lookup a server which object belongs to
* @object: the input string which indicates an object
* return the server_s structure, do not modify the value,
* or it will cause a disaster
*/
CONHASH_API const struct node_s*
conhash_lookup(const struct conhash_s *conhash,
const char *object);

Libconhash非常易于使用。项目中有一个示例,说明如何使用该库。

首先,创建一个conhash实例。然后,您可以添加或删除实例的节点,并查找对象。

更新节点的副本功能尚未实现。

/* init conhash instance */
struct conhash_s *conhash = conhash_init(NULL);
if(conhash)
{
/* set nodes */
conhash_set_node(&g_nodes[0], "titanic", 32);
/* ... */

/* add nodes */
conhash_add_node(conhash, &g_nodes[0]);
/* ... */
printf("virtual nodes number %d\n", conhash_get_vnodes_num(conhash));
printf("the hashing results--------------------------------------:\n");

/* lookup object */
node = conhash_lookup(conhash, "James.km");
if(node) printf("[%16s] is in node: [%16s]\n", str, node->iden);
}

参考

执照