1. 程式人生 > >C++和go實現輪詢排程演算法(Round-Robin Scheduling)

C++和go實現輪詢排程演算法(Round-Robin Scheduling)

在多臺機器實現負載均衡的時候,經常用到輪詢排程演算法(Round-Robin Scheduling)。

輪詢排程演算法就是:以迴圈的方式,依次將請求排程到不同的伺服器,即每次排程執行i = (i + 1) mod n,並選出第i臺伺服器。

演算法的優點是其簡潔性,它無需記錄當前所有連線的狀態,所以它是一種無狀態排程。

1、演算法流程:
假設有一組伺服器 S = {S0, S1, …, Sn-1} ,有相應的權重,變數i表示上次選擇的伺服器,權值curWeight初始化為0,index初始化為-1 ,當第一次的時候取權值取最大的那個伺服器,通過權重的不斷遞減,尋找適合的伺服器返回,直到輪詢結束,權值返回為0。

2. C++版程式碼實現

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include<algorithm>
#include<map>

using namespace std;

#define BUFFER_SIZE 1024

struct srv_info {
    srv_info() {
        ip  = new char[BUFFER_SIZE];
        weight = 0;
    }

    char* ip;
    int weight;
};

static vector<srv_info> server;		//伺服器資訊

int getGcd(int a, int b);		    //獲得兩個數的最大公約數
int getMaxGcd();                    //獲得所有數的最大公約數
int getMaxWeight();				//獲得所有伺服器中的最大權值
int getSelectServer(srv_info* serverInfo, int serverNum, int maxGcd, int maxWeight, int *curWeight,int *serverIndex); //輪詢排程

int main(int argc, char **argv) {
    ////填充伺服器IP和權重
    server.clear();
    char tmp[BUFFER_SIZE];
    struct srv_info sinfo;

    const char* ip_x = "192.168.0.10";
    memset(tmp, '\0', BUFFER_SIZE);
    sprintf(tmp, "%s%d", ip_x, 1);
    memcpy(sinfo.ip, tmp, BUFFER_SIZE);
    sinfo.weight = 1;
    server.push_back(sinfo);

    memset(tmp, '\0', BUFFER_SIZE);
    sprintf(tmp, "%s%d", ip_x, 2);
    memcpy(sinfo.ip, tmp, BUFFER_SIZE);
    sinfo.weight = 2;
    server.push_back(sinfo);

    memset(tmp, '\0', BUFFER_SIZE);
    sprintf(tmp, "%s%d", ip_x, 3);
    memcpy(sinfo.ip, tmp, BUFFER_SIZE);
    sinfo.weight = 3;
    server.push_back(sinfo);

    memset(tmp, '\0', BUFFER_SIZE);
    sprintf(tmp, "%s%d", ip_x, 4);
    memcpy(sinfo.ip, tmp, BUFFER_SIZE);
    sinfo.weight = 4;
    server.push_back(sinfo);

    ////輸出伺服器資訊
    printf("server count: %ld\n", server.size());
    for (size_t i = 0; i < server.size(); i++) {
        printf("%s	weight: %d\n", server[i].ip, server[i].weight);
    }

    printf("====================================\n");

    int serverNum = int(server.size());	//伺服器個數
    int maxGcd = getMaxGcd(); //最大公約數
    int maxWeight = getMaxWeight(); //最大權重值

    srv_info serverInfo;
    int retIndex = -1;
    int count = 0;
    map<int,int> mapIndex2Count;
    map<int, int>::iterator iter;
    int curWeight = 0;			//當前排程的權值
    int serverIndex = -1;		//上一次選擇的伺服器

    for (int i = 0; i < 100; i++) { //排程100次
        retIndex = getSelectServer(&serverInfo, serverNum, maxGcd, maxWeight, &curWeight, &serverIndex);
        if (retIndex == -1) {
            continue;
        }

        printf("Ip: %s, Weight: %d, Index: %d\n", serverInfo.ip, serverInfo.weight, serverIndex);

        iter = mapIndex2Count.find(retIndex);
        if (iter != mapIndex2Count.end()) {
            count = mapIndex2Count[retIndex];
            mapIndex2Count[retIndex] = ++count;
        } else {
            mapIndex2Count[retIndex] = 1;
        }
    }

    printf("====================================\n");

    for (size_t i = 0; i < server.size(); i++) {
        printf("ip:%s, weight:%d, called %d times\n", server[i].ip, server[i].weight, mapIndex2Count[i]);
    }

    return 0;
}

int getGcd(int a, int b) {
    int c = 0;
    while(b>0) {
        c = b;
        b = a%b;
        a = c;
    }
    return a;
}

//獲取所有權重的最大公約數
int getMaxGcd() {
    int res = server[0].weight;
    int curMax=0, curMin=0;
    for (size_t i = 0; i < server.size(); i++)
    {
        curMax = int(max(res, server[i].weight)); //比較上次計算結果和本次權重值,取兩個數中的較大者
        curMin = int(min(float(res), float(server[i].weight))); //比較上次計算結果和本次權重值,取兩個數中的較小者
        res = getGcd(curMax, curMin); //本次計算結果,獲取兩個數的最大公約數
    }

    return res;
}

int getMaxWeight() {
    int max = 0;
    for (size_t i = 0; i < server.size(); i++) {
        if (server[i].weight > max) {
            max = server[i].weight;
        }
    }

    return max;
}

/**
 * 演算法流程:
 * 假設有一組伺服器 S = {S0, S1, …, Sn-1} ,有相應的權重,變數serverIndex表示上次選擇的伺服器,
 * 權值curWeight初始化為0,serverIndex初始化為-1 ,當第一次的時候,取權重值最大的那個伺服器.
 * 通過權重值的不斷遞減,尋找適合的伺服器返回,直到輪詢結束,權值返回為0.
 */
int getSelectServer(srv_info* serverInfo, int serverNum, int maxGcd, int maxWeight, int *curWeight,int *serverIndex) {
    while (true) {
        *serverIndex = (*serverIndex + 1) % serverNum;
        if (*serverIndex == 0) {
            *curWeight = *curWeight - maxGcd;
            if (*curWeight <= 0) {
                *curWeight = maxWeight;
                if (*curWeight == 0) {
                    return -1;
                }
            }
        }

        if (server[*serverIndex].weight >= *curWeight) {
            serverInfo->weight = server[*serverIndex].weight;
            memcpy(serverInfo->ip, server[*serverIndex].ip, BUFFER_SIZE);
            return *serverIndex;
        }
    }
}

執行結果如下:

server count: 4
192.168.0.104	weight: 1
192.168.0.104	weight: 2
192.168.0.104	weight: 3
192.168.0.104	weight: 4
====================================
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 1, Index: 0
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 1, Index: 0
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 1, Index: 0
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 1, Index: 0
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 1, Index: 0
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 1, Index: 0
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 1, Index: 0
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 1, Index: 0
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 1, Index: 0
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
Ip: 192.168.0.104, Weight: 1, Index: 0
Ip: 192.168.0.104, Weight: 2, Index: 1
Ip: 192.168.0.104, Weight: 3, Index: 2
Ip: 192.168.0.104, Weight: 4, Index: 3
====================================
ip:192.168.0.104, weight:1, called 10 times
ip:192.168.0.104, weight:2, called 20 times
ip:192.168.0.104, weight:3, called 30 times
ip:192.168.0.104, weight:4, called 40 times

 

3. go語言實現版本

package main

import (
	"math"
	"fmt"
)

type Servers struct {
	service string
	weight int
	provider string
}

//獲取權重總和
func getSumWeight(serverList []Servers) int {
	r := 0
	for _, server := range serverList {
		r += server.weight
	}
	return r
}

//獲取兩個數的最大公約數
func getGcd(a int, b int) int {
	c := 0
	for b>0 {
		c = b
		b = a%b
		a = c
	}
	return a
}

//獲取所有權重的最大公約數
func getMaxGcd(serverList []Servers) int {
	resGcd := serverList[0].weight
	for i:=1; i<len(serverList); i++ {
		max := int(math.Max(float64(resGcd), float64(serverList[i].weight))) //比較上次計算結果和本次權重值,取兩個數中的較大者
		min := int(math.Min(float64(resGcd), float64(serverList[i].weight))) //比較上次計算結果和本次權重值,取兩個數中的較小者
		resGcd = getGcd(max, min) //本次計算結果,獲取兩個數的最大公約數
	}
	return resGcd
}

//獲取所有權重的最大值
func getMaxWeight(s []Servers) int {
	m := 0
	for _, server := range s {
		if server.weight > m {
			m = server.weight
		}
	}
	return m
}

func getSelectServer(s []Servers, gcd int, maxWeight int, index *int, cw *int) int {
	for {
		*index = (*index+1)%len(s)
		if *index == 0 {
			*cw = *cw - gcd
			if *cw <= 0 {
				*cw = maxWeight
				if *cw == 0 {
					return -1
				}
			}
		}

		if s[*index].weight >= *cw {
			return *index
		}
	}
}

//按照權重進行分配
func wrr(serverList []Servers) {
	maxGcd := getMaxGcd(serverList) //獲取所有權重的最大公約數
	maxWeight := getMaxWeight(serverList) //獲取所有權重的最大值
	fmt.Println("所有權重的最大公約數: ", maxGcd)
	fmt.Println("獲取所有權重的最大值: ", maxWeight)

	sumWeight := getSumWeight(serverList) //獲取所有權重的總和
	fmt.Println("獲取所有權重的總和: ", sumWeight)

	index := -1 //索引值
	curWeight := 0 //當前權重
	mapProvider2Times := make(map[string]int) //統計結果

	for i:=0; i<sumWeight; i++ {
		retIndex := getSelectServer(serverList, maxGcd, maxWeight, &index, &curWeight)
		if retIndex == -1 {
			continue;
		}
		fmt.Printf("節點名稱:%s,節點權重:%d, 索引值:%d, 當前權重計算結果%d \r\n", serverList[index].provider, serverList[index].weight, index, curWeight)
		mapProvider2Times[serverList[index].provider] += 1
	}
	fmt.Println("統計結果")
	for key, value := range mapProvider2Times {
		fmt.Printf("名稱: %s, 次數: %d\n", key, value)
	}
}

func main()  {
	serverList := []Servers{
		{
			service: "cd",
			weight:6,
			provider:"test1",
		},
		{
			service: "book",
			weight:2,
			provider: "test2",
		},
		{
			service: "pen",
			weight:4,
			provider: "test3",
		},
	}
	wrr(serverList)
}

執行結果如下:

所有權重的最大公約數:  2
獲取所有權重的最大值:  6
獲取所有權重的總和:  12
節點名稱:test1,節點權重:6, 索引值:0, 當前權重計算結果6
節點名稱:test1,節點權重:6, 索引值:0, 當前權重計算結果4
節點名稱:test3,節點權重:4, 索引值:2, 當前權重計算結果4
節點名稱:test1,節點權重:6, 索引值:0, 當前權重計算結果2
節點名稱:test2,節點權重:2, 索引值:1, 當前權重計算結果2
節點名稱:test3,節點權重:4, 索引值:2, 當前權重計算結果2
節點名稱:test1,節點權重:6, 索引值:0, 當前權重計算結果6
節點名稱:test1,節點權重:6, 索引值:0, 當前權重計算結果4
節點名稱:test3,節點權重:4, 索引值:2, 當前權重計算結果4
節點名稱:test1,節點權重:6, 索引值:0, 當前權重計算結果2
節點名稱:test2,節點權重:2, 索引值:1, 當前權重計算結果2
節點名稱:test3,節點權重:4, 索引值:2, 當前權重計算結果2
統計結果
名稱: test1, 次數: 6
名稱: test3, 次數: 4
名稱: test2, 次數: 2