单机最大负载_分布式高可靠之负载均衡,今天看了你肯定会

到目前为止,我已经为你介绍了分布式起源、分布式协调与同步、分布式资源管理与负载调度、分布式计算技术、分布式通信技术和分布式数据存储。

可以说,掌握了这些内容,基本上就掌握了分布式的关键技术。然而,只有可靠的分布式系统才能真正应用起来。那么,分布式系统的可靠性又是如何实现的呢?

不要着急,接下来几篇文章,我会和你一起学习分布式可靠性相关的知识,包括负载均衡、流量控制、故障隔离和故障恢复。

在这其中,负载均衡是分布式可靠性中非常关键的一个问题或技术,在一定程度上反映了分布式系统对业务处理的能力。比如,早期的电商抢购活动,当流量过大时,你可能就会发现有些地区可以购买,而有些地区因为服务崩溃而不能抢购。这,其实就是系统的负载均衡出现了问题。

接下来,我们就一起来打卡分布式高可靠之负载均衡。

什么是负载均衡?

先举个例子吧。以超市收银为例,假设现在只有一个窗口、一个收银员:

  • 一般情况下,收银员平均 2 分钟服务一位顾客,10 分钟可以服务 5 位顾客;
  • 到周末高峰期时,收银员加快收银,平均 1 分钟服务一位顾客,10 分钟最多服务 10 位顾客,也就是说一个顾客最多等待 10 分钟;
  • 逢年过节,顾客数量激增,一下增加到 30 位顾客,如果仍然只有一个窗口和一个收银员,那么所有顾客就只能排队等候了,一个顾客最多需要等待 30 分钟。这样购物体验,就非常差了。

那有没有解决办法呢?

当然有。那就是新开一个收银窗口,每个收银窗口服务 15 个顾客,这样最长等待时间从 30 分钟缩短到 15 分钟。但如果,这两个窗口的排队顾客数严重不均衡,比如一个窗口有 5 个顾客排队,另一个窗口却有 25 个顾客排队,就不能最大化地提升顾客的购物体验。

所以,尽可能使得每个收银窗口排队的顾客一样多,才能最大程度地减少顾客的最长排队时间,提高用户体验。

看完这个例子,你是不是想到了一句话“不患寡,而患不均”?这,其实就是负载均衡的基本原理。

通常情况下,负载均衡可以分为两种

  • 一种是请求负载均衡,即将用户的请求均衡地分发到不同的服务器进行处理;
  • 另一种是数据负载均衡,即将用户更新的数据分发到不同的存储服务器。

我在(数据分布方式之哈希与一致性哈希,我就是个神算子)文章分享数据分布方法时,提到:数据分布算法很重要的一个衡量标准,就是均匀分布。可见,哈希和一致性哈希等,其实就是数据负载均衡的常用方法。那么今天,我就与你着重说说服务请求的负载均衡技术吧。

分布式系统中,服务请求的负载均衡是指,当处理大量用户请求时,请求应尽量均衡地分配到多台服务器进行处理,每台服务器处理其中一部分而不是所有的用户请求,以完成高并发的请求处理,避免因单机处理能力的上限,导致系统崩溃而无法提供服务的问题。

比如,有 N 个请求、M 个节点,负载均衡就是将 N 个请求,均衡地转发到这 M 个节点进行处理。

服务请求的负载均衡方法

通常情况下,计算机领域中,在不同层有不同的负载均衡方法。比如,从网络层的角度,通常有基于 DNS、IP 报文等的负载均衡方法;在中间件层(也就是我们专栏主要讲的分布式系统层),常见的负载均衡策略主要包括轮询策略、随机策略、哈希和一致性哈希等策略。

今天,我着重与你分析的就是,中间件层所涉及的负载均衡策略。接下来,我们就具体看看吧。

轮询策略

轮询策略是一种实现简单,却很常用的负载均衡策略,核心思想是服务器轮流处理用户请求,以尽可能使每个服务器处理的请求数相同。生活中也有很多类似的场景,比如,学校宿舍里,学生每周轮流打扫卫生,就是一个典型的轮询策略。

在负载均衡领域中,轮询策略主要包括顺序轮询和加权轮询两种方式

首先,我们一起看看顺序轮询。假设有 6 个请求,编号为请求 1~6,有 3 台服务器可以处理请求,编号为服务器 1~3,如果采用顺序轮询策略,则会按照服务器 1、2、3 的顺序轮流进行请求。

如表所示,将 6 个请求当成 6 个步骤:

  1. 请求 1 由服务器 1 处理;
  2. 请求 2 由服务器 2 进行处理。
  3. 以此类推,直到处理完这 6 个请求。

e5101ac6d6715f4c79232c4b26353051.png

最终的处理结果是,服务器 1 处理请求 1 和请求 4,服务器 2 处理请求 2 和请求 5,服务器 3 处理请求 3 和请求 6。

接下来,我们看一下加权轮询

加权轮询为每个服务器设置了优先级,每次请求过来时会挑选优先级最高的服务器进行处理。比如服务器 1~3 分配了优先级{4,1,1},这 6 个请求到来时,还当成 6 个步骤,如表所示。

  1. 请求 1 由优先级最高的服务器 1 处理,服务器 1 的优先级相应减 1,此时各服务器优先级为{3,1,1};
  2. 请求 2 由目前优先级最高的服务器 1 进行处理,服务器 1 优先级相应减 1,此时各服务器优先级为{2,1,1}。
  3. 以此类推,直到处理完这 6 个请求。每个请求处理完后,相应服务器的优先级会减 1

8a2c7b43031c35cda55b17ac660cc405.png

最终的处理结果是,服务器 1 处理请求 1~4,服务器 2 处理请求 5,服务器 3 会处理请求 6。

以上就是顺序轮询和加权轮询的核心原理了。轮询策略的应用比较广泛,比如 Nginx 默认的负载均衡策略就是一种改进的加权轮询策略。我们具体看看它的核心原理吧。

首先,我来解释下 Nginx 轮询策略需要用到的变量吧。

  • weight:配置文件中为每个服务节点设置的服务节点权重,固定不变。
  • effective_weight:服务节点的有效权重,初始值为 weight。在 Nginx 的源码中有一个最大失败数的变量 max_fails,当服务发生异常时,则减少相应服务节点的有效权重,公式为 effective_weight = effective_weight - weight / max_fails;之后再次选取本节点,若服务调用成功,则增加有效权重,effective_weight ++ ,直至恢复到 weight。
  • current_weight:服务节点当前权重,初始值均为 0,之后会根据系统运行情况动态变化。

假设,各服务器的优先级是{4,1,1},我还是将 6 个请求分为 6 步来进行讲解,如表所示:

  1. 遍历集群中所有服务节点,使用 current_weight = current_weight + effective_weight,计算此时每个服务节点的 current_weight,得到 current_weight 为{4,1,1},total 为 4+1+1=6。选出 current_weight 值最大的服务节点即服务器 1 来处理请求,随后服务器 1 对应的 current_weight 减去此时的 total 值,即 4 - 6,变为了 -2 。
  2. 按照上述步骤执行,首先遍历,按照 current_weight = current_weight + effective_weight 计算每个服务节点 current_weight 的值,结果为{2,2,2},total 为 6,选出 current_weight 值最大的服务节点。current_weight 最大值有多个服务节点时,直接选择第一个节点即可,在这里选择服务器 1 来处理请求,随后服务器 1 对应的 current_weight 值减去此时的 total,即 2 - 6,结果为 -4。
  3. 以此类推,直到处理完这 6 个请求。

38da85d4627b5d7eda202ac8c46df74b.png

最终的处理结果为,服务器 1 处理请求 1、2、4、6,服务器 2 处理请求 3,服务器 3 会处理请求 5。

可以看到,与普通的加权轮询策略相比,这种轮询策略的优势在于,当部分请求到来时,不会集中落在优先级较高的那个服务节点

还是上面的例子,假设只有 4 个请求,按照普通的加权轮询策略,会全部由服务器 1 进行处理,即{1,1,1,1};而按照这种平滑的加权轮询策略的话,会由服务器 1 和 2 共同进行处理,即{1,1,2,1}。

轮询策略的优点就是,实现简单,且对于请求所需开销差不多时,负载均衡效果比较明显,同时加权轮询策略还考虑了服务器节点的异构性,即可以让性能更好的服务器具有更高的优先级,从而可以处理更多的请求,使得分布更加均衡。

轮询策略的缺点是,每次请求到达的目的节点不确定,不适用于有状态请求的场景。并且,轮询策略主要强调请求数的均衡性,所以不适用于处理请求所需开销不同的场景。

但轮询策略的缺点是,每次请求到达的目的节点不确定,不适用于有状态请求的场景。并且,轮询策略主要强调请求数的均衡性,所以不适用于处理请求所需开销不同的场景。比如,有两个服务器(节点 A 和节点 B)性能相同,CPU 个数和内存均相等,有 4 个请求需要处理,其中请求 1 和请求 3 需要 1 个 CPU,请求 2 和请求 4 需要 2 个 CPU。根据轮询策略,请求 1 和请求 3 由节点 A、请求 2 和请求 4 由节点 B 处理。由此可见,节点 A 和节点 B 关于 CPU 的负载分别是 2 和 4,从这个角度来看,两个节点的负载并不均衡。

综上所述,轮询策略适用于用户请求所需资源比较接近的场景。

随机策略

随机策略也比较容易理解,指的就是当用户请求到来时,会随机发到某个服务节点进行处理,可以采用随机函数实现。这里,随机函数的作用就是,让请求尽可能分散到不同节点,防止所有请求放到同一节点或少量几个节点上。

如图所示,假设有 5 台服务器 Server 1~5 可以处理用户请求,每次请求到来时,都会先调用一个随机函数来计算出处理节点。这里,随机函数的结果只能是{1,2,3,4,5}这五个值,然后再根据计算结果分发到相应的服务器进行处理。比如,图中随机函数计算结果为 2,因此该请求会由 Server2 处理。

74b69f59f572605c27a4cd0e9ddd8157.png

这种方式的优点是,实现简单,但缺点也很明显,与轮询策略一样,每次请求到达的目的节点不确定,不适用于有状态的场景,而且没有考虑到处理请求所需开销。除此之外,随机策略也没有考虑服务器节点的异构性,即性能差距较大的服务器可能处理的请求差不多。

因此,随机策略适用于,集群中服务器节点处理能力相差不大,用户请求所需资源比较接近的场景

比如,我在前面文章中提到的 RPC 框架 Dubbo,当注册中心将服务提供方地址列表返回给调用方时,调用方会通过负载均衡算法选择其中一个服务提供方进行远程调用。关于负载均衡算法,Dubbo 提供了随机策略、轮询策略等。

哈希和一致性哈希策略

无论是轮询还是随机策略,对于一个客户端的多次请求,每次落到的服务器很大可能是不同的,如果这是一台缓存服务器,就会对缓存同步带来很大挑战。尤其是系统繁忙时,主从延迟带来的同步缓慢,可能会造成同一客户端两次访问得到不同的结果。解决方案就是,利用哈希算法定位到对应的服务器

哈希和一致性哈希,是数据负载均衡的常用算法。我在(数据分布方式之哈希与一致性哈希,我就是个神算子)文章介绍哈希与一致性哈希时,提到过:数据分布算法的均匀性,一方面指数据的存储均匀,另一方面也指数据请求的均匀。

数据请求就是用户请求的一种,哈希、一致性哈希、带有限负载的一致性哈希和带虚拟节点的一致性哈希算法,同样适用于请求负载均衡。

所以,哈希与一致性策略的优点是,哈希函数设置合理的话,负载会比较均衡。而且,相同 key 的请求会落在同一个服务节点上,可以用于有状态请求的场景。除此之外,带虚拟节点的一致性哈希策略还可以解决服务器节点异构的问题。

但其缺点是,当某个节点出现故障时,采用哈希策略会出现数据大规模迁移的情况,采用一致性哈希策略可能会造成一定的数据倾斜问题。同样的,这两种策略也没考虑请求开销不同造成的不均衡问题。

应用哈希和一致性哈希策略的框架有很多,比如 Redis、Memcached、Cassandra 等,你可以再回顾下(数据分布方式之哈希与一致性哈希,我就是个神算子)文章中的相关内容。

除了以上这些策略,还有一些负载均衡策略比较常用。比如,根据服务节点中的资源信息(CPU,内存等)进行判断,服务节点资源越多,就越有可能处理下一个请求;再比如,根据请求的特定需求,如请求需要使用 GPU 资源,那就需要由具有 GPU 资源的节点进行处理等。

对比分析

以上,就是轮询策略、随机策略、哈希和一致性哈希策略的主要内容了。接下来,我再通过一个表格对比下这三种方法,以便于你学习和查阅。

cd4e2f877d952f5be43172bbd83eb299.png

总结

今天,我主要带你学习了分布式高可靠技术中的负载均衡。

首先,我以超市收银为例,与你介绍了什么是负载均衡。负载均衡包括数据负载均衡和请求负载均衡,我在前面中介绍的数据分布其实就是数据的负载均衡,所以我今天重点与你分享的是请求的负载均衡。

然后,我与你介绍了常见的负载均衡策略,包括轮询策略、随机策略、哈希和一致性哈希策略。其中,轮询策略和随机策略,因为每次请求到达的目的节点不确定,只适用于无状态请求的场景;而哈希和一致性哈希策略,因为相同 key 的请求会落在同一个服务节点上,所以可以用于有状态请求的场景。

最后,我再通过一张思维导图来归纳一下今天的核心知识点吧。

312feed6091fedc467d721fa2458943b.png

加油,相信通过本讲的学习,你对分布式系统中的负载均衡有了一定的理解,也可以进一步对电商系统、火车票系统等涉及的请求负载均衡的问题进行分析了。加油,行动起来吧!

在下方公众号【架构师修炼】菜单中可自行获取专属架构视频资料,无套路分享,包括不限于 java架构、python系列、人工智能系列、架构系列,以及最新面试、小程序、大前端均无私奉献,你会感谢我的哈!

下一篇预告:分布式流量控制

往期精选

分布式数据之缓存技术,一起来揭开其神秘面纱

分布式数据复制技术,今天就教你真正分身术

数据分布方式之哈希与一致性哈希,我就是个神算子

分布式存储系统三要素,掌握这些就离成功不远了

想要设计一个好的分布式系统,必须搞定这个理论

分布式通信技术之发布订阅,干货满满

分布式通信技术之远程调用:RPC

消息队列Broker主从架构详细设计方案,这一篇就搞定主从架构

消息中间件路由中心你会设计吗,不会就来学学

消息队列消息延迟解决方案,跟着做就行了

秒杀系统每秒上万次下单请求,我们该怎么去设计

【分布式技术】分布式系统调度架构之单体调度,非掌握不可

CDN加速技术,作为开发的我们真的不需要懂吗?

烦人的缓存穿透问题,今天教就你如何去解决

分布式缓存高可用方案,我们都是这么干的

每天百万交易的支付系统,生产环境该怎么设置JVM堆内存大小

你的成神之路我已替你铺好,没铺你来捶我

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/348152.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【强化学习】一文带你理清强化学习

整理不易,希望留个赞再走哦!! 学习路线 这个图描述的比较清晰,蓝框里是整个强化学习的一些概念基础了,橙色是一些学习方法,可以针对性的选择一些,废话不多说,接下来就按照这个路线图…

了解Java缓冲池

了解Java缓冲池 缓冲池空间位于垃圾收集器管理的内存之外。 这是分配本地堆外内存的一种方法。 使用缓冲池有什么好处? 为了回答这个问题,让我们首先了解什么是字节缓冲区。 字节缓冲区 非直接缓冲区 ByteBuffer类附带了java.nio包。 它允许我们分配直接…

java 循环拼接字符串用分号隔开_Java 8中字符串拼接新姿势:StringJoiner

在为什么阿里巴巴不建议在for循环中使用””进行字符串拼接一文中,我们介绍了几种Java中字符串拼接的方式,以及优缺点。其中还有一个重要的拼接方式我没有介绍,那就是Java 8中提供的StringJoiner ,本文就来介绍一下这个字符串拼接…

module ‘tensorflow_core.compat.v1‘ has no attribute ‘contrib‘问题的完美解决

问题描述&#xff1a; Instructions for updating: Use keras.layers.Dense instead. Traceback (most recent call last):File "run_cnn.py", line 200, in <module>model TextCNN(config)File "D:\MY DATA\学习资料\研究生\深度学习\text-classificat…

doctrine find的对象转换成数组_「ES6基础」Array数组的新方法(上)

在日常工作中我们经常会与数组打交道&#xff0c;因此需要熟练掌握数组操作的相关方法&#xff0c;ES6中关于数组的操作&#xff0c;又给我们带来了哪些惊喜呢&#xff0c;Array数组操作又添加了哪些新方法&#xff1f;本篇文章将从以下几个方面进行介绍&#xff1a;Array.from…

No module named ‘__main__.common‘; ‘__main__‘ is not a package

今天在调通代码时遇到一个难缠的bug,源代码如下&#xff1a; from .common import _FLOATX, _EPSILON结果报错&#xff1a; 问题原因&#xff1a; 也就是说&#xff0c;这是相对导入&#xff0c;只有在父模块在当前运行环境中被导入过才能用。 这揭示了报错的缘由&#xff0…

spring 异步返回结果_使用Spring Integration聚合异步结果

spring 异步返回结果嗨&#xff0c;我遇到了一个问题&#xff0c;该问题使用Spring Integration解决方案非常好。 很多时候&#xff0c;我们需要一种将消息分发到未知数量的目的地的方案。 为此&#xff0c;我们使用主题方法。 但是有时&#xff0c;我们还希望从收到消息的所有…

ValueError( Shape(4, ?, 1, 20) and () are incompatible

报错&#xff1a; 解决&#xff1a; 将文件中的return tf.concat(axis, tensors) 改为&#xff1a;return tf.concat(tensors, axis)问题完美解决

adadelta算法_对C++用户比较友好的机器学习算法库

由于疫情影响&#xff0c;这几天在家学习编程&#xff0c;整理了基于c语言的机器学习算法库。目前大部分机器学习库都是面向pyhton语言的&#xff0c;尽管很python包的底层语言是c&#xff0c;但c用户使用起来很麻烦&#xff0c;这里整理了一些对c比较优化的机器学习算法库&…

不解析,使用解析对象

将面向对象的后端与外部系统集成的传统方式是通过数据传输对象 &#xff0c;这些对象在外出之前先序列化为JSON&#xff0c;然后在返回时反序列化。 这种方法很流行&#xff0c;而且是错误的。 序列化部分应该由打印机代替&#xff0c;我在前面已经解释过。 这是我对反序列化的…

yaml报错TypeError: load() missing 1 required positional argument: ‘Loader‘

添加一下命令即可&#xff1a; 我的操作&#xff1a; return yaml.load(config)改为 return yaml.full_load(config)问题完美解决

3d制作中需要注意的问题_珠宝首饰工艺篇-戒指3D造型设计制作注意要点

1、戒指的常规手寸数据和戒指外围周长计算公式应用参考如下图&#xff1a;戒指手寸内直径大小说明图1、戒指手寸规格有14号、15号、16号、17号、18号、19号、20号、&#xff0c;但是从开发角度来说比较常见规格是17号18号19号这几种规格比较多&#xff0c;那我们电脑3D建模的时…

微信推送封面尺寸_连封面图都搞不明白,做什么新时代的新媒体人?

嘿&#xff0c;胖友们大家好呀&#xff0c;我是三儿。又是精(bu)神(xiang)满(shang)满(ban)的周一啦&#xff0c;胖友们准备好开始迎接新的一周了吗&#xff01;为了迎接这个崭新的周一&#xff0c;三儿特意为你们准备的一些实用的小工具教学&#xff0c;帮助你们快速的完成工作…

2021总结

去年做了复盘&#xff0c;感觉很良好。以后希望把这个每年复盘一次的习惯能够好好坚持下去&#xff0c;再加上年初的规划就更好啦&#xff01;2021年&#xff0c;比起2020来说&#xff0c;就相对平静了很多&#xff0c;没经历过大风大浪&#xff0c;心智还是有所变化&#xff0…

java queue使用_使用Java使用Amazon Simple Queue Service

java queue使用Amazon Simple Queue Service或SQS是Amazon Webservice堆栈提供的高度可扩展的托管消息队列。 Amazon SQS可用于完全解耦系统内不同组件的操作&#xff0c;这些组件否则将交换数据以执行独立的任务。 Amazon SQS还可以帮助我们保存在应用程序关闭或组件之一不可用…

latex学习篇【一】论文中的图片技巧QA

第一篇论文总算收尾了&#xff0c;整理一波latex写论文的Q&A。 LATEX专栏第一篇&#xff1a;在Latex模板中引入各种图片的问题。 工具&#xff1a;overleaf网站&#xff1a;https://www.overleaf.com/project/60e9ceb20f8db14efa31dc80在overleaf上面插入图片什么形式最好…

配置HTTPS以与Servlet一起使用

要配置Java EE应用程序以通过HTTPS进行通信&#xff0c;需要在web.xml文件中使用几行XML。 web.xml文件位于项目的WEB-INF目录中&#xff0c;通常在IDE生成Java EE Web应用程序时自动创建。 如果不是&#xff0c;您可以自己创建它。 HTTPS的动机 为Web应用程序配置安全连接的…

LATEX学习篇【二】:论文中的公式技巧QA

写论文免不了写好多好多公式&#xff0c;众所周知啊latex的公式是很难编辑的&#xff0c;有没有简便一点的办法呢&#xff1f;有的&#xff0c;让我们继续读下去吧&#xff01; 写公式时会用到的一些奇奇怪怪的符号大全&#xff08;来自百度&#xff09; 2. 写公式好用的工具…

latex学习篇【三】论文中的表格技巧QA

latex中表格是一个大头 和公式一样&#xff0c;推荐一个好用的工具&#xff01; https://www.tablesgenerator.com/latex_tables 这个网站可以在线编辑表格&#xff0c;直接生成代码&#xff0c;粘贴过去就可以啦&#xff01;&#xff01;&#xff01; 但是关于细节还有很多要调…

JRebel适用于Gradle Spring Boot应用程序

关于如何将JRebel添加到使用Gradle作为构建工具的Spring Boot应用程序中&#xff0c;有一些文档 。 它是基本的&#xff0c;但是效果很好。 您所要做的就是在build.gradle中添加几行&#xff1a; if (project.hasProperty(rebelAgent)) {bootRun.jvmArgs rebelAgent }然后在…