文件系统-负载均衡算法

1. 引言

分布式系统是由多台计算机组成的计算机系统,这些计算机通过网络互联,彼此之间进行通信和协作,共同完成分布式系统的各项任务。在分布式系统中,负载均衡是一个重要的问题。负载均衡的概念是指将任务或请求合理地分配到系统的各个节点上,使得每个节点的负载相对均衡,以实现系统的高性能、高可用和高扩展性。负载均衡的设计和实现对于提高分布式系统的整体性能至关重要。

1.1 负载均衡解决问题

如何合理地分配请求或任务到各个节点上,使得系统的整体负载相对均衡;
如何应对节点故障或新增节点时的负载调整;
如何利用负载均衡提高系统的性能和可靠性。

1.2 负载均衡的分类

根据负载均衡的实现方式和策略,可以将负载均衡算法分为以下几类:

  • 静态负载均衡:在系统启动时预先分配服务器的负载,并且在运行期间不再作任何调整。

  • 动态负载均衡:根据服务器的运行状态和负载情况,动态地调整请求的分配策略,以实现负载的均衡。

  • 内部负载均衡:在单个应用程序内部进行负载均衡,根据请求的特性和数据的分布等因素进行负载分配。

  • 外部负载均衡:通过独立的负载均衡设备(如负载均衡器)对请求进行分配,将请求转发到不同的服务器上。

2. 常用的负载均衡算法

2.1 轮询算法

轮询算法是最简单也是最常用的负载均衡算法之一。它的原理是循环地按顺序将请求发送给后端服务器。每个请求都会按照一定顺序依次分发给服务器,直到循环一次后重新开始。这样可以保证每台服务器在一段时间内都能够平均处理到相同数量的请求。

2.2 加权轮询算法

加权轮询算法是在轮询算法的基础上引入权重的一种负载均衡算法。每个后端服务器都会分配一个权重值,权重越高的服务器会接收到更多的请求。

func doSimpleWeightBound(servers []Server,initWeight []int){
	res:=list.New()
	weight:=make([]int,len(initWeight))
	count:=make([]int, len(weight))
	totalWeight:=0
	for i := 0; i< len(initWeight);i++  {
		totalWeight+=initWeight[i]
	}
	copy(weight,initWeight)
    nowWeight:=totalWeight
    //一共做totalWeight*100次,查看结果。
	for i := 0; i < totalWeight*100; i++ {
		//1. 选出当前权重最高的机器
		position:=selectHighWeight(weight)
		count[position]++
		res.PushBack(servers[position].name)
		//2. 选出的机器权重数减1
		weight[position]--
		//3. 总权重减1
		nowWeight--
		if(nowWeight<=0){
			copy(weight,initWeight)
			nowWeight=totalWeight
		}
	}
	for i :=0; i < len(weight); i ++ {
		fmt.Printf("%d ",weight[i])
	}
	fmt.Println()
	for i := res.Front(); i != nil; i = i.Next() {
		fmt.Printf("%s ",i.Value)
	}
	fmt.Println()
	for i :=0; i < len(count); i ++ {
		fmt.Printf("%s: %d\n",string('A'+i),count[i])
	}
}

2.3 随机算法

随机算法是将请求随机地分发给后端服务器的负载均衡算法。它通过生成随机数来决定请求的分发目标,以达到均衡负载的目的。

2.4 加权随机算法

加权随机法跟加权轮询法类似,根据后台服务器不同的配置和负载情况配置不同的权重。不同的是,它是按照权重来随机选取服务器的,而非顺序。加权随机算法一般应用的场景是在一个集合S{A,B,C,D}中随机抽取一项,但是抽取的概率不同,比如希望抽到A的概率是50%、抽到B和C的概率是20%、抽到D的概率是10%。一般来说,我们可以给各项附加一个权重,抽取的概率正比于这个权重,上述集合就成了{A:5,B:2,C:3,D:1}。扩展这个集合,使每一项出现的次数与其权重正相关,即{A,A,A,A,A,B,B,C,C,D},然后就可以用均匀随机算法从中选择取了。
具体可以参考本文:
https://lotabout.me/2018/Weighted-Random-Sampling/

std::string Crush::SelectItem(const std::vector<std::string> items)
{
    // https://lotabout.me/2018/Weighted-Random-Sampling/
    // normalization
    double sum = 0.0;
    for(uint32_t i = 0; i < items.size();i++) {
        sum = sum + map_items_[items[i]].weight;
    }

    double max_ki = 0.0;
    size_t selected = 0;
    for(size_t i = 0; i < items.size(); i++) {
        double wi = map_items_[items[i]].weight / sum;
        double ui = common::util::GenerateRandom();

        double ki = pow(ui, (1/wi));
        //std::cout<< "Crush::CalcSatisfiedRuleItem:" << wi << " , " << ui << " , " << ki << std::endl;
        if (ki > max_ki) {
            selected = i;
            max_ki = ki;
        }
    }
    return items[selected];
}

2.5 源地址哈希算法

哈希算法是基于请求的某一属性(如请求的IP地址、用户ID等)计算哈希值,并根据哈希值决定请求的分发目标的负载均衡算法。同一属性的请求将被分发到同一台服务器上,保证相同属性的请求能够在同一个服务器上进行处理。

2.6 一致性哈希算法

一致性哈希(Hash)法解决了分布式环境下机器增加或者减少时简单的取模运算无法获取较高命中率的问题。通过虚拟节点的使用,一致性哈希算法可以均匀分配机器的负载,使得这一算法更具现实意义。正因如此,一致性哈希算法被广泛应用于分布式系统中。

#include <iostream>
#include <string>
#include <map>
#include <unordered_map>
#include <set>
#include <vector>
#include <algorithm>

using namespace std;

class ConsistentHash {
private:
    using HashType = uint32_t;
    int virtual_nodes_count; // 虚拟节点数量

    // 哈希环(有序)
    multiset<HashType> ring;
    // 哈希值到物理节点的映射
    unordered_map<HashType, string> hash_to_node;
    // 物理节点到其虚拟节点哈希值的映射(用于快速删除)
    unordered_map<string, vector<HashType>> node_vnodes;

    // 生成32位哈希值(FNV-1a算法)
    static HashType hash_key(const string& key) {
        HashType hash = 2166136261u;
        for (char c : key) {
            hash ^= c;
            hash *= 16777619; // FNV-1a的质数
        }
        return hash;
    }

public:
    // 构造函数,默认虚拟节点数量为100
    ConsistentHash(int vnodes = 100) : virtual_nodes_count(vnodes) {}

    // 添加物理节点
    void addNode(const string& node) {
        vector<HashType> vnodes; // 当前节点的虚拟节点哈希列表
        for (int i = 0; i < virtual_nodes_count; ++i) {
            string vnode_id = node + "-" + to_string(i);
            HashType h = hash_key(vnode_id);
            ring.insert(h);
            hash_to_node[h] = node;
            vnodes.push_back(h);
        }
        node_vnodes[node] = vnodes;
    }

    // 移除物理节点
    void removeNode(const string& node) {
        auto vnodes_it = node_vnodes.find(node);
        if (vnodes_it == node_vnodes.end()) return;

        // 遍历所有虚拟节点哈希并删除
        const vector<HashType>& vnodes = vnodes_it->second;
        for (HashType h : vnodes) {
            auto it = ring.find(h);
            if (it != ring.end()) ring.erase(it);
            hash_to_node.erase(h);
        }
        node_vnodes.erase(vnodes_it);
    }

    // 根据键找到对应节点
    string get_node(const string& key) {
        if (ring.empty()) return ""; // 无节点时返回空

        HashType key_hash = hash_key(key);
        auto it = ring.lower_bound(key_hash);
        if (it == ring.end()) {
            // 环绕到第一个元素
            it = ring.begin();
        }
        return hash_to_node[*it];
    }

    // 打印哈希环信息(调试用)
    void printRing() const {
        cout << "Hash Ring:\n";
        for (HashType h : ring) {
            cout << "Hash: " << h << " -> Node: " << hash_to_node[h] << endl;
        }
    }
};

int main() {
    ConsistentHash ch(100); // 使用100个虚拟节点

    // 添加节点
    ch.addNode("NodeA");
    ch.addNode("NodeB");
    cout << "After adding NodeA and NodeB:\n";
    ch.printRing();

    // 测试数据分布
    cout << "\nTesting data distribution:\n";
    cout << "data1 -> " << ch.get_node("data1") << endl;
    cout << "data2 -> " << ch.get_node("data2") << endl;
    cout << "data3 -> " << ch.get_node("data3") << endl;

    // 移除节点
    ch.removeNode("NodeB");
    cout << "\nAfter removing NodeB:\n";
    ch.printRing();

    // 重新测试数据分布
    cout << "\nTesting data distribution after removal:\n";
    cout << "data1 -> " << ch.get_node("data1") << endl;
    cout << "data2 -> " << ch.get_node("data2") << endl;
    cout << "data3 -> " << ch.get_node("data3") << endl;

    return 0;
}

2.7 最少连接算法

最少连接算法是一种动态调整的负载均衡算法。其思路是尽可能将请求分配到当前空闲连接数最少的后端服务器,以达到负载均衡的效果。在实现过程中,通常需要定期检测每个服务器的连接数并进行动态调整。

参考
1、
https://download.youkuaiyun.com/blog/column/11291510/119836860

2、
https://lotabout.me/2018/Weighted-Random-Sampling/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值