一致性hash的c++簡單實現

lipeng08發表於2017-01-10

很晚了,一致性hash的理論不再多說,直接上我寫的c++程式碼

程式碼功能:
1,初始化一些實際的server節點,每個server節點生成一堆虛擬節點
2,將所有的虛擬節點根據它的名字生成的hash key散佈到一個環內
3,對新插入環內的元素(隨機生成),計算它的hash key,然後在環中尋找大於等於它的虛擬節點,根據虛擬節點即可找到物理節點。隨機生成很多歌元素,檢視散佈到各個節點的元素數量是否均衡。

程式碼一些特點:
對於server節點,只需要儲存它的虛擬節點hash值以及與實際server名字的對應。對於新插入的元素,為了快速找到它所存放的位置(即大於等於它的hash值的虛擬節點),應該採用二分查詢。 故而,虛擬節點使用map結構儲存,底層是紅黑樹的實現,類似於二分,同時支援高效的插入刪除。 當新的虛擬節點插入到環內,只需要移動很少量的元素到它上面。

還有chord演算法要好好看看,還沒有看。

#include<iostream>
#include <sstream>
#include <string>
#include <vector>
#include <list>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <iomanip>
#include <random>

#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cstdio>

using namespace std;
typedef unsigned long Key;

template <class T>
struct NodeHasher
{
	template <class T>
	Key operator() (const T& t){
		return 0;
	}

	template <>
	Key operator()<string> (const string& t){
		return hash2((unsigned char*)t.c_str());
	}
public:
	//not a good hash function
	static unsigned long hash(unsigned char *str)
	{
		unsigned long hash = 5381;
		int c;

		while (c = *str++)
			hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

		return hash;
	}
	static unsigned long hash2(unsigned char *str)
	{
		int p = 16777619;
		int hash = (int)2166136261L;
		int c;
		while (c = *str++)
			hash = (hash ^ c) * p;

		hash += hash << 13;
		hash ^= hash >> 7;
		hash += hash << 3;
		hash ^= hash >> 17;
		hash += hash << 5;

		// 如果算出來的值為負數則取其絕對值 
		if (hash < 0)
			hash = abs(hash);
		return hash;
	}
};

template <class T>
class ConsistentHash
{
public:
	ConsistentHash(int numberOfReplica = 150, bool isTest = false, int server=5): numOfReplicas(numberOfReplica){
		if (isTest)
		{
			for (int c = 0; c < server; ++c)
			{
				addServer(to_string(c));
			}
		}
	}
public:
	void addServer(T node){
		cout << "add real server:" << node << endl;
		for (int i = 0; i < numOfReplicas; ++i)
		{
			Key k = NodeHasher<T>()(node + std::to_string(i));
			if (i < 2)
				cout << "	virtual server: " << k << endl;
			else if (i == 2)
				cout << "	... " <<numOfReplicas<< endl;
			circles.insert({ k, node });
		}
	}
	void removeServer(T node){
		cout << "remove real server:" << node << endl;
		for (int i = 0; i < numOfReplicas; ++i)
		{
			Key k = NodeHasher<T>()(node+std::to_string(i));
			auto it = circles.find(k);
			if (it != circles.end()) circles.erase(it);
		}
	}
public:
	T get(Key k){
		auto it = circles.find(k);
		if (it != circles.end()) return it->second;
		it = circles.lower_bound(k);
		return (it == circles.end()) ? circles.begin()->second : it->second;
	}
public:
	static void testIt(){
		int number = 100000;
		int server = 10;
		int virutal_server = 150;
		ConsistentHash chash(virutal_server, true, server);

		struct {
			void operator()(int number, ConsistentHash& chash) {
				map<T, int> countNumMap;

				std::random_device seed;
				std::mt19937 gen(seed());
				std::uniform_int_distribution<int> dist(0, LONG_MAX);
				for (int i = 0; i < number; ++i)
				{
					Key k = dist(gen);
					T node = chash.get(k);
					countNumMap[node]++;
				}
				cout << "server numOfElems:" << endl;
				for (auto it : countNumMap){
					cout << it.first << " " << it.second << " " << std::setprecision(2) << it.second*1.0 / number << endl;
				}
			}
		}show;

		for (int i = 0; i < server; ++i)
		{
			show(number, chash);
			chash.removeServer(std::to_string(i));
		}
	}
private:
	int numOfReplicas;
	map<Key, T> circles;
};
int main()
{
	ConsistentHash<string>::testIt();
	return 0;
}

相關文章