一致性哈希算法

![在这里插入图片描述](https://img-blog.csdnimg.cn/1986bca946aa4cf8b1c60edf3e36c00d.png)

2. 负载场景

在这里插入图片描述
经过哈希算法hash(ip:port)%N,同一客户的请求都会被映射到相同的服务器上,这有效解决了会话共享的问题,这时候就不需要加入redis缓存服务器去记录客户的登录状态,避免引用外部模块过多导致系统不稳定的问题。

但这种方法也有问题,比如当某个服务器宕机了(或增加服务器),这时候N的值就发生改变,导致相同客户的请求就不一定还会转发到以前那台服务器上,这会发生身份认证的问题。

我们想要的是同一 ip:port的请求,永远被映射到同一台server上处理

2. 缓存场景

客户通常发请求查询信息的时候,是现在缓存服务器上查找,查找不到再到DB查找,然后把数据从DB转移到缓存服务器。
在这里插入图片描述

  1. 若此时有一台服务器宕机了,同理在0号缓存服务器中的信息再次被查询时,这个请求很有可能会被转发到1号服务器,此时无法在1号缓存服务器中找到信息,就会去DB中查找,此时大量的磁盘IO会严重影响server的响应速度。更严重来讲,一台服务器的负载太高,可能会影响整个系统的运转,甚至崩溃。
  2. 若此时增加了一台服务器,N发生改变。举个极端的例子,此时所有的请求都被转发到新的服务器中,新的服务器无法查询到相应信息,同理会产生大量的磁盘IO,甚至服务器崩溃。

我们的理想情况是:

某个server挂了,不影响其他server的正常运转,不会请求急剧增加
server增加了,不影响原来server的请求,只会把后续的请求映射到新的server上

解决方法:一致性哈希算法
在这里插入图片描述

3. 一致性哈希算法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<set>
#include<list>
#include<map>
#include<string>
#include"md5.h"

using namespace std;
//一致性哈希环的取值类型
using uint = unsigned int;
//前置声明物理节点主机类型
class PhysicalHost;
//虚拟节点
class VirtualHost {
public:
	VirtualHost(string ip, PhysicalHost* phy_host_ptr) 
	: ip_(ip)
	, phy_host_ptr_(phy_host_ptr)
	{
		md5_ = getMD5(ip_.c_str());
	}

	// 虚拟节点存放到set的时候,需要排序,默认less,需要提供operator<
	bool operator<(const VirtualHost& vir_host) const {
		// 根据md5值排序
		return md5_ < vir_host.md5_;
	}

	// 删除哈希环上的虚拟节点时,需要查找,重载operator==
	bool operator==(const VirtualHost& vir_host) const {
		return ip_ == vir_host.ip_;
	}

	const uint get_md5() const {
		return md5_;
	}

	const PhysicalHost* get_phy_host() const {
		return phy_host_ptr_;
	}
private:
	string ip_;                   // 虚拟节点记录的ip信息
	uint md5_;                    // 根据物理节点的ip计算的ip值得到的MD5,这是32位加密串运算得到的uint
	PhysicalHost* phy_host_ptr_;  // 指向实际的物理节点
};

//物理节点
class PhysicalHost {
public:
	// 物理节点的ip,创建虚拟节点的个数
	PhysicalHost(string ip, int v_number)
	: ip_(ip)
	{
		for (int i = 0; i < v_number; i++) {
			// 虚拟节点需要记录ip以及对应的物理节点
			virtual_hosts_.push_back(VirtualHost(ip_ + "#" + ::to_string(i), this));
		}
	}

	const string get_ip() const{
		return ip_;
	}

	const list<VirtualHost>& get_virtual_hosts() const {
		return virtual_hosts_;
	}

private:
	string ip_;//物理机器的ip地址
	list<VirtualHost> virtual_hosts_;  // 双向循环链表,存储虚拟节点的链表
};

//一致性哈希	
class ConsistentHash {
public:
	// 添加物理主机的虚拟节点到一致性哈希环
	void add_host(PhysicalHost& phy_host) {
		auto vir_list = phy_host.get_virtual_hosts();
		for (auto vir_host : vir_list) {
			hash_circle_.insert(vir_host);
		}
	}

	// 删除哈希环物理节点所有的虚拟节点
	void del_host(PhysicalHost& phy_host) {
		//获取物理主机所有的虚拟节点列表
		auto vir_list = phy_host.get_virtual_hosts();
		for (auto vir_host : vir_list) {
			// 红黑树查找,O(log2n)
			auto iter = hash_circle_.find(vir_host);
			if (iter != hash_circle_.end()) {
				hash_circle_.erase(iter);
			}
		}
	}

	// 根据客户的ip,计算其对应的虚拟主机,然后根据虚拟主机返回真是的物理主机的ip
	string get_client_host(string client_ip) const{
		uint client_md5 = getMD5(client_ip.c_str());
		// 找第一个比客户ip的md5大的虚拟主机
		for (VirtualHost vir_host : hash_circle_) {
			if (vir_host.get_md5() > client_md5) {
				return vir_host.get_phy_host()->get_ip();
			}
		}
		// 客户的ip得到的md5过大,无法找到更大的md5,那直接分配第一个虚拟节点
		return hash_circle_.begin()->get_phy_host()->get_ip();
	}

private:
    // 由于需要顺时针查找,需要排序,所以一致性哈希算法底层用到红黑树
	set<VirtualHost> hash_circle_;
};

void show_consistent_hash(const ConsistentHash& hash_circle) {
	list<string> client_ip_list{
		"192.168.1.100",
		"192.168.1.101",
		"192.168.1.102",
		"192.168.1.103",
		"192.168.1.104",
		"192.168.1.105",
		"192.168.1.106",
		"192.168.1.107",
		"192.168.1.108",
		"192.168.1.109",
		"192.168.1.110",
		"192.168.1.111",
		"192.168.1.112",
		"192.168.1.113",
	};

	// 物理服务器ip  所服务的客户端ip
	map<string, list<string>> ip_map;
	for (string client_ip : client_ip_list) {
		// 根据客户端的ip,计算哈希环上的虚拟主机,从而拿到对应物理主机的ip
		string phy_host_ip = hash_circle.get_client_host(client_ip);  
		ip_map[phy_host_ip].push_back(client_ip);
	}
	for (auto pair : ip_map) {
		cout << "server ip :" << pair.first << endl;
		cout << "该服务器服务的客户端有" << pair.second.size() << "个" << endl;
		cout << "client ip :" << endl;
		for (string client_ip : pair.second) {
			cout << client_ip << endl;
		}
		cout << "-----------------------------" << endl;
	}
}

int main() {
	PhysicalHost phy_host1("10.117.121.66", 200);
	PhysicalHost phy_host2("10.117.121.67", 200);
	PhysicalHost phy_host3("10.117.121.68", 200);
	
	ConsistentHash hash_circle;
	hash_circle.add_host(phy_host1);
	hash_circle.add_host(phy_host2);
	hash_circle.add_host(phy_host3);
	
	show_consistent_hash(hash_circle);
	hash_circle.del_host(phy_host2);
	cout << "*********主机2宕机*********" << endl;
	show_consistent_hash(hash_circle);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值