├── CMakeLists.txt ├── README.md ├── consistent_hash_map.hpp ├── example.cpp └── test.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 2 | PROJECT(consistant_hash) 3 | IF(APPLE) 4 | SET(CMAKE_CXX_COMPILER clang++) 5 | ENDIF(APPLE) 6 | set(CMAKE_BUILD_TYPE DEBUG) 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g") 8 | 9 | INCLUDE_DIRECTORIES(/usr/local/include) 10 | LINK_DIRECTORIES(/usr/local/lib) 11 | 12 | ADD_EXECUTABLE(test test.cpp) 13 | ADD_EXECUTABLE(example example.cpp) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 一致性哈希的C++实现 2 | 3 | 一致性哈希是分布式计算领域被广泛应用的一个算法。在许多分布式系统包括 Amazon Dynamo, memcached, Riak 等中都有使用。 4 | 一致性哈希的原理比较简单,网上有很多介绍的比较好的文章,也有一些相关的代码,但是这些代码都不太令人满意,因此自己实现了一个。代码很简单,放在了 [github](https://github.com/ioriiod0/consistent_hash) 上面。 5 | 6 | 7 | ##consistent_hash_map 8 | 9 | 一致性哈希的功能被封装在模板类consistent_hash_map中: 10 | 11 | template > > 14 | class consistent_hash_map 15 | 16 | 17 | consistent_hash_map 使用了stl map风格的接口。实际上其内部也使用了std::map 来管理和维护所有节点。 18 | 19 | consistent_hash_map只提供最基本的一致性hash的功能,并不直接支持虚拟节点的概念,但是虚拟节点的概念可以很容易的通过定制的T 和 Hash类型来实现。这样设计的好处在于可以使consitent_hash_map的设计和实现变得非常的简单,同时留给用户以极大的灵活性和可定制性。 20 | 后面的例子中将介绍如何实现虚拟节点。 21 | 22 | 23 | ##模板参数 24 | 25 | 1. T: consistent hash的节点类型。 26 | 2. Hash: 一元函数对象。接收T类型对象作为参数,返回一个整形作为其hash值,该hash值将被用于内部的排序。Hash需在其内部定义result_type 指明返回整形的类型。 27 | 3. Alloc: 内存分配器,默认为std::allocator 28 | 29 | ##member type 30 | size_type Hash::reslut_type hash函数返回值的类型 31 | value_type std::pair first为节点的哈希值,second为节点 32 | iterator a bidirectional iterator to value_type 双向迭代器 33 | reverse_iterator reverse_iterator 反向迭代器 34 | 35 | ##member function 36 | std::size_t size() const; 37 | 返回consistent_hash_map内的节点数量。 38 | 39 | bool empty() const; 40 | 判断consistent_hash_map是否为空 41 | 42 | std::pair insert(const T& node); 43 | 插入一个节点,如果返回值中bool变量为真,iterator则为指向插入节点的迭代器。如果bool为假,表示插入失败,iterator指向已经存在的节点。插入失败因为节点已经存在或者是节点的hash值与其他节点发生冲突。 44 | 45 | void erase(iterator it); 46 | 通过迭代器删除指定节点。 47 | 48 | std::size_t erase(const T& node); 49 | 通过节点值删除指定节点。 50 | 51 | iterator find(size_type hash); 52 | 通过传入的hash值找对其在consistent_hash中对应的节点的迭代器。 53 | 54 | iterator begin(); 55 | iterator end(); 56 | 返回对应迭代器 57 | 58 | reverse_iterator rbegin(); 59 | reverse_iterator rend(); 60 | 返回对应的反向迭代器 61 | 62 | ##虚拟节点的例子 63 | 64 | 整个例子的完整代码[在这](https://github.com/ioriiod0/consistent_hash/blob/master/example.cpp)。 65 | 在这个例子中,我们首先定义虚拟节点类型,和其对应的hasher。 66 | 67 | #include 68 | #include 69 | #include 70 | 71 | #include "consistent_hash_map.hpp" 72 | 73 | //所有主机的列表 74 | const char* nodes[] = { 75 | "192.168.1.100", 76 | "192.168.1.101", 77 | "192.168.1.102", 78 | "192.168.1.103", 79 | "192.168.1.104" 80 | }; 81 | 82 | //虚拟节点 83 | struct vnode_t { 84 | vnode_t() {} 85 | vnode_t(std::size_t n,std::size_t v):node_id(n),vnode_id(v) {} 86 | 87 | std::string to_str() const { 88 | return boost::str(boost::format("%1%-%2%") % nodes[node_id] % vnode_id); 89 | } 90 | 91 | std::size_t node_id; //主机ID,主机在主机列表中的索引 92 | std::size_t vnode_id; //虚拟节点ID 93 | 94 | }; 95 | 96 | //hasher,使用CRC32作为hash算法,注意需要定义result_type 97 | struct crc32_hasher { 98 | uint32_t operator()(const vnode_t& node) { 99 | boost::crc_32_type ret; 100 | std::string vnode = node.to_str(); 101 | ret.process_bytes(vnode.c_str(),vnode.size()); 102 | return ret.checksum(); 103 | } 104 | typedef uint32_t result_type; 105 | }; 106 | 107 | 108 | 为每个主机生成100个虚拟节点,然后加入consistent_hash_map中。 109 | 110 | typedef consistent_hash_map consistent_hash_t; 111 | consistent_hash_t consistent_hash_; 112 | 113 | for(std::size_t i=0;i<5;++i) { 114 | for(std::size_t j=0;j<100;j++) { 115 | consistent_hash_.insert(vnode_t(i,j)); 116 | } 117 | } 118 | 119 | 120 | 查找某个hash值对应的vnode 和 主机: 121 | 122 | consistent_hash_t::iterator it; 123 | it = consistent_hash_.find(290235110); 124 | //it -> first是该节点的hash值,it -> second是该虚拟节点。 125 | std::cout<second.node_id] % it->second.vnode_id % it->first << std::endl; 127 | 128 | 129 | 130 | 遍历consistent_hash中的所有的vnode,统计每个虚拟节点的key的数量和每个主机包含key的数量: 131 | 132 | std::size_t sums[] = {0,0,0,0,0}; 133 | consistent_hash_t::iterator i = consistent_hash_.begin(); //第一个节点 134 | consistent_hash_t::reverse_iterator j = consistent_hash_.rbegin(); //最后一个节点 135 | std::size_t n = i->first + UINT32_MAX - j->first; //计算第一个节点包含的key的数量 136 | std::cout<second.to_str() % i->first % n << std::endl; 138 | sums[i->second.node_id] += n; //更新主机包含的key数量。 139 | 140 | //计算剩余所有节点包含的key的数量,并更新主机包括的key的数量。 141 | uint32_t priv = i->first; 142 | uint32_t cur; 143 | consistent_hash_t::iterator end = consistent_hash_.end(); 144 | while(++i != end) { 145 | cur = i->first; 146 | n = cur - priv; 147 | std::cout<second.to_str() % cur % n << std::endl; 149 | sums[i->second.node_id] += n; 150 | priv = cur; 151 | } 152 | 153 | for(std::size_t i=0;i<5;++i) { 154 | std::cout< 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | 9 | #ifndef __CONSISTENT_HASH_H__ 10 | #define __CONSISTENT_HASH_H__ 11 | 12 | 13 | 14 | template > > 17 | class consistent_hash_map 18 | { 19 | public: 20 | 21 | typedef typename Hash::result_type size_type; 22 | typedef std::map,Alloc> map_type; 23 | typedef typename map_type::value_type value_type; 24 | typedef value_type& reference; 25 | typedef const value_type& const_reference; 26 | typedef typename map_type::iterator iterator; 27 | typedef typename map_type::reverse_iterator reverse_iterator; 28 | typedef Alloc allocator_type; 29 | 30 | public: 31 | 32 | consistent_hash_map() { 33 | 34 | } 35 | 36 | ~consistent_hash_map() { 37 | 38 | } 39 | 40 | public: 41 | std::size_t size() const { 42 | return nodes_.size(); 43 | } 44 | 45 | bool empty() const { 46 | return nodes_.empty(); 47 | } 48 | 49 | std::pair insert(const T& node) { 50 | size_type hash = hasher_(node); 51 | return nodes_.insert(value_type(hash,node)); 52 | } 53 | 54 | 55 | void erase(iterator it) { 56 | nodes_.erase(it); 57 | } 58 | 59 | 60 | std::size_t erase(const T& node) { 61 | size_type hash = hasher_(node); 62 | return nodes_.erase(hash); 63 | } 64 | 65 | iterator find(size_type hash) { 66 | if(nodes_.empty()) { 67 | return nodes_.end(); 68 | } 69 | 70 | iterator it = nodes_.lower_bound(hash); 71 | 72 | if (it == nodes_.end()) { 73 | it = nodes_.begin(); 74 | } 75 | 76 | return it; 77 | } 78 | 79 | iterator begin() { return nodes_.begin(); } 80 | iterator end() { return nodes_.end(); } 81 | reverse_iterator rbegin() { return nodes_.rbegin(); } 82 | reverse_iterator rend() { return nodes_.rend(); } 83 | 84 | 85 | private: 86 | 87 | Hash hasher_; 88 | map_type nodes_; 89 | }; 90 | 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /example.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "consistent_hash_map.hpp" 10 | 11 | 12 | 13 | const char* nodes[] = { 14 | "192.168.1.100", 15 | "192.168.1.101", 16 | "192.168.1.102", 17 | "192.168.1.103", 18 | "192.168.1.104" 19 | }; 20 | 21 | struct vnode_t { 22 | vnode_t() {} 23 | vnode_t(std::size_t n,std::size_t v):node_id(n),vnode_id(v) {} 24 | 25 | std::string to_str() const { 26 | return boost::str(boost::format("%1%-%2%") % nodes[node_id] % vnode_id); 27 | } 28 | 29 | std::size_t node_id; 30 | std::size_t vnode_id; 31 | 32 | }; 33 | 34 | 35 | struct crc32_hasher { 36 | uint32_t operator()(const vnode_t& node) { 37 | boost::crc_32_type ret; 38 | std::string vnode = node.to_str(); 39 | std::cout<<"vnode:"< consistent_hash_t; 50 | consistent_hash_t consistent_hash_; 51 | 52 | for(std::size_t i=0;i<5;++i) { 53 | for(std::size_t j=0;j<100;j++) { 54 | consistent_hash_.insert(vnode_t(i,j)); 55 | } 56 | } 57 | 58 | 59 | { 60 | std::cout<<"========================================================="<first + UINT32_MAX - j->first; 65 | std::cout<second.to_str() % i->first % n << std::endl; 67 | sums[i->second.node_id] += n; 68 | 69 | uint32_t priv = i->first; 70 | uint32_t cur; 71 | consistent_hash_t::iterator end = consistent_hash_.end(); 72 | while(++i != end) { 73 | cur = i->first; 74 | n = cur - priv; 75 | std::cout<second.to_str() % cur % n << std::endl; 77 | sums[i->second.node_id] += n; 78 | priv = cur; 79 | } 80 | 81 | for(std::size_t i=0;i<5;++i) { 82 | std::cout<second.node_id] % it->second.vnode_id % it->first << std::endl; 93 | } 94 | 95 | 96 | return 0; 97 | } -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "consistent_hash_map.hpp" 11 | 12 | struct crc32_hasher { 13 | uint32_t operator()(const std::string& node) { 14 | boost::crc_32_type ret; 15 | ret.process_bytes(node.c_str(),node.size()); 16 | return ret.checksum(); 17 | } 18 | typedef uint32_t result_type; 19 | }; 20 | 21 | 22 | int main(int argc, char const *argv[]) 23 | { 24 | typedef consistent_hash_map consistent_hash_t; 25 | consistent_hash_t consistent_hash_; 26 | boost::format node_fmt("192.168.1.%1%"); 27 | 28 | for(std::size_t i=0;i<3;++i) { 29 | std::string node = boost::str(node_fmt % i); 30 | consistent_hash_.insert(node); 31 | std::cout<second % it->first << std::endl; 39 | } 40 | } 41 | 42 | 43 | { 44 | consistent_hash_t::iterator it; 45 | it = consistent_hash_.find(290235110); 46 | std::cout<second % it->first << std::endl; 47 | } 48 | 49 | { 50 | consistent_hash_t::iterator it; 51 | it = consistent_hash_.find(2286285664); 52 | std::cout<second % it->first << std::endl; 53 | } 54 | 55 | { 56 | consistent_hash_t::iterator it; 57 | it = consistent_hash_.find(4282565578); 58 | std::cout<second % it->first << std::endl; 59 | } 60 | 61 | 62 | std::cout<<"========================================================="<second % it->first << std::endl; 68 | } 69 | } 70 | 71 | std::cout<<"========================================================="<second % it->first << std::endl; 76 | std::cout<<"-------------------------------------------"<second % it->first << std::endl; 80 | } 81 | } 82 | 83 | std::cout<<"========================================================="<second % it->first << std::endl; 89 | std::cout<<"-------------------------------------------"<second % it->first << std::endl; 93 | std::cout<<"-------------------------------------------"<second % it->first << std::endl; 99 | } 100 | std::cout<<"-------------------------------------------"<