irpas技术客

负载均衡算法_jaronho_负载均衡算法

网络 8420

文章目录 一、前言二、概述三、常见的负载均衡算法1、轮询(Round Robin)法2、随机(Random)法3、加权轮询(Weight Round Robin)法4、加权随机(Weight Random)法5、最小连接数(Least Connections)法

一、前言

????????最近在开发聊天室服务器,考虑到服务器的并发性,因此需要支持聊天服务器的集群功能。对服务器需要做负载均衡,查阅了一些资料,在此做下简单总结和笔记。

二、概述

????????负载均衡(Load Balance),指由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助。通过某种负载分担技术,将外部发送来的请求均匀分配到对称结构中的某一台服务器上,而接收到请求的服务器独立地回应客户的请求。负载均衡能够平均分配客户请求到服务器阵列,借此提供快速获取重要数据,解决大量并发访问服务问题,这种集群技术可以用最少的投资获得接近于大型主机的性能。

????????目前负载均衡算法主要分为两类:

静态负载均衡算法:以固定的概率分配任务,不考虑服务器的状态信息,如:轮询法、加权轮询法、随机法、加权随机法等。动态负载均衡算法:以服务器的实时负载状态信息来决定任务的分配,如:最小连接法、加权最小连接数法等。 三、常见的负载均衡算法 1、轮询(Round Robin)法

????????轮询法,就是将用户的请求轮流分配给服务器,比如有10台服务器,从第1台开始分配,每次有请求进来,就分配到下一台服务器。这种算法比较简单,具有绝对均衡的优点,但是也正是因为绝对均衡,因此它无法保证分配任务的合理性,无法根据服务器承受能力来分配任务。 ????????结论:轮询法适用于机器性能都在同一水平的服务,一旦某台机器性能不好,极有可能产生木桶效应,性能差的机器扛不住更多的流量。

2、随机(Random)法

????????随机法,是随机选择一台服务器来分配任务。它保证了请求的分散性达到了均衡的目的。同时它是没有状态的不需要维持上次的选择状态和均衡因子。但是随着任务量的增大,它的效果趋向轮询后也会具有轮询算法的部分缺点。 ????????结论:同样地,它也不适用于机器性能有差异的分布式系统。

3、加权轮询(Weight Round Robin)法

????????实际上,不同的服务器存在着配置和当前系统的负载不相同,因此它们的处理能力也不一样。所以对配置高、负载低的机器分配更高的权重,使其能够处理更多的请求,而配置低、负载高的机器,则给其分配较低的权重,降低其系统负载,加权轮询很好的处理了这一问题,并将请求按照顺序且根据权重分配给服务器。 ????????说明:Nginx默认的负载均衡使用的就是加权轮询法。 例如:集群中共有3台服务器A、B、C,其中A的硬件水平是B的2倍,B的硬件水平是C的3倍,则权重值可以如下划分:

服务器A服务器B服务器C631

分配10次,服务器的选择过程如下:

第N轮服务器1A2A3A4A5B6A7B8A9B10C

算法:

首先计算所有服务器权重的最大公约数maxGcd,以及所有服务器权重的最大值maxWeight。currIndex表示选择的服务器的索引,初始值为-1,currWeight表示当前调度的权值,初始值为0。从currIndex+1开始轮询服务器数组并保存索引到currIndex,找到其中权重大于currWeight的第一个服务器。在轮询时,如果到达了数组末尾,则重新从头开始搜索,并且减小currWeight的值:currWeight -= maxGcd。如果currWeight等于0,则将其重置为maxWeight。

Golang的实现 wrr.go

package slb import ( "math" "sync" ) // 节点 type WrrNode struct { server string // 服务器 weight int // 权重值 } // 加权轮询(Nginx的默认负载均衡) type WeightRoundRobin struct { nodeList []*WrrNode // 服务器节点列表 currIndex int // 当前索引值 currWeight int // 当前权重 mux sync.Mutex } // 功能: 计算两个数的最大公约数 // // 返回: 最大公约数 func getGcd(a int, b int) int { c := 0 for b > 0 { c = b b = a % b a = c } return a } // 功能: 获取所有权重的最大公约数 // // 返回: 最大公约数 func (r *WeightRoundRobin) getMaxGcd() int { maxGcd := 0 if r.nodeList != nil { for _, node := range r.nodeList { if maxGcd == 0 { maxGcd = node.weight } else { maxV := int(math.Max(float64(maxGcd), float64(node.weight))) // 比较上次计算结果和本次权重值, 取两个数中的较大者 minV := int(math.Min(float64(maxGcd), float64(node.weight))) // 比较上次计算结果和本次权重值, 取两个数中的较小者 maxGcd = getGcd(maxV, minV) } } } return maxGcd } // 功能: 获取权重最大值 // // 返回: 权重最大值 func (r *WeightRoundRobin) getMaxWeight() int { maxWeight := 0 if r.nodeList != nil { for _, node := range r.nodeList { if node.weight > maxWeight { maxWeight = node.weight } } } return maxWeight } // 功能: 添加节点 // // 参数: server 服务器地址 // // 参数: weight 权重 // // 返回: true-成功, fasle-失败 func (r *WeightRoundRobin) Add(server string, weight int) bool { r.mux.Lock() defer r.mux.Unlock() if r.nodeList == nil { r.nodeList = []*WrrNode{} r.currIndex = -1 r.currWeight = 0 } for i := 0; i < len(r.nodeList); i++ { if server == r.nodeList[i].server { return false } } node := &WrrNode{server: server, weight: weight} r.nodeList = append(r.nodeList, node) return true } // 功能: 删除节点 // // 参数: server 服务器地址 func (r *WeightRoundRobin) Del(server string) { r.mux.Lock() defer r.mux.Unlock() if r.nodeList != nil { for i := 0; i < len(r.nodeList); i++ { if server == r.nodeList[i].server { r.nodeList = append(r.nodeList[0:i], r.nodeList[i+1:]...) return } } } } // 功能: 修改节点 // // 参数: server 服务器地址 // // 参数: weight 权重 func (r *WeightRoundRobin) Modify(server string, weight int) { r.mux.Lock() defer r.mux.Unlock() if r.nodeList != nil { for i := 0; i < len(r.nodeList); i++ { if server == r.nodeList[i].server { r.nodeList[i].weight = weight return } } } } // 功能: 获取服务器 // // 返回: 服务器 func (r *WeightRoundRobin) GetServer() string { r.mux.Lock() defer r.mux.Unlock() if len(r.nodeList) <= 0 { return "" } maxGcd := r.getMaxGcd() // 获取所有权重的最大公约数 maxWeight := r.getMaxWeight() // 获取所有权重的最大值 if r.currIndex >= len(r.nodeList) { r.currIndex = -1 r.currWeight = 0 } for { r.currIndex = (r.currIndex + 1) % len(r.nodeList) if r.currIndex == 0 { r.currWeight = r.currWeight - maxGcd if r.currWeight <= 0 { r.currWeight = maxWeight if r.currWeight == 0 { return "" } } } node := r.nodeList[r.currIndex] if node.weight >= r.currWeight { return node.server } } }

main.go

package main import ( "log" "strconv" "test/slb" ) func main() { wrr := slb.WeightRoundRobin{} // 不同节点的添加顺序可能结果会有些不同(不影响主要算法) wrr.Add("a", 6) wrr.Add("b", 3) wrr.Add("c", 1) // 模拟10次请求进入 for i := 1; i <= 10; i++ { server := wrr.GetServer() log.Println("[" + strconv.Itoa(i) + "] " + server) } }

输出:

D:\workspace\test_go>go run main.go 2021/12/22 11:19:59 [1] a 2021/12/22 11:19:59 [2] a 2021/12/22 11:19:59 [3] a 2021/12/22 11:19:59 [4] a 2021/12/22 11:19:59 [5] b 2021/12/22 11:19:59 [6] a 2021/12/22 11:19:59 [7] b 2021/12/22 11:19:59 [8] a 2021/12/22 11:19:59 [9] b 2021/12/22 11:19:59 [10] c 4、加权随机(Weight Random)法

????????与加权轮询法类似,加权随机法也是根据服务器不同的配置和负载情况来配置不同的权重。不同的是,它是按照权重来随机选择服务器的,而不是顺序。

5、最小连接数(Least Connections)法

????????最小连接法,将任务分配给此时具有最小连接数的服务器。当一台服务器收到一个请求后,它的连接数间1。但是根据连接数并不能真实的反应服务器的负载能力,当服务器不处于同一水平时,有可能连接数少的服务器处理能力差,而连接数多的服务器处理能力强。 ????????结论:最小连接法适用于机器性能都在同一水平的服务。 Golang实现 lc.go

package slb import ( "sync" ) // 节点 type LcNode struct { server string // 服务器 connections int // 当前连接数 } // 最小连接数 type LeastConnections struct { nodeList map[string]*LcNode // 服务器节点列表 mux sync.Mutex } // 功能: 添加节点 // // 参数: server 服务器地址 // // 参数: connections 连接数 // // 返回: true-成功, fasle-失败 func (r *LeastConnections) Add(server string, connections int) bool { r.mux.Lock() defer r.mux.Unlock() if r.nodeList == nil { r.nodeList = map[string]*LcNode{} } if _, ok := r.nodeList[server]; ok { return false } node := &LcNode{server: server, connections: connections} r.nodeList[server] = node return true } // 功能: 删除节点 // // 参数: server 服务器地址 func (r *LeastConnections) Del(server string) { r.mux.Lock() defer r.mux.Unlock() if r.nodeList != nil { delete(r.nodeList, server) } } // 功能: 修改节点 // // 参数: server 服务器地址 // // 参数: connections 连接数 func (r *LeastConnections) Modify(server string, connections int) { r.mux.Lock() defer r.mux.Unlock() if r.nodeList != nil { if node, ok := r.nodeList[server]; ok { node.connections = connections } } } // 获取服务器 func (r *LeastConnections) GetServer() string { r.mux.Lock() defer r.mux.Unlock() var best *LcNode for server := range r.nodeList { node := r.nodeList[server] if best == nil || node.connections < best.connections { best = node } } if best == nil { return "" } return best.server }


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #负载均衡算法 #Round #对服务器需要做负载均衡查阅