六种常见负载均衡算法(Go语言实现)

原创
2021/05/01 20:57
阅读数 5.4K

引言

你说妹子太多了,压力有点大,身体有点吃不消,有点支持不住了。
于是乎,你找了几个兄弟说:哥们,帮忙分担一下压力呗。于是你开始把这些妹子推向你兄弟的魔爪,把妹子一个个的介绍给你不同的兄弟,并且尽力撮合他们。PS: 当然自己得留点。

这大概就是负载均衡,你既充当着负载均衡器, 也充当着服务器, 你的兄弟就是个服务器。你的压力相比之下减少了很多,这就是负载均衡的作用。

用专业术语来说:

  • 负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。
  • 负载均衡(Load Balance)其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。

简单点来说:

  • 其实就是请求太多,一台服务器处理不过来,把它分摊到多台服务器处理。

那么如何将请求分摊到多台服务器?

接下来一一介绍常见的几种负载均衡算法。

轮询法

  • 将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eAolzy2P-1619539822631)(https://www.classmatelin.top/images/20210427234214.png)]

  • Go语言实现示例如下:

    /** * Author: ClassmateLin * Site: https://www.classmatelin.top * mail: classmatelin.site@gmail.com * Date: 2021/4/26 21:32 */
    
    package main
    
    import (
    	"fmt"
    )
    
    type RoundRobin struct {
         
    	servers []string
    	current int
    }
    
    /** 获取下一个服务器 */
    func (R *RoundRobin) next() string {
         
    	R.current++
    	R.current = R.current % len(R.servers) // 访问到最后一个服务器之后,重置会第一台。 5%5=0。
    	return R.servers[R.current]
    }
    
    func main() {
         
    
    	r := &RoundRobin{
         
    		servers: []string{
         "192.168.10", "192.168.11", "192.168.12"},
    		current: -1,
    	}
    	
    	for i := 0; i < 10; i++ {
         
    		fmt.Printf("| %d | %s |\n", i + 1, r.next())
    	}
    }

加权轮询法

  • 每台后端服务器的配置可能不太一样,有些性能好,能处理的请求多, 有些则性能比较差,能处理的请求较少。

  • 它们的抗压能力不相同,因此按顺序的分配服务器的话导致性能好的服务器无法发挥最大作用,性能差的服务器压力太大。

  • 那么加权轮询法可以解决这个问题,给性能好的服务器分配较高的权重,性能差的服务器分配较低的权重。

  • go语言实现的平滑的加权轮询法:

/** * @Author: ClassmateLin * @Site: https://www.classmatelin.top * @Email: classmatelin.site@gmail.com * @Date: 2021/4/27 22:44 */

package main

import "fmt"

type Server struct {
   
	host string  // 主机地址
	weight int // 配置的权重
	currentWeight int  // 当前权重
}

func getSever(servers []*Server) (s *Server)  {
   

	allWeight := 0 // 总权重

	for _, server := range servers {
   
		if server == nil {
   
			return nil
		}
		// 每一轮选择都用自身的权重加到当前权重
		allWeight += server.weight
		server.currentWeight += server.weight

		// 当前未选中节点或当前节点比之前选中的节点权重高,那么更新当前选中的节点
		if s == nil || server.currentWeight > s.currentWeight{
   
			s = server
		}
	}

	s.currentWeight -= allWeight

	return
}


func main() {
   
	servers := []*Server{
   
		{
   "192.168.10.10", 5, 0},
		{
   "192.168.10.11", 2, 0},
		{
   "192.168.10.12", 1, 0},
	}

	for i := 0; i < 20; i++ {
   
		server := getSever(servers)

		if server == nil {
   
			continue
		}

		fmt.Printf("| %s | %d |\n", server.host, server.weight)
	}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eVhLqR1j-1619539822633)(https://www.classmatelin.top/images/20210427233715.png)]

  • 平滑是指什么?例如有以下三台服务器:
A: {
   host:"192.168.10.10", "weight": 3}
B: {
   host:"192.168.10.11", "weight": 1}
C: {
   host:"192.168.10.12", "weight": 1}

如果出现的序列为AAABC, 可能会给第一台机器造成压力过大。如果出现的序列为ABACA,避免同时造成同一台服务器压力过大的问题,就是平滑的。)

随机法

  • 随机法就是通过随机算法,从服务器列表中随机地选取一台服务器进行访问。随着客户端调用服务器的次数增加到一定数量级别,每台服务器的平均访问次数跟轮询法的次数相近,也就是说趋近于轮询法。

  • go实现示例:

/** * @Author: ClassmateLin * @Site: https://www.classmatelin.top * @Email: classmatelin.site@gmail.com * @Date: 2021/4/27 22:44 */

package main

import (
	"fmt"
	"math/rand"
)

type Random struct {
   
	servers []string
}

func (R *Random) next() string {
   
	return R.servers[rand.Intn(len(R.servers))]
}

func main()  {
   
	r := Random{
   
		servers: []string{
   "192.168.10.10", "192.168.10.11", "192.168.10.12"},
	}

	for i := 0; i < 10; i++ {
   
		fmt.Println(r.next())
	}
}

加权随机法

  • 加权随机法根据后端机器的配置,系统的负载分配不同的权重,按照权重进行随机选取,与加权轮询法相似。

  • go语言实现示例:

源地址哈希法

  • 源地址哈希法是根据请求来源的地址,通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。

  • 采用源地址哈希法进行负载均衡,同一源地址的请求,当服务器列表不变时,它每次都会映射到同一台服务器进行访问。

  • go语言实现示例:

最小数连接法

  • 最小连接数法是根据服务器当前的连接情况进行负载均衡的,当请求到来时,会选取当前连接数最少的一台服务器来处理请求。

  • 由此也可以延伸出,根据服务器CPU 占用最少,根据单位时间内处理请求的效率高低等进行服务器选择。

  • go语言实现示例:

展开阅读全文
go
加载中

作者的其它热门文章

打赏
1
11 收藏
分享
打赏
1 评论
11 收藏
1
分享
返回顶部
顶部