当前位置: 代码网 > it编程>编程语言>Java > Dubbo-负载均衡原理解析,卑微打工人

Dubbo-负载均衡原理解析,卑微打工人

2024年07月31日 Java 我要评论
如果你选择了IT行业并坚定的走下去,这个方向肯定是没有一丝问题的,这是个高薪行业,但是高薪是凭自己的努力学习获取来的,这次我把P8大佬用过的一些学习笔记(pdf)都整理在本文中了《Java中高级核心知识全面解析》小米商场项目实战,别再担心面试没有实战项目:自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。

特点

  • 加权随机,按权重设置随机概率。

  • 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

  • 缺点:存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

代码实现

public class randomloadbalance {

public static string getserver(){

// 生成一个随机数作为list的下标值

java.util.random random = new java.util.random();

int randompos = random.nextint(serverips.list.size());

return serverips.list.get(randompos);

}

public static void main(string[] args) {

//连续调用10次

for (int i = 0; i < 10; i++) {

system.out.println(getserver());

}

}

}

执行结果如下

192.168.0.3

192.168.0.3

192.168.0.5

192.168.0.3

192.168.0.1

192.168.0.7

192.168.0.5

192.168.0.6

192.168.0.3

192.168.0.8

当调用次数比较少时,random产生的随机数可能会比较集中,此时多数请求会落到同一台服务器上,只有在经过多次请求后,才能使调用请求进行“均为”分配。调用量少这一点并没有什么关系,负载均衡机制正式为了应对请求量多的情况,所以随机算法是用的比较多的一种算法。

//在serverips类中增加服务器权重对应关系map,权重之和为50:

public static final map<string, integer> weight_list = new hashmap<string, integer>();

static {

// 权重之和为50

weight_list.put(“192.168.0.1”, 1);

weight_list.put(“192.168.0.2”, 8);

weight_list.put(“192.168.0.3”, 3);

weight_list.put(“192.168.0.4”, 6);

weight_list.put(“192.168.0.5”, 5);

weight_list.put(“192.168.0.6”, 5);

weight_list.put(“192.168.0.7”, 4);

weight_list.put(“192.168.0.8”, 7);

weight_list.put(“192.168.0.9”, 2);

weight_list.put(“192.168.0.10”, 9);

}

那么现在的随机算法变成了权重随机算法,当请求量比较多的时候,服务器使用的分布应该近似对应权重的分布。

权重随机算法


方式一:权重数复制实现

把每个服务器按它所对应的服务器权重数进行复制

public class weightrandom {

public static string getserver() {

//生成一个随机数作为list的下标值

list ips = new arraylist();

for (string ip : serverips.weight_list.keyset()) {

integer weight = serverips.weight_list.get(ip);

//按权重进行服务器数量的复制

for (int i = 0; i < weight; i++) {

ips.add(ip);

}

}

random random = new random();

int randompos = random.nextint(ips.size());

return ips.get(randompos);

}

public static void main(string[] args) {

//连续调用10次

for (int i = 0; i < 10; i++) {

system.out.println(getserver());

}

}

}

执行结果如下

192.168.0.6

192.168.0.10

192.168.0.10

192.168.0.6

192.168.0.4

192.168.0.2

192.168.0.6

192.168.0.2

192.168.0.4

192.168.0.6

这种实现方法在遇到权重之和特别大的时候就会比较消耗内存,因为需要对ip地址进行复制,权重之和越大,那么上文中的ips就需要越多的内存。

方式二:区间范围实现

假设我们有一组服务器servers=[a,b,c],他们对应的权重为weights=[5,3,2],权重总和为10。现在把这些权重值平铺在一维坐标值上,[0, 5) 区间属于服务器 a,[5, 8) 区间属于服务器 b,[8, 10) 区间属于服务器 c。接下来通过随机数生成器生成一个范围 [0, 10) 之间的随机数,然后计算在这个随机数会落在哪个区间上。比如数字3会落在服务器a对应的区间上,此时返回服务器a即可。权重越大的机器,在坐标轴上对应的区间范围就越大,因此随机数生成器生成的数字就会有更大的概率落在此区间内。只要随机数生成器产生的随机数分布性很好,在经过多次选择后,每个服务器被选中的次数比例接近其权重比例。

比如,经过一万次选择后,服务器a被选中的次数大约为5000次,服务器b被选中的次数约为3000次,服务器c被选中的次数约为2000次。

假设现在随机数offset=7;

  1. offset<5 is false,所以不在[0, 5)区间,将offset = offset - 5(offset=2)

  2. offset<3 is true,所以处于[5, 8)区间,所以应该选用b服务器

public class weightrandomv2 {

public static string getserver() {

int totalweight = 0;

boolean sameweight = true; //如果所有权重都相等,随机一个ip即可

object[] weights = serverips.weight_list.values().toarray();

for (int i = 0; i < weights.length; i++) {

integer weight = (integer) weights[i];

totalweight += weight;

//判断权重是否都相等

if (sameweight && i > 0 && !weight.equals(weights[i - 1])) {

sameweight = false;

}

}

random random = new random();

int randompos = random.nextint(totalweight);

//权重值不相等

if (!sameweight) {

for (string ip : serverips.weight_list.keyset()) {

integer value = serverips.weight_list.get(ip);

if (randompos < value) {

return ip;

}

randompos = randompos - value;

}

}

return (string) serverips.weight_list.keyset().toarray()[new random().nextint(serverips.weight_list.size())];

}

public static void main(string[] args) {

//连续调用10次

for (int i = 0; i < 10; i++) {

system.out.println(getserver());

}

}

}

轮询算法-roundrobinloadbalance


特点

  • 加权轮询,按公约后的权重设置轮询比率,循环调用节点

  • 缺点:同样存在慢的提供者累积请求的问题。

代码实现(简单的轮询算法)

public class roundrobin {

private static integer pos = 0;

public static string getserver() {

string ip = “”;

//pos同步

synchronized (pos) {

if (pos >= serverips.list.size()) {

pos = 0;

}

ip = serverips.list.get(pos);

pos++;

}

return ip;

}

public static void main(string[] args) {

//连续调用10次

for (int i = 0; i < 11; i++) {

system.out.println(getserver());

}

}

}

执行结果如下:

192.168.0.1

192.168.0.2

192.168.0.3

192.168.0.4

192.168.0.5

192.168.0.6

192.168.0.7

192.168.0.8

192.168.0.9

192.168.0.10

192.168.0.1

这种算法很简单,也很公平,每台服务轮流来进行服务,但是有的机器性能好,所以能者多劳,和随机算法一样,加上权重维度以后,其中一种实现方法就是复制法,复制法的缺点是,比较消耗内存。

介绍另外一种算法:这种算法引入一个概念:调用编号,比如第1次调用为1,第2次调用为2,第100次调用为100,调用编号是递增的,所以我们可以根据这个调用编号推算出服务器。

假设我们有三台服务器servers = [a, b, c],对应的权重为 weights = [ 2, 5, 1], 总权重为8,我们可以理解为有8台“服务器”,这是8台“不具有并发功能”,其中有2台为a,5台为b,1台为c,一次调用过来的时候,需要按顺序访问,比如有10次调用,那么服务器调用顺序为aabbbbbcaa,调用编号会越来越大,而服务器是固定的,所以需要把调用编号“缩小”,这里对调用编号进行取余,除数为总权重和

比如:

  1. 1号调用,1%8=1;

  2. 2号调用,2%8=2;

  3. 3号调用,3%8=3;

  4. 8号调用,8%8=0;

  5. 9号调用,9%8=1;

  6. 100号调用,100%8=4;

我们发现调用编号可以被缩小为0-7之间的8个数字,问题是怎么根据这个8个数字找到对应的服务器呢?和我们随机算法类似,这里也可以把权重想象为一个坐标轴“0-----2-----7-----8”

  1. 1号调用,1%8=1,offset = 1, offset <= 2 is true,取a;

  2. 2号调用,2%8=2;offset = 2,offset <= 2 is true, 取a;

  3. 3号调用,3%8=3;offset = 3, offset <= 2 is false, offset = offset - 2, offset = 1, offset <= 5,取b

  4. 8号调用,8%8=0;offset = 0, 特殊情况,offset = 8,offset <= 2 is false, offset = offset - 2, offset = 6, offset <= 5 is false, offset = offset - 5, offset = 1, offset <= 1 is true, 取c;

  5. 9号调用,9%8=1;// …

  6. 100号调用,100%8=4; //…

模拟调用编号获取工具:

public class sequence {

public static integer num=0;

public static integer getandincrement(){

return ++num;

}

}

public class weightroundrobin {

private static integer pos = 0;

public static string getserver() {

int totalweight = 0;

boolean sameweight = true;

object[] weights = serverips.weight_list.values().toarray();

for (int i = 0; i < weights.length; i++) {

integer weight = (integer) weights[i];

totalweight += weight;

if (sameweight && i > 0 && !weight.equals(weights[i - 1])) {

sameweight = false;

}

}

integer sequencenum = sequence.getandincrement();

integer offset = sequencenum % totalweight;

offset = offset == 0 ? totalweight : offset;

if (!sameweight) {

for (string ip : serverips.weight_list.keyset()) {

integer weight = serverips.weight_list.get(ip);

if (offset <= weight) {

return ip;

}

offset = offset - weight;

}

}

string ip = “”;

synchronized (pos) {

if (pos >= serverips.list.size()) {

pos = 0;

}

ip = serverips.list.get(pos);

pos++;

}

return ip;

}

public static void main(string[] args) {

// 连续调用11次

for (int i = 0; i < 11; i++) {

system.out.println(getserver());

}

}

}

执行结果如下

192.168.0.2

192.168.0.2

192.168.0.2

192.168.0.2

192.168.0.2

192.168.0.2

192.168.0.2

192.168.0.2

192.168.0.1

192.168.0.4

192.168.0.4

但是这种算法有一个缺点:一台服务器的权重特别大的时候,他需要连续的的处理请求,但是实际上我们想达到的效果是,对于100次请求,只要有100*8/50=16次就够了,这16次不一定要连续的访问,比如假设我们有三台服务器 servers = [a, b, c],对应的权重为 weights = [5, 1, 1] , 总权重为7,那么上述这个算法的结果是:aaaaabc,那么如果能够是这么一个结果呢:aabacaa,把b和c平均插入到5个a中间,这样是比较均衡的了。

我们这里可以改成平滑加权轮询。

平滑加权轮询


特点

思路:每个服务器对应两个权重,分别为weight 和currentweight.其中weight是固定的,currentweight会动态调整,初始值为0.当有新的请求进来时,遍历服务器列表,让它的currentweight加上自身权重。遍历完成后,找到最大的 currentweight,并将其减去权重总和,然后返回相应的服务器即可。

假设我们有三台服务器 servers = [a, b, c],对应的权重为 weights = [5, 1, 1] , 总权重为7

| 请求编号 | currentweight 数组 (current_weight += weight) | 选择结果(max(currentweight)) | 减去权重总和后的currentweight 数组(max(currentweight) -= sum(weight)) |

| — | — | — | — |

| 1 | [5, 1, 1] | a | [-2, 1, 1] |

| 2 | [3, 2, 2] | a | [-4, 2, 2] |

| 3 | [1, 3, 3] | b | [1, -4, 3] |

| 4 | [6, -3, 4] | a | [-1, -3, 4] |

| 5 | [4, -2, 5] | c | [4, -2, -2] |

| 6 | [9, -1, -1] | a | [2, -1, -1] |

| 7 | [7, 0, 0] | a | [0, 0, 0] |

如上,经过平滑性处理后,得到的服务器序列为 [a, a, b, a, c, a, a],相比之前的序列 [a, a, a, a, a, b, c],分布性要好一些。初始情况下 currentweight = [0, 0, 0],第7个请求处理完后,currentweight 再次变为 [0, 0, 0]。

代码实现

// 增加一个weight类,用来保存ip, weight(固定不变的原始权重), currentweight(当前会变化的权重)

public class weight {

private string ip;

private integer weight;

private integer currentweight;

public weight(string ip, integer weight, integer currentweight) {

this.ip = ip;

this.weight = weight;

this.currentweight = currentweight;

}

public string getip() {

return ip;

}

public void setip(string ip) {

this.ip = ip;

}

public integer getweight() {

return weight;

}

public void setweight(integer weight) {

this.weight = weight;

}

public integer getcurrentweight() {

return currentweight;

}

public void setcurrentweight(integer currentweight) {

this.currentweight = currentweight;

}

}

public class weightroundrobinv2 {

private static map<string, weight> weightmap = new hashmap<string, weight>();

public static string getserver() {

// 获取权重之和

int totalweight = serverips.weight_list1.values().stream().reduce(0, (w1, w2) -> w1 + w2);

//初始化weightmap,初始时将currentweight赋值为weight

if (weightmap.isempty()) {

serverips.weight_list1.foreach((key, value) -> {

weightmap.put(key, new weight(key, value, value));

});

}

//找出currentweight最大值

weight maxcurrentweight = null;

for (weight weight : weightmap.values()) {

if (maxcurrentweight == null || weight.getcurrentweight() > maxcurrentweight.getcurrentweight()) {

maxcurrentweight = weight;

}

}

//将maxcurrentweight减去总权重和

maxcurrentweight.setcurrentweight(maxcurrentweight.getcurrentweight() - totalweight);

//所有的ip的currentweight统一加上原始权重

for (weight weight : weightmap.values()) {

weight.setcurrentweight(weight.getcurrentweight() + weight.getweight());

}

//返回maxcurrentweight所对应的ip

return maxcurrentweight.getip();

}

public static void main(string[] args) {

// 连续调用10次

for (int i = 0; i < 10; i++) {

system.out.println(getserver());

}

}

}

serverips里添加数据weight_list1:

public static final map<string, integer> weight_list1 = new hashmap<string, integer>();

static {

// 权重之和为50

weight_list1.put(“a”, 5);

weight_list1.put(“b”, 1);

weight_list1.put(“c”, 1);

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、oppo等大厂,18年进入阿里一直到现在。

深知大多数java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加v获取:vip1024b (备注java)
img

总结

如果你选择了it行业并坚定的走下去,这个方向肯定是没有一丝问题的,这是个高薪行业,但是高薪是凭自己的努力学习获取来的,这次我把p8大佬用过的一些学习笔记(pdf)都整理在本文中了

《java中高级核心知识全面解析》

小米商场项目实战,别再担心面试没有实战项目:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、oppo等大厂,18年进入阿里一直到现在。

深知大多数java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-dimvddet-1711932914974)]
[外链图片转存中…(img-zxq8idpg-1711932914976)]
[外链图片转存中…(img-qzcing3h-1711932914977)]
[外链图片转存中…(img-tvprgk3k-1711932914978)]
[外链图片转存中…(img-puscemvs-1711932914979)]
[外链图片转存中…(img-zmx9iy0j-1711932914980)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加v获取:vip1024b (备注java)
[外链图片转存中…(img-cciimqlc-1711932914982)]

总结

如果你选择了it行业并坚定的走下去,这个方向肯定是没有一丝问题的,这是个高薪行业,但是高薪是凭自己的努力学习获取来的,这次我把p8大佬用过的一些学习笔记(pdf)都整理在本文中了

《java中高级核心知识全面解析》

[外链图片转存中…(img-1rah1iky-1711932914983)]

小米商场项目实战,别再担心面试没有实战项目:

[外链图片转存中…(img-thpdytab-1711932914983)]

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com