算法之权重轮询算法

Posted 浅弋、璃鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法之权重轮询算法相关的知识,希望对你有一定的参考价值。

实现:

package transform

import (
	"fmt"
	"strings"
	"sync"

	"project/pkg/base/log"
)

// 权重轮询调度算法

// 每一个服务定义
type RecvServer struct  // nolint
	Weight        int
	currentWeight int
	RecvAddr      string


// 通过权重实现调用轮询的定义
type WeightServerRoundRobin struct  // nolint
	effectiveWeight int
	recvServerList  []*RecvServer
	sync.Mutex


// NewWeightServerRoundRobin 创建一个负载轮询器
func NewWeightServerRoundRobin() *WeightServerRoundRobin 
	return &WeightServerRoundRobin
		effectiveWeight: 0,
	


// AddRecvServer 新增一个负载节点
func (r *WeightServerRoundRobin) AddRecvServer(recvServer *RecvServer) 
	r.Lock()
	defer r.Unlock()
	r.effectiveWeight += recvServer.Weight
	r.recvServerList = append(r.recvServerList, recvServer)


// GetRecvServer 获取可用的负载的节点
func (r *WeightServerRoundRobin) GetRecvServer() *RecvServer 
	var expectRecvServer *RecvServer
	if len(r.recvServerList) == 0 
		// 没有可用的
		return expectRecvServer
	
	r.Lock()
	defer r.Unlock()
	for _, recvServer := range r.recvServerList 
		// 给每个服务增加自身权重
		recvServer.currentWeight += recvServer.Weight
		if expectRecvServer == nil 
			expectRecvServer = recvServer
		
		if recvServer.currentWeight > expectRecvServer.currentWeight 
			expectRecvServer = recvServer
		
	
	// r.VisitRecvServerCurrentWeight()
	// 把选择的服务权重减掉总权重
	expectRecvServer.currentWeight -= r.effectiveWeight
	return expectRecvServer


// UpdateWeighted 更新recvServer节点的权重newWeighted
func (r *WeightServerRoundRobin) UpdateWeighted(recvServer *RecvServer, newWeighted int) 
	r.Lock()
	defer r.Unlock()
	subWeight := recvServer.Weight - newWeighted
	recvServer.Weight = newWeighted
	r.effectiveWeight -= subWeight


// VisitRecvServerCurrentWeight 打印服务的当前权重变化
func (r *WeightServerRoundRobin) VisitRecvServerCurrentWeight() 
	serverListForLog := []string
	for _, recvServer := range r.recvServerList 
		serverListForLog = append(serverListForLog,
			fmt.Sprintf("%v", recvServer.currentWeight))
	
	log.Infof("(%v)\\n", strings.Join(serverListForLog, ", "))

测试:

package transform

import (
	"fmt"
	"testing"
)

func TestRecvServerRoundRobin1(t *testing.T) 
	weightServerRoundRobin := NewWeightServerRoundRobin()
	weightServerRoundRobin.AddRecvServer(&RecvServer
		RecvAddr: "ServerA",
		Weight:   5,
	)
	weightServerRoundRobin.AddRecvServer(&RecvServer
		RecvAddr: "ServerB",
		Weight:   3,
	)
	weightServerRoundRobin.AddRecvServer(&RecvServer
		RecvAddr: "ServerC",
		Weight:   2,
	)

	for i := 0; i < 10; i++ 
		weightServerRoundRobin.VisitRecvServerCurrentWeight()
		recvServer := weightServerRoundRobin.GetRecvServer()
		if recvServer == nil 
			t.Error("获取不到可用的负载")
			return
		
		fmt.Printf("%d[%v], actual:%v\\n", i, t.Name(), recvServer.RecvAddr)
	


func TestRecvServerRoundRobin2(t *testing.T) 
	weightServerRoundRobin := NewWeightServerRoundRobin()
	weightServerRoundRobin.AddRecvServer(&RecvServer
		RecvAddr: "ServerA",
		Weight:   5,
	)
	weightServerRoundRobin.AddRecvServer(&RecvServer
		RecvAddr: "ServerB",
		Weight:   3,
	)
	weightServerRoundRobin.AddRecvServer(&RecvServer
		RecvAddr: "ServerC",
		Weight:   2,
	)

	fmt.Println("(A, B, C)")
	for i := 0; i <= 10; i++ 
		weightServerRoundRobin.VisitRecvServerCurrentWeight()
		recvServer := weightServerRoundRobin.GetRecvServer()
		if recvServer == nil 
			t.Error("获取不到可用的负载")
			return
		
		// rn := rand.Intn(10)
		rn := 2
		if i == 0 
			weightServerRoundRobin.UpdateWeighted(recvServer, rn)
		
		fmt.Printf("%d[%v], actual:%v; effectiveWeight: %d\\n", rn, t.Name(), recvServer.RecvAddr, weightServerRoundRobin.effectiveWeight)
	


func TestRecvServerRoundRobin3(t *testing.T) 
	weightServerRoundRobin := NewWeightServerRoundRobin()
	weightServerRoundRobin.AddRecvServer(&RecvServer
		RecvAddr: "ServerA",
		Weight:   5,
	)
	weightServerRoundRobin.AddRecvServer(&RecvServer
		RecvAddr: "ServerB",
		Weight:   3,
	)
	weightServerRoundRobin.AddRecvServer(&RecvServer
		RecvAddr: "ServerC",
		Weight:   1,
	)

	expectServerNameList := []string
		"ServerA", "ServerB", "ServerA", "ServerC", "ServerA", "ServerB", "ServerA", "ServerB", "ServerA",
		"ServerA", "ServerB", "ServerA", "ServerC", "ServerA", "ServerB", "ServerA", "ServerB", "ServerA",
	
	fmt.Println("(A, B, C)")
	for ii, expectServerName := range expectServerNameList 
		weightServerRoundRobin.VisitRecvServerCurrentWeight()
		recvServer := weightServerRoundRobin.GetRecvServer()
		if recvServer == nil 
			t.Error("获取不到可用的负载")
			return
		
		if recvServer.RecvAddr != expectServerName 
			t.Errorf("%v.%v.expect:%v, actual:%v", t.Name(), ii, expectServerName, recvServer.RecvAddr)
			return
		
	

以上是关于算法之权重轮询算法的主要内容,如果未能解决你的问题,请参考以下文章

Nginx之负载均衡算法

golang实现权重轮询调度算法

平滑的加权轮询均衡算法

实现一个简单的加权轮询算法

常见负载均衡算法实现详解

负载均衡算法 — 轮询