负载均衡算法

一、前言

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

二、概述

        负载均衡(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 服务器C
6 3 1
分配10次,服务器的选择过程如下:
第N轮 服务器
1 A
2 A
3 A
4 A
5 B
6 A
7 B
8 A
9 B
10 C
算法
  • 首先计算所有服务器权重的最大公约数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
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值