交换机链路聚合技术的负载均衡算法原理(含C语言实现)

[注:本文【原理】部分来自转载,转载来源为https://blog.csdn.net/wuheshi/article/details/79128044,此部分原创作者暂未找到,但感谢原作者的知识贡献]

【原理】

HASH表介绍:
在交换机内部,每创建一个聚合组时,底层同时创建对应该聚合组的一个hash表,该表存在于交换芯片上,hash表内容如下(简化):左列index为芯片的硬性支持,现在一般是256,512,1024,更高的未见过。Index(索引值)数量越高,负载分担越均衡。这儿以3个成员为例:

indexinterface
0eth0_0
1eth0_1
2eth0_2
3eth0_0
4eth0_1
5eth0_2
1022eth0_0
1023eth0_1

一、HASH表维护:

交换机里有专门的线程,实时检测聚合组有效成员,一旦成员状态发生变化,立即刷新hash表项。顺便谈谈刷新HASH表这个技术。工程师up/down成员口,底层就必须实时的刷新成员(这儿比较考验厂商技术),刷新速度越慢,成员状态变动时丢包越多。技术最强的如cisco,可以做到up/down成员口时,不丢包。而我公司最初会丢一秒钟的包(研发设计思路问题)。后来优化后才达到up/down成员端口,有0.0几秒的丢包,无法做到不丢包。up/down分析:当工程师在命令行up/down聚合组成员时,底层表项会有那么一丁点儿的响应时间刷新表项,这丁点儿时间,已经down掉的接口还存在hash表里,而报文是一直都有的,正好被hash到这个无效的出端口的报文都会被丢弃!)
交换机负载均衡转发原理:
虽然底层有了一张HASH表,那么到底是怎么利用这张表的呢?
1)工程师设定端口成员与HASH算法,如SIP、DIP、SIP+DIP、SIP+DIP+SP+DP等。
2)交换机根据成员生成HASH表,根据算法提取报文中相应内容。
3)使用特定HASH值的计算方法,把提取的内容计算出一个10bits的值。
4)找到底层HASH表项中该值对应的出端口。
5)把报文从这个出端口转发出去。

二、HASH值的计算方法:

xor是异或运算,即两个值不相同,则异或结果为真;反之,为假。不同为1,相同为0。

1、SIP(源IP)

1)SIP xor 0 得到一个32bit的值.
2)然后作高16bits和低16bits的xor.
3)再用16bits的15-12bits与11-8bitsxor,将得到的4bits替换到11-8bits,得到12bits右移2位得到10bits的hash值
注:10bits的值必然是0-1023里的一个数,该index对应的interface是多少,就从该接口转发出去。(相同的IP必然是相同的hash值)

2、DIP(目的IP)

同SIP

3、SIP+DIP(源IP+目的IP)

1)DIP xor SIP得到一个32bits的值。
2)然后作高16bits和低16bits的xor。
3)再用16bits的15-12bits与11-8bitsxor,将得到的4bits替换到11-8bits,得到12bits右移2位得到10bits的hash值。

4、SIP+DIP+SP+DP(源地址 + 目的地址 + 源端口 + 目的端口)

1)SIP xor DIP得到32bit的值value1;
2)hashtemp1的低16bits xor SP 得到32bit的hashtemp2;
3)hashtemp2 的低 16bit xor DP 得到 32bit 的hashtemp3;
4)然后作高16bits和低16bits的xor5)再用16bits的15~12bits和11~8bits xor,将得到的4bits替换到11~8bits,得到12bits右移2位得到10bits的hash值。

【代码实现(本程序假定只有2个物理成员接口)】

本部分是基于源IP地址进行哈希运算,从而计算出哈希值,并通过这个哈希值(上文描述的索引值)选择对应的物理成员接口转发出去。
算法模拟程序运行结果如下:
在这里插入图片描述
以下是关键代码的解释:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

【模拟程序运行结果说明与总结】

通过对模拟程序计算的结果来看,按照SIP(源IP地址)进行哈希运算,并通过最后的索引值来决定转发的物理成员接口,我们发现该大部分的IP地址都会从程序中设定的1号物理成员接口转发出去,剩下的IP地址才是从2号物理成员接口进行转发。
这样的结果就意味着,各个物理成员接口流量的分摊比例是不同的,通俗点讲,可能就会出现1号物理口承担了全网80%的流量,2号物理口只承担了全网20%的流量,源IP这样的聚合算法(负载分担模式)并不是绝对平均的。
其它的聚合算法也会有这种“不绝对平均”的问题,不过一般说来,初始组合值越长,比如“源MAC+目的MAC+源IP+目的IP”这样哈希值,经过运算之后,会达到一种更加平均的流量走向

【基于此原理须在实际生产环境考虑的问题(个人观点)】

在某些对流量负载比较严格的环境下,必须要考虑不同链路的分摊比例对网络及其业务造成的影响,并且还要考虑将链路资源浪费的情况降到最低,也要保证各个链路不要出现网络拥塞。

【聚合算法与手工负载分担、静态LACP之间的关系】

聚合算法是实现链路聚合的原理实现,是前提基础,而手工负载分担和静态LACP是链路聚合根据应用场景的不同使用的两种模式,他们的前提和基础仍旧是聚合算法,在配置上他们是不冲突的。

【算法验证】

为验证上面所有描述的正确性,我们使用HCL(华三云实验室)模拟器进行验证:(经过程序模拟,192.102.20.54与1.1.1.1不会从同一个物理接口转发出)
网络拓扑如下:
在这里插入图片描述
通过对左侧交换机的两个物理出接口抓包,其结果如下:
在这里插入图片描述
可以看出,通过HCL模拟器的运行结果和上述C程序计算结果一致。

【附:模拟程序源码(C程序)】

/*
**程序:交换机链路聚合负载均衡算法(源IP)
**作者:木下^_俱欢颜*
**备注:此程序假设前提聚合接口的物理成员接口只有两个
**IDE:Visual Studio 2017
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void Switch_EthTrunk_Hash() {
	int S_IPadd[32];               //源IP地址
	int S_IPadd_XOR[16];           //高16位与低16位异或后的中间值
	int S_IPadd_XOR_Forebit[4];    //整个算法在第三步所产生的4位中间值
	int S_IPadd_Hash_num[10];      //整个算法最后得到的10位二进制哈希值
	int S_IPadd_Hash_numTen = 0;   //声明变量:最后哈希值的十进制表示,初始值为0
	int S_IPaddTen4= 0, S_IPaddTen3= 0, S_IPaddTen2 = 0, S_IPaddTen1= 0;  //将IP地址以点分十进制所需变量
	//以下代码为输入源目IP地址
	printf("请输入32位二进制源IP地址(本端交换机IP地址):\n");
	for (int i = 0; i < 32; i++) {
		printf("本端IP地址第%d位为:", 32 - i);
		scanf("%d", &S_IPadd[i]);
	}
	printf("\n");
	//将二进制IP地址转换为点分十进制形式(变量j是次幂的指数,每次进行十进制转换前都应当初始化为7)
	int j = 7;
	for (int i = 0; i < 8; i++) {
		S_IPaddTen4 += S_IPadd[i] * pow(2, j);
		j--;
	}
	j = 7;
	for (int i = 8; i < 16; i++) {
		S_IPaddTen3 += S_IPadd[i] * pow(2, j);
		j--;
	}
	j = 7;
	for (int i = 16; i < 24; i++) {
		S_IPaddTen2 += S_IPadd[i] * pow(2, j);
		j--;
	}
	j = 7;
	for (int i = 24; i < 32; i++) {
		S_IPaddTen1 += S_IPadd[i] * pow(2, j);
		j--;
	}
	printf("您输入的二进制IP地址的点分十进制形式为:%d.%d.%d.%d\n\n",S_IPaddTen4, S_IPaddTen3, S_IPaddTen2, S_IPaddTen1);
	/*
	**对源地址进行哈希计算(包含三个步骤:)
	*/
	//1、与0异或得到32bit的值
	for (int i = 0; i < 32; i++) {
		S_IPadd[i] = S_IPadd[i] ^ 0;
	}
	printf("算法步骤1--->二进制IP地址与0异或之后的结果为:");
	for (int i = 0; i < 32; i++) {
		printf("%d",S_IPadd[i]);
	}
	printf("\n");
	//2、之后低16bit与高16bit异或
	for (int i = 0; i < 16; i++) {
		S_IPadd_XOR[i] = S_IPadd[i] ^ S_IPadd[i + 15];
	}
	printf("算法步骤2--->步骤1结果的高16位与低16位异或的结果为:");
	for (int i = 0; i < 16; i++) {
		printf("%d", S_IPadd_XOR[i]);
	}
	printf("\n");
	//3、再用16位的15-12位与11-8位异或,将得到的4bits替换到11-8bits,得到12位数右移2位得到10bits的哈希值
	for (int i = 0; i < 4; i++) {
		S_IPadd_XOR_Forebit[i] = S_IPadd_XOR[i] ^ S_IPadd_XOR[i + 4];
	}
	printf("算法步骤3.1--->步骤2结果的16位中第15-12位与11-8位异或,将得到的4位二进制数为:");
	for (int i = 0; i < 4; i++) {
		printf("%d", S_IPadd_XOR_Forebit[i]);
	}
	printf("\n");
	for (int i = 0; i < 4; i++) {
		S_IPadd_XOR[i + 4] = S_IPadd_XOR_Forebit[i];
	}
	//以下代码为12位数向右移动两位的实现
	for (int i = 4; i <= 13; i++) {
		S_IPadd_Hash_num[i - 4] = S_IPadd_XOR[i];
	}
	printf("算法步骤3.2--->步骤3.1生成的10位二进制哈希值为:");
	for (int i = 0; i < 10; i++) {
		printf("%d", S_IPadd_Hash_num[i]);
	}
	printf("\n");
	printf("算法最终计算的结果如下:\n\n");
	if (S_IPadd_Hash_num[9] == 0) {
		printf("此源IP地址相关数据包将从Eth-Trunk聚合接口的1号物理口转发出去\n\n");
	}
	else if (S_IPadd_Hash_num[9] == 1) {
		printf("此源IP地址相关数据包将从Eth-Trunk聚合接口的2号物理口转发出去\n\n");
	}
	else {
		printf("数据非法!\n\n");
	}
}
int main()
{
	Switch_EthTrunk_Hash();
	system("pause\n");
	return 0;
}
  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值