经典负载均衡算法实现例子汇总(附有代码)

前言

本文介绍几个经典负载均衡算法的实现示例,包括轮询,加权轮询,哈希,顺序,最小连接时间,最少连接,基于资源的负载均衡算法。 以较深入的方式理解和掌握这些应用较多的算法。

实现SLB轮询的示例

使用一个轮询(Round Robin)负载均衡算法在服务器列表之间分配请求:

package main

import (
    "fmt"
)

// LoadBalancer 结构体定义
type LoadBalancer struct {
    servers     []string
    currentIndex int
}

// NewLoadBalancer 构造函数,创建 LoadBalancer 实例
func NewLoadBalancer(servers []string) *LoadBalancer {
    return &LoadBalancer{
        servers:     servers,
        currentIndex: 0,
    }
}

// GetNextServer 获取下一个服务器,使用轮询算法
func (lb *LoadBalancer) GetNextServer() string {
    // 获取当前服务器
    nextServer := lb.servers[lb.currentIndex]
    // 更新 currentIndex,确保轮询到下一个服务器
    lb.currentIndex = (lb.currentIndex + 1) % len(lb.servers)
    return nextServer
}

func main() {
    // 服务器列表
    serverList := []string{"Server1", "Server2", "Server3"}

    // 创建负载均衡器实例
    loadBalancer := NewLoadBalancer(serverList)

    // 模拟请求
    for i := 0; i < 10; i++ {
        nextServer := loadBalancer.GetNextServer()
        fmt.Printf("Request %d: Routed to %s\n", i+1, nextServer)
    }
}

解析: LoadBalancer 结构体包含 servers 字符串切片(类似于 Java 中的 List)和 currentIndex 整数。 NewLoadBalancer 函数作为构造函数,初始化服务器列表和索引。GetNextServer 方法实现轮询算法,返回当前索引的服务器,并更新索引使其指向下一个服务器。 main 函数创建服务器列表并实例化负载均衡器,然后模拟了10次请求,每次请求调用 GetNextServer 方法来获取下一个服务器。

运行结果

Request 1: Routed to Server1
Request 2: Routed to Server2
Request 3: Routed to Server3
Request 4: Routed to Server1
Request 5: Routed to Server2
Request 6: Routed to Server3
Request 7: Routed to Server1
Request 8: Routed to Server2
Request 9: Routed to Server3
Request 10: Routed to Server1

这样每次请求将会轮询地分配到不同的服务器上,直到轮询回到第一个服务器。

何时使用轮询负载均衡算法

非常适合所有服务器都具有相似容量和性能的应用程序。 适用于均匀分布的工作负载,例如基本 Web 请求。 最适合没有复杂资源需求的简单环境。在请求顺序不如服务器之间的均衡分配重要的设置中很有用。

轮询负载均衡算法的优缺点
好处:

  • 简单性:易于实施和理解。
  • Fairness:确保每个服务器获得相等的负载份额。

缺点:

  • 不等容量:不考虑服务器的不同容量;平等对待所有服务器。
  • 可预测性:对于服务器容量异构的方案,可能不是最佳方案。

加权轮询的实例

加权轮询负载均衡算法 加权轮询算法也是一种静态负载均衡方法,它与轮询技术非常相似。唯一的区别是,列表中的每个资源都提供了一个加权分数。根据加权分数,请求将分发到这些服务器。

权重较高的服务器将获得更大比例的请求。 分布是循环的,类似于循环技术,但每个服务器接收的请求数与其权重成正比。 如果服务器达到其处理能力,它可能会开始拒绝或排队其他请求,具体取决于服务器的特定行为。 例如:
假设您的朋友对糖果的渴望程度不同。你想公平,所以你把更多的糖果送给最爱他们的朋友。Weighted Round Robin 做类似的事情 – 它给可以更好地处理任务的朋友更多任务。

假设您有三台具有权重的服务器:Server1(权重 0.3)、Server2(权重 0.2)和 Server3(权重 0.1)。总权重为 0.3 + 0.2 + 0.1 = 0.6。在每个周期中,Server1 将收到 0.3/0.650%) 的请求,Server2 将收到 0.2/0.633.33%),Server3 将收到 0.1/0.616.67%)。

Go实现的带权重的轮询负载均衡器,功能与Java代码相同。此实现中,我们使用结构体来定义服务器(带有名称和权重)以及加权轮询负载均衡器本

package main

import (
    "fmt"
    "math/rand"
    "time"
)

// Server 结构体,包含服务器名称和权重
type Server struct {
    name   string
    weight int
}

// WeightedRoundRobinBalancer 结构体
type WeightedRoundRobinBalancer struct {
    servers           []Server
    cumulativeWeights []int
    totalWeight       int
    random            *rand.Rand
}

// NewWeightedRoundRobinBalancer 构造函数,初始化负载均衡器
func NewWeightedRoundRobinBalancer(servers []Server) *WeightedRoundRobinBalancer {
    balancer := &WeightedRoundRobinBalancer{
        servers: servers,
        random:  rand.New(rand.NewSource(time.Now().UnixNano())),
    }
    balancer.totalWeight = balancer.calculateTotalWeight()
    fmt.Printf("balancer.totalWeight:%v\n", balancer.totalWeight)
    balancer.cumulativeWeights = balancer.calculateCumulativeWeights()
    fmt.Printf("balancer.cumulativeWeights:%v\n", balancer.cumulativeWeights)
    return balancer
}

// 计算服务器的总权重
func (w *WeightedRoundRobinBalancer) calculateTotalWeight() int {
    totalWeight := 0
    for _, server := range w.servers {
        totalWeight += server.weight
    }
    return totalWeight
}

// 计算累计权重数组
func (w *WeightedRoundRobinBalancer) calculateCumulativeWeights() []int {
    cumulativeWeights := make([]int, len(w.servers))
    cumulativeWeights[0] = w.servers[0].weight
    for i := 1; i < len(w.servers); i++ {
        cumulativeWeights[i] = cumulativeWeights[i-1] +
                w.servers[i].weight
    }
    return cumulativeWeights
}

// 获取下一个服务器,使用随机值来确定
func (w *WeightedRoundRobinBalancer) GetNextServer() Server {
    randomValue := w.random.Intn(w.totalWeight)
    for i, cumulativeWeight := range w.cumulativeWeights {
        if randomValue < cumulativeWeight {
            return w.servers[i]
        }
    }
    return w.servers[len(w.servers)-1]
}

func main() {
    // 定义带权重的服务器列表
    serverList := []Server{
        {"Server1", 3},
        {"Server2", 2},
        {"Server3", 1},
    }

    // 创建加权轮询负载均衡器
    balancer := NewWeightedRoundRobinBalancer(serverList)

    // 模拟请求
    for i := 0; i < 10; i++ {
        nextServer := balancer.GetNextServer()
        fmt.Printf("Request %d: Routed to %s\n", i+1, nextServer.name)
    }
}

运行结果为:

balancer.totalWeight:6
balancer.cumulativeWeights:[3 5 6]
Request 1: Routed to Server3
Request 2: Routed to Server1
Request 3: Routed to Server1

每次运行输出可能不同,因为在带权重的选择中使用了随机数,但服务器将根据其权重的比例更频繁地被选中。

代码解析:

  • Server 结构体定义服务器信息,包括名称和权重。
  • WeightedRoundRobinBalancer 结构体定义带权重的负载均衡器,它包括服务器列表、累计权重数组、总权重和随机生成器。
  • NewWeightedRoundRobinBalancer 是构造函数,用于计算总权重和累计权重数组。
  • calculateTotalWeight 计算服务器列表的总权重。
  • calculateCumulativeWeights 生成累计权重数组,这样便于随机选择权重范围内的服务器。
  • GetNextServer 方法使用一个随机值在累计权重数组中定位到下一个服务器。
  • main 函数创建了服务器列表和负载均衡器,并模拟请求,显示请求被路由到的服务器。

何时使用加权轮询负载均衡算法?

当服务器具有不同的容量或性能级别时。 非常适合服务器资源(CPU、内存等)不同的环境。 当您希望最大限度地提高所有服务器的资源利用率时,这非常有用。 有助于防止小型服务器过载,同时有效使用大型服务器。

加权轮询负载均衡算法的优缺点
好处:

  • 容量注意事项:通过分配权重来考虑不同的服务器容量。
  • 灵活性:可以调整以有效处理不同的工作负载。

缺点:

  • 复杂性:比简单的循环更复杂。
  • 维护:需要根据服务器容量的变化调整权重。

负载均衡中的哈希算法

基于哈希的算法,其中负载平衡设备为客户端和服务器的源和目标 IP 地址分配唯一的哈希密钥。

这可确保如果同一用户返回并发出另一个请求,则该用户请求将被定向到他们之前使用的同一服务器。此外,服务器会保留之前会话期间输入的任何数据。

源 IP 哈希负载均衡算法是网络负载均衡中使用的一种方法,用于根据源 IP 地址的哈希值在一组服务器之间分配传入请求。 此算法旨在确保源自同一源 IP 地址的请求始终定向到同一服务器。

如果负载均衡器配置为会话持久性,则可确保来自同一源 IP地址的后续请求始终定向到同一服务器。 这对于需要维护会话信息或状态的应用程序非常有用。

想想有顾客来商超选购商品,你希望记住他们每次来访时谁会选择到哪个商品。 IP 哈希就像记住上次哪个顾客选择了哪个商品,所以你总是给他们推荐类似的商品。

源ip的hash负载均衡算法

实现一种负载均衡算法,该算法根据源 IP 地址的哈希值在一组服务器之间分配传入请求。目标是确保来自同一源 IP 地址的请求始终路由到同一服务器。

以下是源 IP 哈希负载均衡算法的实现:

package main

import (
    "fmt"
    "hash/fnv"
)

// SourceIpHashLoadBalancer represents the load balancer structure.
type SourceIpHashLoadBalancer struct {
    ipToServerMap map[string]string
}

// NewSourceIpHashLoadBalancer SourceIpHashLoadBalancer的构造函数
func NewSourceIpHashLoadBalancer() *SourceIpHashLoadBalancer {
    return &SourceIpHashLoadBalancer{
        ipToServerMap: make(map[string]string),
    }
}

// AddServer 用于将服务器添加到 ipToServerMap 映射。
func (lb *SourceIpHashLoadBalancer) AddServer(serverName string) {
    lb.ipToServerMap[serverName] = serverName
}

// GetServerForIp retrieves the server associated with the given source IP.
// 通过 sourceIp 的哈希值计算索引,将请求分配到一个服务器。
func (lb *SourceIpHashLoadBalancer) GetServerForIp(sourceIp string) string {
    // Calculate hash for the source IP.
    hash := fnv.New32a()
    hash.Write([]byte(sourceIp))
    hashValue := hash.Sum32()
    fmt.Printf("\nsourceIp: %s, hashValue: %d\n", sourceIp, hashValue)
    // Get the list of available servers.
    var servers []string
    for server := range lb.ipToServerMap {
        servers = append(servers, server)
    }

    // Map the hash value to a server index.
    serverIndex := int(hashValue) % len(servers)

    // Return the selected server.
    return servers[serverIndex]
}

func main() {
    // Create a source IP hash load balancer.
    loadBalancer := NewSourceIpHashLoadBalancer()

    // Add servers to the load balancer.
    loadBalancer.AddServer("Server1")
    loadBalancer.AddServer("Server2")
    loadBalancer.AddServer("Server3")

    // Simulate requests with different source IPs.
    sourceIps := []string{"192.168.1.1", "10.0.0.1", "172.16.0.1", "192.168.1.1"}

    for _, sourceIp := range sourceIps {
        selectedServer := loadBalancer.GetServerForIp(sourceIp)
        fmt.Printf("Request from %s routed to %s\n", sourceIp, selectedServer)
    }
}

运行

sourceIp: 192.168.1.1, hashValue: 2103183524
Request from 192.168.1.1 routed to Server3

sourceIp: 10.0.0.1, hashValue: 3737042573
Request from 10.0.0.1 routed to Server3

sourceIp: 172.16.0.1, hashValue: 4173611025
Request from 172.16.0.1 routed to Server2

sourceIp: 192.168.1.1, hashValue: 2103183524
Request from 192.168.1.1 routed to Server3

这里使用 map 数据结构来存储服务器,并使用 Go 的 hash/fnv 包计算源 IP 的哈希值

SourceIpHashLoadBalancer

属性:

  • ipToServerMap:服务器名称到服务器名称的映射(用于一致性哈希)。
  • AddServer: 用于将服务器添加到 ipToServerMap 映射。

方法:

  • addServer:将服务器添加到负载均衡器。
  • getServerForIp:计算源 IP 的哈希值,并确定处理请求的服务器。 通过 sourceIp 哈希值计算索引,将请求分配到一个服务器。

SourceIpHashLoadBalancerExample 类演示
SourceIpHashLoadBalancer 创建负载均衡器的实例,添加服务器,并模拟来自不同源 IP 的请求。
fnv.New32a: 使用 FNV-1a 哈希算法处理 IP 字符串并生成整数哈希值。
NewSourceIpHashLoadBalancer: 构造函数,初始化 SourceIpHashLoadBalancer

何时使用源 IP 哈希负载均衡算法?

非常适合需要会话一致性的应用程序,例如网上银行,其中同一用户必须在整个会话期间连接到同一服务器。
当特定区域的用户应连接到专用服务器以获得更好的性能或合规性时,这非常有用。
当少数 IP 产生大部分流量时有效,确保负载均衡分配,无需随机切换。

基于IP 哈希负载均衡算法的优缺点:

优点:
一致性:确保来自同一源 IP 的请求始终发送到同一服务器,同时保持会话状态。 可预测性:当连接持久性至关重要时非常有用。

缺点:
有限分布:如果某些源 IP 更活跃,可能会导致负载分布不均匀。 扩展挑战:添加或删除服务器可能会中断会话持久性。

最小连接时间算法

最小连接算法是一种动态负载平衡方法,它将新请求分配给活动连接最少的服务器。其理念是以最小化每台服务器上当前负载的方式分配传入的工作负载,目标是在所有可用资源之间平衡分配连接。

为此,负载均衡器需要执行一些额外的计算,以确定连接数最少的服务器。 与循环方法相比,这可能成本略高,但评估基于服务器上的当前负载。

实现一种负载均衡算法,将传入请求分配到一组服务器之间,并且应该通过将新请求定向到活动连接最少的服务器来最小化每个服务器上的活动连接数。这确保了工作负载的均衡分配,并防止了单个服务器的过载.

如下最少连接 负载均衡器会将请求分配给连接数最少的服务器、

package main

import (
    "fmt"
    "math"
    "sync"
)

// LeastConnectionLoadBalancer 结构体定义
type LeastConnectionLoadBalancer struct {
    serverConnections map[string]int
    mutex             sync.Mutex
}

// NewLeastConnectionLoadBalancer 构造函数,初始化负载均衡器
func NewLeastConnectionLoadBalancer() *LeastConnectionLoadBalancer {
    return &LeastConnectionLoadBalancer{
        serverConnections: make(map[string]int),
    }
}

// AddServer 添加服务器到负载均衡器,初始连接数为0
func (lb *LeastConnectionLoadBalancer) AddServer(serverName string) {
    lb.mutex.Lock()
    defer lb.mutex.Unlock()
    lb.serverConnections[serverName] = 0
}

// GetServerWithLeastConnections 找到连接数最少的服务器
func (lb *LeastConnectionLoadBalancer) GetServerWithLeastConnections() string {
    lb.mutex.Lock()
    defer lb.mutex.Unlock()

    minConnections := math.MaxInt32
    var selectedServer string

    for server, connections := range lb.serverConnections {
        if connections < minConnections {
            minConnections = connections
            selectedServer = server
        }
    }

    // 更新所选服务器的连接数
    if selectedServer != "" {
        lb.serverConnections[selectedServer]++
    }

    return selectedServer
}

// ReleaseConnection SimulateRequest 模拟一次请求结束后释放服务器的连接
func (lb *LeastConnectionLoadBalancer) ReleaseConnection(serverName string) {
    lb.mutex.Lock()
    defer lb.mutex.Unlock()

    if _, exists := lb.serverConnections[serverName]; exists && lb.serverConnections[serverName] > 0 {
        lb.serverConnections[serverName]--
    }
}

func main() {
    // 创建最少连接负载均衡器
    loadBalancer := NewLeastConnectionLoadBalancer()

    // 添加服务器
    loadBalancer.AddServer("Server1")
    loadBalancer.AddServer("Server2")
    loadBalancer.AddServer("Server3")

    // 模拟请求
    for i := 0; i < 10; i++ {
        selectedServer := loadBalancer.GetServerWithLeastConnections()
        fmt.Printf("Request %d: Routed to %s\n", i+1, selectedServer)

        // 模拟请求处理后释放连接
        loadBalancer.ReleaseConnection(selectedServer)
    }
}

运行输出:

Request 1: Routed to Server1
Request 2: Routed to Server1
Request 3: Routed to Server1
Request 4: Routed to Server1
Request 5: Routed to Server1
Request 6: Routed to Server3
Request 7: Routed to Server1
Request 8: Routed to Server1
Request 9: Routed to Server1
Request 10: Routed to Server1

可以确保请求被均匀分配到连接数最少的服务器,并在处理完请求后释放连接。

LeastConnectionLoadBalancer 结构体用于存储服务器连接数的映射和一个互斥锁以确保并发安全。
NewLeastConnectionLoadBalancer 函数创建负载均衡器实例。
AddServer 方法用于将新服务器添加到映射中,连接数初始为0。
GetServerWithLeastConnections 方法寻找连接数最少的服务器,并递增该服务器的连接计数。
ReleaseConnection 模拟释放连接的功能,减少指定服务器的连接数。
main 函数模拟了10次请求的路由和连接释放。

何时使用最小连接负载均衡算法?

非常适合某些请求需要比其他请求处理更长的时间的应用程序(例如,视频流或大文件上传)。
当某些连接保持活动状态的时间更长时很有用,因为它可以确保新请求发送到活动连接较少的服务器。
非常适合流量波动的系统,因为它根据实时服务器负载进行平衡,而不仅仅是计算请求。

最小连接负载均衡算法的优缺点:

优点:

  • 平衡负载:将流量分配到活动连接最少的服务器,防止过载。
  • 动态:适应不断变化的服务器工作负载。

缺点:

  • Ignored Capacities:忽略服务器容量;连接较少的服务器可能仍具有较少的容量。
  • 粘性会话:可能不适用于需要会话持久性的场景。

最少响应时间算法

最少响应方法是一种动态负载平衡方法,旨在通过将新请求定向到响应时间最快的服务器来最大限度地减少响应时间。

它会考虑服务器的历史性能,以决定将传入请求路由到何处,从而优化以加快处理速度。 动态方面来自对服务器响应时间的持续监控以及算法的自适应性,以将传入请求路由到具有历史最短响应时间的服务器。

这是一种将传入请求分配到一组服务器的负载平衡算法,并且应该通过将新请求定向到累积响应时间最少的服务器来最小化响应时间。这可确保工作负载的均衡分配,并有助于优化整体系统性能。

以下是最小响应时间负载均衡器将请求分配给累计响应时间最少的服务器,并在每次选择后增加该服务器的响应时间。

package main

import (
    "fmt"
    "math"
    "sync"
)

// LeastResponseLoadBalancer 结构体定义
type LeastResponseLoadBalancer struct {
    serverResponseTimes map[string]int64
    mutex               sync.Mutex
}

// NewLeastResponseLoadBalancer 构造函数,初始化负载均衡器
func NewLeastResponseLoadBalancer() *LeastResponseLoadBalancer {
    return &LeastResponseLoadBalancer{
        serverResponseTimes: make(map[string]int64),
    }
}

// AddServer 将服务器添加到负载均衡器,初始响应时间为0
func (lb *LeastResponseLoadBalancer) AddServer(serverName string) {
    lb.mutex.Lock()
    defer lb.mutex.Unlock()
    lb.serverResponseTimes[serverName] = 0
}

// GetServerWithLeastResponseTime 获取累计响应时间最少的服务器
func (lb *LeastResponseLoadBalancer) GetServerWithLeastResponseTime() string {
    lb.mutex.Lock()
    defer lb.mutex.Unlock()

    minResponseTime := int64(math.MaxInt64)
    var selectedServer string

    // 查找响应时间最少的服务器
    for server, responseTime := range lb.serverResponseTimes {
        if responseTime < minResponseTime {
            minResponseTime = responseTime
            selectedServer = server
        }
    }

    // 更新所选服务器的累计响应时间
    if selectedServer != "" {
        lb.serverResponseTimes[selectedServer]++
    }

    return selectedServer
}

func main() {
    // 创建最少响应时间负载均衡器
    loadBalancer := NewLeastResponseLoadBalancer()

    // 添加服务器
    loadBalancer.AddServer("Server1")
    loadBalancer.AddServer("Server2")
    loadBalancer.AddServer("Server3")

    // 模拟请求
    for i := 0; i < 10; i++ {
        selectedServer := loadBalancer.GetServerWithLeastResponseTime()
        fmt.Printf("Request %d: Routed to %s\n", i+1, selectedServer)
    }
}

运行输出

Request 1: Routed to Server2
Request 2: Routed to Server1
Request 3: Routed to Server3
Request 4: Routed to Server1
Request 5: Routed to Server2
Request 6: Routed to Server3
Request 7: Routed to Server1
Request 8: Routed to Server2
Request 9: Routed to Server3
Request 10: Routed to Server1

输出会根据服务器的响应时间自动分配,但确保每次请求分配给响应时间最低的服务器,从而均衡负载。

  • 结构体:LeastResponseLoadBalancer 使用 serverResponseTimes 维护一个服务器名称与其累计响应时间的映射。
  • 互斥锁:使用 sync.Mutex 来保证并发安全。
  • 构造函数:NewLeastResponseLoadBalancer 初始化一个负载均衡器实例。
  • AddServer:将服务器添加到映射中,初始响应时间设为0。
  • GetServerWithLeastResponseTime:寻找累计响应时间最少的服务器,并增加该服务器的响应时间。
  • main 函数:模拟了10次请求,每次选择累计响应时间最少的服务器。

何时使用最小响应时间负载均衡算法?

  • 非常适合用户流量大、波动性大且响应时间很重要的应用程序。
  • 非常适合电子商务网站或流媒体服务等应用程序,在这些应用程序中,快速响应可以改善用户体验。
  • 当服务器具有不同的负载级别时,它的效果很好,因为它将流量定向到可用且响应速度最快的服务器。

最短响应时间负载均衡算法的优缺点:

好处:

  • 优化性能:将流量定向到响应时间最快的服务器,从而优化整体系统性能。
  • 适应性强:根据服务器响应能力随时间的变化进行调整。

缺点:

  • 历史偏差:受过去响应时间的严重影响,可能并不总是反映当前的服务器功能。
  • 复杂实施:需要跟踪和管理历史响应时间。

基于资源的负载均衡

基于资源的负载均衡算法根据每个服务器的当前资源可用性(如 CPU 使用率、内存或网络带宽)分配传入请求。此算法不仅仅是平等地路由流量或基于过去的性能,而是评估每个服务器的当前 “资源运行状况” 以决定新请求的去向。

基于资源的负载平衡将请求定向到具有最多可用资源的服务器。

要实施基于资源的负载均衡,需要跟踪每个服务器的当前资源,然后将每个新请求路由到能够根据实时数据最好地处理它的服务器。以下是基于资源的负载均衡算法的示例实现。

这个简单的实现使用 CPU 负载作为指标来决定哪个服务器应该处理每个请求。

以下基于资源的负载均衡器根据当前服务器CPU负载进行选择,将请求路由到CPU负载最低的服务器:

package main

import (
    "fmt"
    "math"
    "sync"
)

// Server 结构体表示服务器,包含名称和CPU负载
type Server struct {
    name    string
    cpuLoad float64
    mu      sync.Mutex
}

// NewServer 创建一个新的服务器实例
func NewServer(name string) *Server {
    return &Server{name: name, cpuLoad: 0.0}
}

// UpdateCpuLoad 更新服务器的CPU负载
func (s *Server) UpdateCpuLoad(load float64) {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.cpuLoad = load
}

// GetCpuLoad 获取当前的CPU负载
func (s *Server) GetCpuLoad() float64 {
    s.mu.Lock()
    defer s.mu.Unlock()
    return s.cpuLoad
}

// GetName 获取服务器名称
func (s *Server) GetName() string {
    return s.name
}

// ResourceBasedLoadBalancer 结构体表示基于资源的负载均衡器
type ResourceBasedLoadBalancer struct {
    servers map[string]*Server
    mu      sync.Mutex
}

// NewResourceBasedLoadBalancer 创建一个新的资源负载均衡器
func NewResourceBasedLoadBalancer() *ResourceBasedLoadBalancer {
    return &ResourceBasedLoadBalancer{
        servers: make(map[string]*Server),
    }
}

// AddServer 将服务器添加到负载均衡器
func (lb *ResourceBasedLoadBalancer) AddServer(server *Server) {
    lb.mu.Lock()
    defer lb.mu.Unlock()
    lb.servers[server.GetName()] = server
}

// GetServerWithMostResources 获取CPU负载最小的服务器
func (lb *ResourceBasedLoadBalancer) GetServerWithMostResources() *Server {
    lb.mu.Lock()
    defer lb.mu.Unlock()

    var bestServer *Server
    lowestLoad := math.MaxFloat64

    for _, server := range lb.servers {
        if load := server.GetCpuLoad(); load < lowestLoad {
            lowestLoad = load
            bestServer = server
        }
    }
    return bestServer
}

// HandleRequest 处理请求并选择最佳服务器
func (lb *ResourceBasedLoadBalancer) HandleRequest() {
    bestServer := lb.GetServerWithMostResources()
    if bestServer != nil {
        fmt.Printf("Routing request to server: %s with current CPU load: %.2f%%\n", bestServer.GetName(), bestServer.GetCpuLoad())
    } else {
        fmt.Println("No servers available.")
    }
}

func main() {
    // 创建资源负载均衡器
    loadBalancer := NewResourceBasedLoadBalancer()

    // 创建服务器并添加到负载均衡器
    server1 := NewServer("Server1")
    server2 := NewServer("Server2")
    server3 := NewServer("Server3")

    loadBalancer.AddServer(server1)
    loadBalancer.AddServer(server2)
    loadBalancer.AddServer(server3)

    // 更新各服务器的CPU负载
    server1.UpdateCpuLoad(30.0)
    server2.UpdateCpuLoad(50.0)
    server3.UpdateCpuLoad(20.0)

    // 根据当前CPU负载路由请求
    loadBalancer.HandleRequest() // 应该路由到 Server3,因为它的 CPU 负载最低
}

示例输出

Routing request to server: Server3 with current CPU load: 20.00%

这个实现会根据当前CPU负载选择最优服务器,并将请求路由到负载最低的服务器,从而均衡服务器的资源使用情况。
Server结构体:Server 结构体包含服务器名称和CPU负载信息。UpdateCpuLoad 用于更新CPU负载,GetCpuLoad 返回当前CPU负载。
ResourceBasedLoadBalancer结构体:ResourceBasedLoadBalancer 管理服务器列表,提供根据资源选择服务器的功能。
AddServer:将服务器添加到servers映射中。
GetServerWithMostResources:遍历所有服务器,找到CPU负载最低的服务器。
HandleRequest:模拟请求处理,通过GetServerWithMostResources找到最优服务器并输出。

何时使用基于资源的负载均衡

对于执行 CPU 密集型或内存密集型任务的应用程序非常有用。
当服务器具有不同的资源级别时,该算法效果很好,因为该算法会适应每个服务器的实时容量。
通过将请求路由到过载最少的服务器来确保可用性,从而降低停机风险。

基于资源的负载均衡算法的优缺点

优点:

  • 资源优化:根据实时资源数据均衡工作负载,提高系统效率。
  • 适应性:根据每个服务器资源的当前状态动态调整。

缺点:

  • 复杂的实施:需要持续监控服务器资源,这可能会增加复杂性。
  • 更高的开销:实时跟踪资源本身可能会消耗额外的系统资源。
关于我
loading