在多台机器实现负载均衡的时候,经常用到轮询调度算法(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